UNPKG

btc-rpc-explorer

Version:

Open-source, self-hosted Bitcoin explorer

446 lines (297 loc) 9.95 kB
"use strict"; const debug = require("debug"); const debugLog = debug("btcexp:router"); const express = require('express'); const router = express.Router(); const util = require('util'); const moment = require('moment'); const qrcode = require('qrcode'); const bitcoinjs = require('bitcoinjs-lib'); const sha256 = require("crypto-js/sha256"); const hexEnc = require("crypto-js/enc-hex"); const Decimal = require("decimal.js"); const markdown = require("markdown-it")(); const asyncHandler = require("express-async-handler"); const utils = require('./../app/utils.js'); const coins = require("./../app/coins.js"); const config = require("./../app/config.js"); const coreApi = require("./../app/api/coreApi.js"); const addressApi = require("./../app/api/addressApi.js"); const rpcApi = require("./../app/api/rpcApi.js"); router.get("/blocks-by-height/:blockHeights", function(req, res, next) { let blockHeightStrs = req.params.blockHeights.split(","); let blockHeights = []; for (let i = 0; i < blockHeightStrs.length; i++) { blockHeights.push(parseInt(blockHeightStrs[i])); } coreApi.getBlocksByHeight(blockHeights).then(function(result) { res.json(result); }).catch(next); }); router.get("/block-headers-by-height/:blockHeights", function(req, res, next) { let blockHeightStrs = req.params.blockHeights.split(","); let blockHeights = []; for (let i = 0; i < blockHeightStrs.length; i++) { blockHeights.push(parseInt(blockHeightStrs[i])); } coreApi.getBlockHeadersByHeight(blockHeights).then(function(result) { res.json(result); next(); }); }); router.get("/block-stats-by-height/:blockHeights", function(req, res, next) { let blockHeightStrs = req.params.blockHeights.split(","); let blockHeights = []; for (let i = 0; i < blockHeightStrs.length; i++) { blockHeights.push(parseInt(blockHeightStrs[i])); } coreApi.getBlocksStatsByHeight(blockHeights).then(function(result) { res.json(result); next(); }); }); router.get("/mempool-txs/:txids", function(req, res, next) { let txids = req.params.txids.split(",").map(utils.asHash); let promises = []; for (let i = 0; i < txids.length; i++) { promises.push(coreApi.getMempoolTxDetails(txids[i], false)); } Promise.all(promises).then(function(results) { res.json(results); next(); }).catch(function(err) { res.json({success:false, error:err}); next(); }); }); router.get("/difficulty-by-height/:blockHeights", asyncHandler(async (req, res, next) => { const blockHeights = req.params.blockHeights.split(",").map(x => parseInt(x)); let results = await coreApi.getDifficultyByBlockHeights(blockHeights); res.json(results); next(); })); const predictedBlocksStatuses = Object.create(null); const predictedBlocksOutputs = Object.create(null); router.get("/predicted-blocks-status", asyncHandler(async (req, res, next) => { const statusId = req.query.statusId; if (statusId && predictedBlocksStatuses[statusId]) { res.json(predictedBlocksStatuses[statusId]); next(); } else { res.json({}); next(); } })); router.get("/get-predicted-blocks", asyncHandler(async (req, res, next) => { const statusId = req.query.statusId; if (statusId && predictedBlocksOutputs[statusId]) { let output = predictedBlocksOutputs[statusId]; res.json(output); next(); delete predictedBlocksOutputs[statusId]; delete predictedBlocksStatuses[statusId]; } else { res.json({}); next(); } })); router.get("/build-predicted-blocks", asyncHandler(async (req, res, next) => { try { // long timeout res.connection.setTimeout(600000); const statusId = req.query.statusId; if (statusId) { predictedBlocksStatuses[statusId] = {}; } res.json({success:true, status:"started"}); next(); let output = await coreApi.buildPredictedBlocks(statusId, (update) => { predictedBlocksStatuses[statusId] = update; }); // store summary until it's retrieved via /api/get-mempool-summary predictedBlocksOutputs[statusId] = output; } catch (err) { utils.logError("329r7whegee", err); } })); const mempoolSummaryStatuses = Object.create(null); const mempoolSummaries = Object.create(null); router.get("/mempool-summary-status", asyncHandler(async (req, res, next) => { const statusId = req.query.statusId; if (statusId && mempoolSummaryStatuses[statusId]) { res.json(mempoolSummaryStatuses[statusId]); next(); } else { res.json({}); next(); } })); router.get("/get-mempool-summary", asyncHandler(async (req, res, next) => { const statusId = req.query.statusId; if (statusId && mempoolSummaries[statusId]) { let summary = mempoolSummaries[statusId]; res.json(summary); next(); delete mempoolSummaries[statusId]; delete mempoolSummaryStatuses[statusId]; } else { res.writeHead(204); res.end("no summary for that id"); next(); } })); router.get("/build-mempool-summary", asyncHandler(async (req, res, next) => { try { // long timeout res.connection.setTimeout(600000); const statusId = req.query.statusId; if (statusId) { mempoolSummaryStatuses[statusId] = {}; } const ageBuckets = req.query.ageBuckets ? parseInt(req.query.ageBuckets) : 100; const sizeBuckets = req.query.sizeBuckets ? parseInt(req.query.sizeBuckets) : 100; let summary = await coreApi.buildMempoolSummary(statusId, ageBuckets, sizeBuckets, (update) => { mempoolSummaryStatuses[statusId] = update; }); // store summary until it's retrieved via /api/get-mempool-summary mempoolSummaries[statusId] = summary; res.json({success:true, status:"started"}); next(); } catch (err) { utils.logError("329r7whegee", err); } })); const miningSummaryStatuses = Object.create(null); const miningSummaries = Object.create(null); router.get("/mining-summary-status", asyncHandler(async (req, res, next) => { const statusId = req.query.statusId; if (statusId && miningSummaryStatuses[statusId]) { res.json(miningSummaryStatuses[statusId]); next(); } else { res.json({}); next(); } })); router.get("/get-mining-summary", asyncHandler(async (req, res, next) => { const statusId = req.query.statusId; if (statusId && miningSummaries[statusId]) { let summary = miningSummaries[statusId]; res.json(summary); next(); delete miningSummaries[statusId]; delete miningSummaryStatuses[statusId]; } else { res.writeHead(204); res.end("no summary for that id"); next(); } })); router.get("/build-mining-summary/:startBlock/:endBlock", asyncHandler(async (req, res, next) => { try { // long timeout res.connection.setTimeout(600000); let startBlock = parseInt(req.params.startBlock); let endBlock = parseInt(req.params.endBlock); const statusId = req.query.statusId; if (statusId) { miningSummaryStatuses[statusId] = {}; } res.json({success:true, status:"started"}); next(); let summary = await coreApi.buildMiningSummary(statusId, startBlock, endBlock, (update) => { miningSummaryStatuses[statusId] = update; }); // store summary until it's retrieved via /api/get-mining-summary miningSummaries[statusId] = summary; } catch (err) { utils.logError("4328943ryh44", err); } })); router.get("/mempool-tx-summaries/:txids", asyncHandler(async (req, res, next) => { try { const txids = req.params.txids.split(",").map(utils.asHash); const promises = []; const results = []; for (let i = 0; i < txids.length; i++) { const txid = txids[i]; const key = txid.substring(0, 6); promises.push(new Promise(async (resolve, reject) => { try { const item = await coreApi.getMempoolTxDetails(txid, false); const itemSummary = { f: item.entry.fees.modified, sz: item.entry.vsize ? item.entry.vsize : item.entry.size, af: item.entry.fees.ancestor, df: item.entry.fees.descendant, dsz: item.entry.descendantsize, t: item.entry.time, w: item.entry.weight ? item.entry.weight : item.entry.size * 4 }; results.push(itemSummary); resolve(); } catch (e) { utils.logError("38yereghee", e); // resolve anyway resolve(); } })); } await Promise.all(promises); res.json(results); next(); } catch (err) { res.json({success:false, error:err}); next(); } })); router.get("/raw-tx-with-inputs/:txid", function(req, res, next) { let txid = utils.asHash(req.params.txid); let promises = []; promises.push(coreApi.getRawTransactionsWithInputs([txid])); Promise.all(promises).then(function(results) { res.json(results); next(); }).catch(function(err) { res.json({success:false, error:err}); next(); }); }); router.get("/block-tx-summaries/:blockHash/:blockHeight/:txids", function(req, res, next) { let blockHash = req.params.blockHash; let blockHeight = parseInt(req.params.blockHeight); let txids = req.params.txids.split(",").map(utils.asHash); let promises = []; let results = []; promises.push(new Promise(function(resolve, reject) { coreApi.buildBlockAnalysisData(blockHeight, blockHash, txids, 0, results, resolve); })); Promise.all(promises).then(function() { res.json(results); next(); }).catch(function(err) { res.json({success:false, error:err}); next(); }); }); router.get("/utils/:func/:params", function(req, res, next) { let func = req.params.func; let params = req.params.params; let data = null; if (func == "formatLargeNumber") { if (params.indexOf(",") > -1) { let parts = params.split(","); data = utils.formatLargeNumber(parseInt(parts[0]), parseInt(parts[1])); } else { data = utils.formatLargeNumber(parseInt(params)); } } else if (func == "formatCurrencyAmountInSmallestUnits") { let parts = params.split(","); data = utils.formatCurrencyAmountInSmallestUnits(new Decimal(parts[0]), parseInt(parts[1])); } else { data = {success:false, error:`Unknown function: ${func}`}; } res.json(data); next(); }); module.exports = router;