From 7267244e94e999dd6d576103fed615306b038d44 Mon Sep 17 00:00:00 2001
From: DJObleezy
Date: Mon, 21 Apr 2025 10:04:54 -0700
Subject: [PATCH] Enhance block finding metrics and UI display
Updated CSS for improved styling, added functions to calculate block finding probability and time, and modified UI to display these metrics based on the 24-hour hashrate. New HTML elements added to the dashboard for better user visibility of block odds.
---
static/css/common.css | 2 +-
static/js/main.js | 197 ++++++++++++++++++++++++++++++++++++++-
templates/dashboard.html | 2 +
3 files changed, 199 insertions(+), 2 deletions(-)
diff --git a/static/css/common.css b/static/css/common.css
index 6bb9878..ac4895e 100644
--- a/static/css/common.css
+++ b/static/css/common.css
@@ -300,7 +300,7 @@ h1 {
.yellow {
color: #ffd700 !important;
- text-shadow: 0 0 2px #ffd700, 0 0 2px #ffd700;
+ text-shadow: 0 0 2px #ffd700, 0 0 2px #ffd700 !important;
}
.white {
diff --git a/static/js/main.js b/static/js/main.js
index 482786b..027fe80 100644
--- a/static/js/main.js
+++ b/static/js/main.js
@@ -1,4 +1,4 @@
-"use strict";
+"use strict";
/**
* ArrowIndicator - A clean implementation for managing metric value change indicators
@@ -404,6 +404,113 @@ function formatHashrateForDisplay(value, unit) {
}
}
+// Function to calculate block finding probability based on hashrate and network hashrate
+function calculateBlockProbability(yourHashrate, yourHashrateUnit, networkHashrate) {
+ // First normalize both hashrates to the same unit (TH/s)
+ const normalizedYourHashrate = normalizeHashrate(yourHashrate, yourHashrateUnit);
+
+ // Network hashrate is in EH/s, convert to TH/s (1 EH/s = 1,000,000 TH/s)
+ const networkHashrateInTH = networkHashrate * 1000000;
+
+ // Calculate probability as your_hashrate / network_hashrate
+ const probability = normalizedYourHashrate / networkHashrateInTH;
+
+ // Format the probability for display
+ return formatProbability(probability);
+}
+
+// Format probability for display
+function formatProbability(probability) {
+ // Format as 1 in X chance (more intuitive for small probabilities)
+ if (probability > 0) {
+ const oneInX = Math.round(1 / probability);
+ return `1 : ${numberWithCommas(oneInX)}`;
+ } else {
+ return "N/A";
+ }
+}
+
+// Calculate theoretical time to find a block based on hashrate
+function calculateBlockTime(yourHashrate, yourHashrateUnit, networkHashrate) {
+ // First normalize both hashrates to the same unit (TH/s)
+ const normalizedYourHashrate = normalizeHashrate(yourHashrate, yourHashrateUnit);
+
+ // Make sure network hashrate is a valid number
+ if (typeof networkHashrate !== 'number' || isNaN(networkHashrate) || networkHashrate <= 0) {
+ console.error("Invalid network hashrate:", networkHashrate);
+ return "N/A";
+ }
+
+ // Network hashrate is in EH/s, convert to TH/s (1 EH/s = 1,000,000 TH/s)
+ const networkHashrateInTH = networkHashrate * 1000000;
+
+ // Calculate the probability of finding a block per hash attempt
+ const probability = normalizedYourHashrate / networkHashrateInTH;
+
+ // Bitcoin produces a block every 10 minutes (600 seconds) on average
+ const secondsToFindBlock = 600 / probability;
+
+ // Log the calculation for debugging
+ console.log(`Block time calculation using network hashrate: ${networkHashrate} EH/s`);
+ console.log(`Your hashrate: ${yourHashrate} ${yourHashrateUnit} (normalized: ${normalizedYourHashrate} TH/s)`);
+ console.log(`Probability: ${normalizedYourHashrate} / (${networkHashrate} * 1,000,000) = ${probability}`);
+ console.log(`Time to find block: 600 seconds / ${probability} = ${secondsToFindBlock} seconds`);
+ console.log(`Estimated time: ${secondsToFindBlock / 86400} days (${secondsToFindBlock / 86400 / 365.25} years)`);
+
+ return formatTimeRemaining(secondsToFindBlock);
+}
+
+// Format time in seconds to a readable format (similar to est_time_to_payout)
+function formatTimeRemaining(seconds) {
+ if (!seconds || seconds <= 0 || !isFinite(seconds)) {
+ return "N/A";
+ }
+
+ // Extremely large values (over 100 years) are not useful
+ if (seconds > 3153600000) { // 100 years in seconds
+ return "Never (statistically)";
+ }
+
+ const minutes = seconds / 60;
+ const hours = minutes / 60;
+ const days = hours / 24;
+ const months = days / 30.44; // Average month length
+ const years = days / 365.25; // Account for leap years
+
+ if (years >= 1) {
+ // For very long timeframes, show years and months
+ const remainingMonths = Math.floor((years - Math.floor(years)) * 12);
+ if (remainingMonths > 0) {
+ return `${Math.floor(years)} year${Math.floor(years) !== 1 ? 's' : ''}, ${remainingMonths} month${remainingMonths !== 1 ? 's' : ''}`;
+ }
+ return `${Math.floor(years)} year${Math.floor(years) !== 1 ? 's' : ''}`;
+ } else if (months >= 1) {
+ // For months, show months and days
+ const remainingDays = Math.floor((months - Math.floor(months)) * 30.44);
+ if (remainingDays > 0) {
+ return `${Math.floor(months)} month${Math.floor(months) !== 1 ? 's' : ''}, ${remainingDays} day${remainingDays !== 1 ? 's' : ''}`;
+ }
+ return `${Math.floor(months)} month${Math.floor(months) !== 1 ? 's' : ''}`;
+ } else if (days >= 1) {
+ // For days, show days and hours
+ const remainingHours = Math.floor((days - Math.floor(days)) * 24);
+ if (remainingHours > 0) {
+ return `${Math.floor(days)} day${Math.floor(days) !== 1 ? 's' : ''}, ${remainingHours} hour${remainingHours !== 1 ? 's' : ''}`;
+ }
+ return `${Math.floor(days)} day${Math.floor(days) !== 1 ? 's' : ''}`;
+ } else if (hours >= 1) {
+ // For hours, show hours and minutes
+ const remainingMinutes = Math.floor((hours - Math.floor(hours)) * 60);
+ if (remainingMinutes > 0) {
+ return `${Math.floor(hours)} hour${Math.floor(hours) !== 1 ? 's' : ''}, ${remainingMinutes} minute${remainingMinutes !== 1 ? 's' : ''}`;
+ }
+ return `${Math.floor(hours)} hour${Math.floor(hours) !== 1 ? 's' : ''}`;
+ } else {
+ // For minutes, just show minutes
+ return `${Math.ceil(minutes)} minute${Math.ceil(minutes) !== 1 ? 's' : ''}`;
+ }
+}
+
// SSE Connection with Error Handling and Reconnection Logic
function setupEventSource() {
console.log("Setting up EventSource connection...");
@@ -1183,6 +1290,50 @@ function updateUI() {
}
updateElementText("hashrate_24hr", formatted24hrHashrate);
+ // Calculate and display theoretical block finding time
+ if (data.hashrate_24hr != null && data.network_hashrate != null) {
+ const blockTime = calculateBlockTime(
+ data.hashrate_24hr,
+ data.hashrate_24hr_unit || 'th/s',
+ data.network_hashrate
+ );
+
+ // Update the time element if it exists, or create it
+ const timeElement = document.getElementById("block_time");
+ if (timeElement) {
+ timeElement.textContent = blockTime;
+ } else {
+ // Find the hashrate_24hr element's parent paragraph
+ const hashratePara = document.getElementById("hashrate_24hr").parentNode;
+
+ // Create the time element inline with spacing
+ const timeSpan = document.createElement("span");
+ timeSpan.id = "block_time";
+ timeSpan.className = "metric-value yellow"; // Using yellow for time values
+ timeSpan.style.marginLeft = "10px";
+ timeSpan.style.fontSize = "0.75em"; // Slightly larger font size
+ timeSpan.style.fontWeight = "Normal"; // Normal font
+ timeSpan.textContent = blockTime;
+
+ // Create a small label for it
+ const timeLabel = document.createElement("span");
+ timeLabel.className = "metric-label";
+ timeLabel.style.fontSize = "0.65em";
+ timeLabel.style.opacity = "0.8";
+ timeLabel.style.marginLeft = "5px";
+ timeLabel.style.color = "white"; // White color for label"
+ timeLabel.style.fontWeight = "Normal"; // Normal font
+ timeLabel.textContent = "[Time to ₿]";
+
+ // Add both elements after the arrow indicator
+ const arrowIndicator = document.getElementById("indicator_hashrate_24hr");
+ if (arrowIndicator && hashratePara) {
+ arrowIndicator.parentNode.insertBefore(timeSpan, arrowIndicator.nextSibling);
+ arrowIndicator.parentNode.insertBefore(timeLabel, timeSpan.nextSibling);
+ }
+ }
+ }
+
// 3hr Hashrate
let formatted3hrHashrate = "N/A";
if (data.hashrate_3hr != null) {
@@ -1193,6 +1344,50 @@ function updateUI() {
}
updateElementText("hashrate_3hr", formatted3hrHashrate);
+ // Calculate and display block finding probability for 24hr hashrate instead of 3hr hashrate
+ if (data.hashrate_24hr != null && data.network_hashrate != null) {
+ const blockProbability = calculateBlockProbability(
+ data.hashrate_24hr,
+ data.hashrate_24hr_unit || 'th/s',
+ data.network_hashrate
+ );
+
+ // Update the block odds element
+ const probabilityElement = document.getElementById("block_odds_3hr");
+ if (probabilityElement) {
+ probabilityElement.textContent = blockProbability;
+ } else {
+ // Find the hashrate_3hr element's parent paragraph
+ const hashratePara = document.getElementById("hashrate_3hr").parentNode;
+
+ // Create the probability element inline with spacing
+ const probSpan = document.createElement("span");
+ probSpan.id = "block_odds_3hr";
+ probSpan.className = "metric-value yellow";
+ probSpan.style.marginLeft = "10px";
+ probSpan.style.fontSize = "0.75em"; // Slightly larger font size
+ probSpan.style.fontWeight = "Normal"; // Normal font
+ probSpan.textContent = blockProbability;
+
+ // Create a small label for it
+ const probLabel = document.createElement("span");
+ probLabel.className = "metric-label";
+ probLabel.style.fontSize = "0.65em";
+ probLabel.style.opacity = "0.8";
+ probLabel.style.marginLeft = "5px";
+ probLabel.style.color = "white";
+ probLabel.style.fontWeight = "Normal"; // Normal font
+ probLabel.textContent = "[₿ Odds]";
+
+ // Add both elements after the arrow indicator
+ const arrowIndicator = document.getElementById("indicator_hashrate_3hr");
+ if (arrowIndicator && hashratePara) {
+ arrowIndicator.parentNode.insertBefore(probSpan, arrowIndicator.nextSibling);
+ arrowIndicator.parentNode.insertBefore(probLabel, probSpan.nextSibling);
+ }
+ }
+ }
+
// 10min Hashrate
let formatted10minHashrate = "N/A";
if (data.hashrate_10min != null) {
diff --git a/templates/dashboard.html b/templates/dashboard.html
index 94d0f15..9721367 100644
--- a/templates/dashboard.html
+++ b/templates/dashboard.html
@@ -159,6 +159,8 @@
{% endif %}
+
+ [₿ Odds]
10min Avg Hashrate: