Last active
July 18, 2021 23:26
-
-
Save Davidson-Souza/1319e815179f216224f96613e1f53f9e to your computer and use it in GitHub Desktop.
This (terrible) code will show some statistics of the bitcoin mining, in a more or less accurate way. The code is intended to be easy to read and understand.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# In order to run this example in Unix-like systems, like linux, you should click in "raw" and copy-paste the code in a .js file | |
# like main.js. The run the follow: | |
npm init | |
npm install node-bitcoin-rpc bignumber.js | |
node main.js |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
This (terrible) code will show some statistics of the bitcoin mining, in a more or less accurate way. | |
@Author: Davidson Souza (Erik) | |
*/ | |
/** | |
NOTE: The actual magic happens within main, these functions are just helping ones | |
*/ | |
/* The only required nodejs dependency is node-bitcoin-rpc for accessing the data and bignumber.js (and a running node) */ | |
const rpc = require("node-bitcoin-rpc") | |
/**Edit: This is just a dummy sample, use your own credentials, and never use adifficultpassword as actual password*/ | |
rpc.init("192.168.42.9", 8332, "erik", "adifficultpassword"); | |
/* If your RPC is taking long to respond, you can change the timeout here or by exporting a TIMEOUT env variable */ | |
rpc.setTimeout(Number (process.env.TIMEOUT) || 10000); | |
const BigNumber = require('bignumber.js'); | |
/** This (even terrible) function will get the best block of the chain */ | |
function getBestBlock() | |
{ | |
return new Promise ((accept, reject) =>{ | |
rpc.call("getblockchaininfo", [], (e, d) => | |
{ | |
if(!e) | |
accept(d.result.headers); | |
}) | |
}) | |
} | |
/** Take the time and the bits form the first block*/ | |
function bestBlockTime(cb) | |
{ | |
rpc.call("getblockchaininfo", [], (e, d) => | |
{ | |
rpc.call("getblockheader", [d.result.bestblockhash], (e, r) => | |
{ | |
if(!r) throw e; | |
cb(parseInt(r.result.time), r.result.bits); | |
}); | |
}); | |
} | |
/** Take the time and height form the first block withn the current retargeting epoch */ | |
function fistEpochBlockTime(cb) | |
{ | |
getBestBlock() | |
.then((v) => | |
{ | |
const first = v - (v % 2016); | |
rpc.call("getblockhash", [first], (e, d) => | |
{ | |
rpc.call("getblockheader", [d.result], (e, d) => | |
{ | |
cb(parseInt (d.result.time), (v % 2016)); | |
}); | |
}); | |
}) | |
} | |
/** Here lies the magic */ | |
async function main() | |
{ | |
fistEpochBlockTime((first, blocksSoFar) => | |
{ | |
bestBlockTime(async (last, bits) => | |
{ | |
/** Extracts the exponent and the base from bits */ | |
const exp = parseInt(bits.substr(0, 2), 16); | |
const base = parseInt(bits.substr(2, 6), 16) | |
/** The target is encoded using the compact format, unpack then */ | |
let target = BigNumber(); | |
target = base * Math.pow (2, (8*(exp - 3))); | |
/** This is the greater possible target (i.e the lesser difficulty) */ | |
const diff_1 = BigNumber("0x00000000FFFF0000000000000000000000000000000000000000000000000000") | |
/** Difficulty is just the lesser possible target divided by the current target | |
* notice that difficulty is the opposite of the possibility of finding one block. | |
* In average, you need make this munch hashes to find a valid block. Notice also | |
* that this diff is calculated as a multiple of the min difficulty. The actual | |
* one is min_diff * diff | |
*/ | |
let diff = diff_1/target; | |
/** | |
* This is a temporary time window for a given epoch, if we assume each block | |
* as taking exactly 10 minutes, they should last this munch | |
*/ | |
const timespan = 10 * 60 * blocksSoFar; | |
/** | |
* Here lies the re-targeting thing, because hashrate is not a constant we need | |
* change the difficulty to compensate fluctuations. | |
*/ | |
let newTarget = target; | |
/** | |
* Again, if the blocks takes 10 minutes to mine to, the difference between | |
* the time of the fist and the last epoch's block should be *timespan*, | |
* however, the may take more or less than that. Here we take the actual | |
* time. | |
*/ | |
let actual = (last - first); | |
/** | |
* Re-targeting is just a matter of multiplying the former target by the | |
* actual time that took for mining to those blocks, and dividing by the | |
* pretended one. One can easily derive this whole logic from simple probability | |
* theory. | |
*/ | |
newTarget *= actual; | |
newTarget /= timespan; | |
/** | |
* Here we can compute the new difficulty based upon the new target. | |
* Remember that this is a estimation unless we are actually in the last | |
* epoch's block. | |
*/ | |
const newDiff = diff_1/newTarget; | |
/** | |
* We can also calculate the percentage of change in difficulty | |
*/ | |
const percentege = ((newDiff - diff) * 100)/diff; | |
/** | |
* That takes *diff* hashes to mine to a new block, and each block takes | |
* 10 minus in average, or 10 minutes * 60 seconds = 600 seconds. In 10 minutes | |
* we make *diff* hashes, in a second we make diff/600. | |
* This 2^32 is for correcting the fact of the diff_1 != 2^32, but about 2^31 or so. | |
* A more accurate version may have a "correction factor", but let's just prune it here | |
* More info: https://en.bitcoin.it/wiki/Difficulty | |
*/ | |
const hashrate = (diff * Math.pow(2, 32))/600 | |
/** | |
* Let's say you got a Antminer S19 Pro that makes 110TH/s. | |
* If we divide both sides of the equation above by the difficulty factor | |
* (the right-side numerator), and take the inverse of the equation (one over it) | |
* we get: h = D/t => h/D = 1/t => D/h = t, where D = difficult, h is the hashrate | |
* and t is the time. | |
* I will show it in years to be more easy to visualize, but this formula gives | |
* the result in seconds. | |
*/ | |
const timeToS19Mine = (diff * Math.pow(2, 32))/(110000000000000); | |
/** | |
* Print this whole mess | |
*/ | |
console.log | |
( | |
`Each block took: ${(actual/blocksSoFar/60).toPrecision(4)} minutes, on average`, | |
`\nThe current diff is: ${diff.toPrecision(2)}`, | |
`\nThe current hashrate is: ${hashrate.toPrecision(2)}`, | |
`\nThe next diff estimation: ${newDiff.toPrecision(2)}`, | |
`\nNext re-targeting percentage: ${percentege.toPrecision(4)}%`, | |
`\nYour poor S19 Pro wold take ${(timeToS19Mine/60/60/24/365).toPrecision(2)} years to mine a single block`, | |
); | |
}); | |
}); | |
} | |
main(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment