A small mining dashboard can get the arithmetic right and still communicate the wrong idea. “Expected time: 12 years” looks like a countdown. It is not one.
For a simple model with constant hashrate and difficulty, block discoveries can be approximated by a Poisson process. That gives a useful engineering exercise: implement a small pure function, test its boundaries, and keep assumptions visible in the UI.
Start with units
Let H be the miner hashrate in hashes per second, D the Bitcoin difficulty, and t the observation window in seconds. The conventional mining approximation for expected hashes per block is D × 2^32. The expected number of blocks is therefore:
lambda = H × t / (D × 2^32)
The probability of finding at least one block is:
P(N >= 1) = 1 - exp(-lambda)
This assumes uninterrupted work at the stated effective hashrate and fixed difficulty. It does not model rejected work, outages, future difficulty changes, pool fees, or electricity costs.
Avoid subtracting two nearly equal numbers
For very small lambda, 1 - Math.exp(-lambda) loses precision. JavaScript already provides the better building block: Math.expm1(x) computes exp(x) - 1 accurately near zero.
export function probabilityAtLeastOneBlock({
hashrateHs,
difficulty,
seconds,
}) {
if (![hashrateHs, difficulty, seconds].every(Number.isFinite)) {
throw new TypeError("Inputs must be finite numbers");
}
if (hashrateHs < 0 || difficulty <= 0 || seconds < 0) {
throw new RangeError("Invalid hashrate, difficulty, or duration");
}
if (hashrateHs === 0 || seconds === 0) return 0;
const lambda = (hashrateHs / difficulty / 2 ** 32) * seconds;
if (!Number.isFinite(lambda)) {
throw new RangeError("Inputs exceed the supported numeric range");
}
return -Math.expm1(-lambda);
}
Keep conversions at the boundary. One TH/s is 10^12 H/s; one day is 86,400 seconds. Do not pass the display value “1.2” as though it were hashes per second.
Test meaning, not just the implementation
Useful checks include zero hashrate, zero duration, invalid inputs, and a deliberately constructed window with lambda = 1. That last case must produce approximately 0.6321205588, not 1.
const difficulty = 100e12; // illustrative, not current network data
const hashrateHs = 1e12;
const meanSeconds = difficulty * 2 ** 32 / hashrateHs;
const p = probabilityAtLeastOneBlock({
hashrateHs, difficulty, seconds: meanSeconds,
});
console.assert(Math.abs(p - 0.6321205588285577) < 1e-12);
At one mean waiting time, the model still assigns about 36.8% probability to finding no block. Also check monotonicity: with the other inputs fixed, increasing time or hashrate must not reduce the probability.
Make the interface honest
Label the output “estimated probability over this period,” and show the difficulty input and its timestamp. If you display expected waiting time, explain that it is a statistical mean, not a deadline. A miner that has run unsuccessfully for a year is not “due” a block under this memoryless model.
Keep observed block data separate from estimates. A block explorer can verify a block and its coinbase transaction; it generally cannot tell you the winning device model or its actual hashrate. A tracker such as SoloBlocks is useful for exploring reported solo-pool discoveries, but classifications and hashrate estimates should not be treated as on-chain facts.
Finally, probability is not profitability. Energy use, equipment cost, downtime, transaction fees and payout rules are separate inputs. A calculator should not turn a mathematically possible outcome into an earnings promise.
Disclosure: Published by Mineshop.eu, a mining-hardware retailer associated with SoloBlocks. This tutorial was prepared with AI assistance; the numeric examples were checked with executable tests. It is an educational model, not a forecast of mining returns.
Top comments (0)