diff --git a/app.js b/app.js index 7b0f83c..3fd0cb7 100644 --- a/app.js +++ b/app.js @@ -13,7 +13,9 @@ var favicon = require("serve-favicon"); var indexRouter = require("./routes/index"); var blockDataRouter = require("./routes/block_data"); var blocksRouter = require("./routes/blocks"); +var exportRouter = require("./routes/export"); var mempoolRouter = require("./routes/mempool"); +var minersRouter = require("./routes/miners"); var searchCommitmentsRouter = require("./routes/search_commitments"); var searchKernelsRouter = require("./routes/search_kernels"); var healthz = require("./routes/healthz"); @@ -102,7 +104,9 @@ app.use("/", indexRouter); app.use("/blocks", blocksRouter); app.use("/block_data", blockDataRouter); app.use("/assets", assetsRouter); +// app.use("/export", exportRouter); app.use("/mempool", mempoolRouter); +app.use("/miners", minersRouter); app.use("/search_commitments", searchCommitmentsRouter); app.use("/search_kernels", searchKernelsRouter); app.use("/healthz", healthz); diff --git a/applications/minotari_app_grpc/proto/base_node.proto b/applications/minotari_app_grpc/proto/base_node.proto index 84b7f07..5728ccd 100644 --- a/applications/minotari_app_grpc/proto/base_node.proto +++ b/applications/minotari_app_grpc/proto/base_node.proto @@ -217,6 +217,8 @@ message NetworkDifficultyResponse { uint64 pow_algo = 5; uint64 sha3x_estimated_hash_rate = 6; uint64 randomx_estimated_hash_rate = 7; + uint64 num_coinbases = 8; + bytes first_coinbase_extra = 9; } // A generic single value response for a specific height @@ -339,10 +341,11 @@ message MetaData { uint64 best_block_height = 1; // The block hash of the current tip of the longest valid chain, or `None` for an empty chain bytes best_block_hash = 2; - // This is the min height this node can provide complete blocks for. A 0 here means this node is archival and can provide complete blocks for every height. - uint64 pruned_height = 6; // The current geometric mean of the pow of the chain tip, or `None` if there is no chain bytes accumulated_difficulty = 5; + // This is the min height this node can provide complete blocks for. A 0 here means this node is archival and can provide complete blocks for every height. + uint64 pruned_height = 6; + uint64 timestamp = 7; } message SyncInfoResponse { diff --git a/package-lock.json b/package-lock.json index 79d1a43..2d04611 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "cookie-parser": "~1.4.4", "cors": "^2.8.5", "express": "~4.16.1", + "fast-csv": "^5.0.1", "hbs": "^4.1.2", "http-errors": "~1.6.3", "morgan": "~1.9.1", @@ -699,6 +700,31 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "node_modules/@fast-csv/format": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@fast-csv/format/-/format-5.0.0.tgz", + "integrity": "sha512-IyMpHwYIOGa2f0BJi6Wk55UF0oBA5urdIydoEDYxPo88LFbeb3Yr4rgpu98OAO1glUWheSnNtUgS80LE+/dqmw==", + "dependencies": { + "lodash.escaperegexp": "^4.1.2", + "lodash.isboolean": "^3.0.3", + "lodash.isequal": "^4.5.0", + "lodash.isfunction": "^3.0.9", + "lodash.isnil": "^4.0.0" + } + }, + "node_modules/@fast-csv/parse": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@fast-csv/parse/-/parse-5.0.0.tgz", + "integrity": "sha512-ecF8tCm3jVxeRjEB6VPzmA+1wGaJ5JgaUX2uesOXdXD6qQp0B3EdshOIed4yT1Xlj/F2f8v4zHSo0Oi31L697g==", + "dependencies": { + "lodash.escaperegexp": "^4.1.2", + "lodash.groupby": "^4.6.0", + "lodash.isfunction": "^3.0.9", + "lodash.isnil": "^4.0.0", + "lodash.isundefined": "^3.0.1", + "lodash.uniq": "^4.5.0" + } + }, "node_modules/@grpc/grpc-js": { "version": "1.9.2", "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.2.tgz", @@ -1706,6 +1732,18 @@ "node": ">= 0.6" } }, + "node_modules/fast-csv": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/fast-csv/-/fast-csv-5.0.1.tgz", + "integrity": "sha512-Q43zC4NdQD5MAWOVQOF8KA+D6ddvTJjX2ib8zqysm74jZhtk6+dc8C75/OqRV6Y9CLc4kgvbC3PLG8YL4YZfgw==", + "dependencies": { + "@fast-csv/format": "5.0.0", + "@fast-csv/parse": "5.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -2194,6 +2232,41 @@ "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" }, + "node_modules/lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==" + }, + "node_modules/lodash.groupby": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz", + "integrity": "sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" + }, + "node_modules/lodash.isfunction": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", + "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==" + }, + "node_modules/lodash.isnil": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/lodash.isnil/-/lodash.isnil-4.0.0.tgz", + "integrity": "sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==" + }, + "node_modules/lodash.isundefined": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz", + "integrity": "sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA==" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -2206,6 +2279,11 @@ "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", "dev": true }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" + }, "node_modules/long": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", @@ -3673,6 +3751,31 @@ } } }, + "@fast-csv/format": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@fast-csv/format/-/format-5.0.0.tgz", + "integrity": "sha512-IyMpHwYIOGa2f0BJi6Wk55UF0oBA5urdIydoEDYxPo88LFbeb3Yr4rgpu98OAO1glUWheSnNtUgS80LE+/dqmw==", + "requires": { + "lodash.escaperegexp": "^4.1.2", + "lodash.isboolean": "^3.0.3", + "lodash.isequal": "^4.5.0", + "lodash.isfunction": "^3.0.9", + "lodash.isnil": "^4.0.0" + } + }, + "@fast-csv/parse": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@fast-csv/parse/-/parse-5.0.0.tgz", + "integrity": "sha512-ecF8tCm3jVxeRjEB6VPzmA+1wGaJ5JgaUX2uesOXdXD6qQp0B3EdshOIed4yT1Xlj/F2f8v4zHSo0Oi31L697g==", + "requires": { + "lodash.escaperegexp": "^4.1.2", + "lodash.groupby": "^4.6.0", + "lodash.isfunction": "^3.0.9", + "lodash.isnil": "^4.0.0", + "lodash.isundefined": "^3.0.1", + "lodash.uniq": "^4.5.0" + } + }, "@grpc/grpc-js": { "version": "1.9.2", "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.2.tgz", @@ -4442,6 +4545,15 @@ } } }, + "fast-csv": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/fast-csv/-/fast-csv-5.0.1.tgz", + "integrity": "sha512-Q43zC4NdQD5MAWOVQOF8KA+D6ddvTJjX2ib8zqysm74jZhtk6+dc8C75/OqRV6Y9CLc4kgvbC3PLG8YL4YZfgw==", + "requires": { + "@fast-csv/format": "5.0.0", + "@fast-csv/parse": "5.0.0" + } + }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -4811,6 +4923,41 @@ "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" }, + "lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==" + }, + "lodash.groupby": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz", + "integrity": "sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" + }, + "lodash.isfunction": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", + "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==" + }, + "lodash.isnil": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/lodash.isnil/-/lodash.isnil-4.0.0.tgz", + "integrity": "sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==" + }, + "lodash.isundefined": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz", + "integrity": "sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA==" + }, "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -4823,6 +4970,11 @@ "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", "dev": true }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" + }, "long": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", diff --git a/package.json b/package.json index 696aa69..845e35c 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "cookie-parser": "~1.4.4", "cors": "^2.8.5", "express": "~4.16.1", + "fast-csv": "^5.0.1", "hbs": "^4.1.2", "http-errors": "~1.6.3", "morgan": "~1.9.1", diff --git a/routes/export.js b/routes/export.js new file mode 100644 index 0000000..1e931bc --- /dev/null +++ b/routes/export.js @@ -0,0 +1,35 @@ +var { createClient } = require("../baseNodeClient"); + +var express = require("express"); +const { format } = require("@fast-csv/format"); +var router = express.Router(); + +router.get("/", async function (req, res) { + try { + let client = createClient(); + let lastDifficulties = await client.getNetworkDifficulty({ + from_tip: 1000, + }); + + const csvStream = format({ headers: true }); + res.setHeader("Content-Disposition", 'attachment; filename="data.csv"'); + res.setHeader("Content-Type", "text/csv"); + + csvStream.pipe(res); + + // Example data + + for (let i = 0; i < lastDifficulties.length; i++) { + csvStream.write(lastDifficulties[i]); + } + + csvStream.end(); + } catch (error) { + res.status(500); + if (req.query.json !== undefined) { + res.json({ error: error }); + } else { + res.render("error", { error: error }); + } + } +}); diff --git a/routes/index.js b/routes/index.js index 60deefb..9997667 100644 --- a/routes/index.js +++ b/routes/index.js @@ -83,7 +83,7 @@ router.get("/", async function (req, res) { let mempool = await client.getMempoolTransactions({}); // estimated hash rates - let lastDifficulties = await client.getNetworkDifficulty({ from_tip: 100 }); + let lastDifficulties = await client.getNetworkDifficulty({ from_tip: 180 }); let totalHashRates = getHashRates(lastDifficulties, [ "estimated_hash_rate", ]); @@ -123,14 +123,16 @@ router.get("/", async function (req, res) { limit, from, algoSplit, - blockTimes: getBlockTimes(last100Headers), - moneroTimes: getBlockTimes(last100Headers, "0"), - shaTimes: getBlockTimes(last100Headers, "1"), + blockTimes: getBlockTimes(last100Headers, null, 2), + moneroTimes: getBlockTimes(last100Headers, "0", 4), + shaTimes: getBlockTimes(last100Headers, "1", 4), currentHashRate: totalHashRates[totalHashRates.length - 1], totalHashRates, currentShaHashRate: shaHashRates[shaHashRates.length - 1], shaHashRates, + averageShaMiners: shaHashRates[shaHashRates.length - 1] / 200_000_000, // Hashrate of an NVidia 1070 currentMoneroHashRate: moneroHashRates[moneroHashRates.length - 1], + averageMoneroMiners: moneroHashRates[moneroHashRates.length - 1] / 2700, // Average apple m1 hashrate moneroHashRates, activeVns, }; @@ -151,7 +153,7 @@ router.get("/", async function (req, res) { function getHashRates(difficulties, properties) { const end_idx = difficulties.length - 1; - const start_idx = end_idx - 60; + const start_idx = end_idx - 720; return difficulties .map((d) => @@ -163,7 +165,7 @@ function getHashRates(difficulties, properties) { .slice(start_idx, end_idx); } -function getBlockTimes(last100Headers, algo) { +function getBlockTimes(last100Headers, algo, targetTime) { let blocktimes = []; let i = 0; if (algo === "0" || algo === "1") { @@ -184,11 +186,12 @@ function getBlockTimes(last100Headers, algo) { while (i < last100Headers.length && blocktimes.length < 60) { if (!algo || last100Headers[i].pow.pow_algo === algo) { blocktimes.push( - (lastBlockTime - parseInt(last100Headers[i].timestamp)) / 60 + (lastBlockTime - parseInt(last100Headers[i].timestamp)) / 60 - + targetTime ); lastBlockTime = parseInt(last100Headers[i].timestamp); } else { - blocktimes.push(0); + blocktimes.push(targetTime); } i++; } diff --git a/routes/miners.js b/routes/miners.js new file mode 100644 index 0000000..503051a --- /dev/null +++ b/routes/miners.js @@ -0,0 +1,130 @@ +var { createClient } = require("../baseNodeClient"); + +var express = require("express"); +var router = express.Router(); + +router.get("/", async function (req, res) { + try { + let client = createClient(); + let lastDifficulties = await client.getNetworkDifficulty({ from_tip: 720 }); + + let data = { + num_blocks: lastDifficulties.length, + difficulties: lastDifficulties, + extras: [], + unique_ids: {}, + os: {}, + versions: {}, + now: Math.floor(Date.now() / 1000), + }; + + for (let i = 0; i < lastDifficulties.length; i++) { + let extra = lastDifficulties[i].first_coinbase_extra.toString(); + let split = extra.split(","); + + let unique_id = + lastDifficulties[i].first_coinbase_extra === "" + ? "Non-universe miner" + : lastDifficulties[i].first_coinbase_extra; + let os = "Non-universe miner"; + let version = "Non-universe miner"; + + if (split.length >= 6) { + unique_id = split[1]; + os = split[4]; + version = split[5]; + } + + if (data.unique_ids[unique_id] === undefined) { + data.unique_ids[unique_id] = { + sha: { + count: 0, + version: version, + os: os, + last_block_time: 0, + time_since_last_block: null, + recent_blocks: 0, + }, + randomx: { + count: 0, + version: version, + os: os, + last_block_time: 0, + time_since_last_block: null, + recent_blocks: 0, + }, + }; + } + data.unique_ids[unique_id][ + lastDifficulties[i].pow_algo === "0" ? "randomx" : "sha" + ].count += 1; + data.unique_ids[unique_id][ + lastDifficulties[i].pow_algo === "0" ? "randomx" : "sha" + ].version = version; + data.unique_ids[unique_id][ + lastDifficulties[i].pow_algo === "0" ? "randomx" : "sha" + ].last_block_time = lastDifficulties[i].timestamp; + data.unique_ids[unique_id][ + lastDifficulties[i].pow_algo === "0" ? "randomx" : "sha" + ].time_since_last_block = Math.ceil( + (data.now - lastDifficulties[i].timestamp) / 60 + ); + + if ( + data.unique_ids[unique_id][ + lastDifficulties[i].pow_algo === "0" ? "randomx" : "sha" + ].time_since_last_block < 120 + ) { + data.unique_ids[unique_id][ + lastDifficulties[i].pow_algo === "0" ? "randomx" : "sha" + ].recent_blocks += 1; + } + + if (data.os[os] === undefined) { + data.os[os] = 0; + } + data.os[os] += 1; + if (data.versions[version] === undefined) { + data.versions[version] = 0; + } + data.versions[version] += 1; + + data.extras.push({ + height: lastDifficulties[i].height, + extra: extra, + unique_id, + os, + version, + }); + } + + data.active_miners = {}; + for (const unique_id of Object.keys(data.unique_ids)) { + console.log(unique_id); + let miner = data.unique_ids[unique_id]; + console.log(miner); + if ( + (miner.sha.time_since_last_block || 1000) < 120 || + (miner.randomx.time_since_last_block || 1000) < 120 + ) { + data.active_miners[unique_id] = miner; + } + } + + if (req.query.json !== undefined) { + res.json(data); + } else { + res.render("miners", data); + } + } catch (error) { + res.status(500); + if (req.query.json !== undefined) { + res.json({ error: error }); + } else { + res.render("error", { error: error }); + } + } +}); + + +module.exports = router; \ No newline at end of file diff --git a/views/export.hbs b/views/export.hbs new file mode 100644 index 0000000..1815627 --- /dev/null +++ b/views/export.hbs @@ -0,0 +1,9 @@ +block_height, hashrate +{{#each outputs as |output|}} + + +

Output {{add @index ../outputsFrom}}

+ {{>TransactionOutput output}} + + +{{/each}} \ No newline at end of file diff --git a/views/index.hbs b/views/index.hbs index 8e1188e..add4ade 100644 --- a/views/index.hbs +++ b/views/index.hbs @@ -78,13 +78,13 @@

Monero

- Target time: 3.3 minutes + Target time: 4 minutes
{{chart this.moneroTimes 15}}
       

SHA3

- Target time: 5 minutes + Target time: 4 minutes
{{chart this.shaTimes 15}}
       
@@ -93,32 +93,22 @@
- - - - + + @@ -140,15 +130,15 @@ {{#each this.headers}} - - - + + + - - - - - + + + + + {{/each}}
-

Estimated Hash Rate

- Current total estimated Hash Rate: - {{this.currentHashRate}} - H/s -
{{chart this.totalHashRates 15}}
-      
-
-

Monero

- Current estimated Hash Rate: + Current estimated Hash Rate (180 blocks, 1.5 hours): {{this.currentMoneroHashRate}} - H/s + H/s ({{this.averageMoneroMiners}} average miners)
{{chart this.moneroHashRates 15}}
       

SHA3

- Current estimated Hash Rate: + Current estimated Hash Rate (180 blocks, 1.5 hours): {{this.currentShaHashRate}} - H/s + H/s ({{this.averageShaMiners}} average miners)
{{chart this.shaHashRates 15}}
       
{{this.height}}{{timestamp this.timestamp}}
{{this.height}}{{timestamp this.timestamp}}{{this.powText}}{{hex this.hash}}{{kernels}}{{outputs}}
{{this.powText}}{{hex this.hash}}{{kernels}}{{outputs}}
@@ -179,17 +169,17 @@ {{#each mempool}} - {{#with this.transaction.body}} - - {{hex - this.signature - }} - {{this.total_fees}} - {{this.outputs.length}} - {{this.kernels.length}} - {{this.inputs.length}} - - {{/with}} + {{#with this.transaction.body}} + + {{hex + this.signature + }} + {{this.total_fees}} + {{this.outputs.length}} + {{this.kernels.length}} + {{this.inputs.length}} + + {{/with}} {{/each}} @@ -205,10 +195,10 @@ {{#each this.activeVns}} - - {{hex this.public_key}} - {{hex this.shard_key}} - + + {{hex this.public_key}} + {{hex this.shard_key}} + {{/each}} @@ -225,4 +215,4 @@ - + \ No newline at end of file diff --git a/views/miners.hbs b/views/miners.hbs new file mode 100644 index 0000000..58c56a0 --- /dev/null +++ b/views/miners.hbs @@ -0,0 +1,132 @@ +

Versions

+ + + + + + {{#each this.versions}} + + + + + {{/each}} +
VersionBlocks Mined
{{@key}}{{this}}
+ + +

Operating System

+ + + + + + {{#each this.os}} + + + + + {{/each}} +
OSBlocks mined
{{@key}}{{this}}
+ + +

Active miners

+ + + + + + + + + + + {{#each this.active_miners}} + {{#if this.sha.count }} + + + + + + + + + + {{/if}} + {{#if this.randomx.count }} + + + + + + + + + + {{/if}} + {{/each}} +
UniqueidResourceBlocks won (total)Blocks won (last 2 hours)VersionOSLast block
{{@key}}gpu{{this.sha.count}}{{this.sha.recent_blocks}}{{this.sha.version}}{{this.sha.os}}{{this.sha.time_since_last_block}} minutes ago
{{@key}}cpu{{this.randomx.count}}{{this.randomx.recent_blocks}}{{this.randomx.version}}{{this.randomx.os}}{{this.randomx.time_since_last_block}} minutes ago
+ +

Unique miners (GPU)

+ + + + + + + + + + {{#each this.unique_ids}} + {{#if this.sha.count }} + + + + + + + + + {{/if}} + {{/each}} +
UniqueidResourceBlocks wonVersionOSLast block
{{@key}}gpu{{this.sha.count}}{{this.sha.version}}{{this.sha.os}}{{this.sha.time_since_last_block}} minutes ago
+ + +

Unique miners (CPU)

+ + + + + + + + + + {{#each this.unique_ids}} + {{#if this.randomx.count }} + + + + + + + + + + {{/if}} + {{/each}} +
UniqueidResourceBlocks wonVersionOSLast block
{{@key}}cpu{{this.randomx.count}}{{this.randomx.version}}{{this.randomx.os}}{{this.randomx.time_since_last_block}} minutes ago
+ + + + +

All extra fields

+ + {{# each this.extras}} + + + + + + + + {{/each}} +
{{this.height}}{{this.extra}}{{this.unique_id}}{{this.os}}{{this.version}}
\ No newline at end of file