Skip to content

Instantly share code, notes, and snippets.

@zefirka
Last active July 29, 2023 17:42
Show Gist options
  • Save zefirka/7d0ada64a9ac3159b02475da6728cdba to your computer and use it in GitHub Desktop.
Save zefirka/7d0ada64a9ac3159b02475da6728cdba to your computer and use it in GitHub Desktop.
const crypto = require('crypto');
const cluster = require('cluster');
const [_, __, ALGO = 'sha256', DIFF = '32', forks = 4] = process.argv;
const FORK_ID = process.env.FORK_ID;
const hash = data => crypto.createHash(ALGO).update(data);
const hashBlock = ([prevBlockHash, data, salt]) => hash([salt, prevBlockHash, JSON.stringify(data)].join(''));
class Block {
constructor(prevBlockHash, data, nonce) {
this.hash = hashBlock([prevBlockHash, data, nonce]);
this.digest = this.hash.digest('hex');
this.prevHash = prevBlockHash;
this.data = data;
this.dataHash = hash(JSON.stringify(data));
this.nonce = nonce;
}
}
class BlockChain {
constructor(genesis) {
this.chain = [genesis];
this.height = 1;
this.target = Math.log2(DIFF) >> 0;
process.on('message', msg => {
if (msg === 'stop') {
this.stop();
process.exit(0);
}
});
}
append(block) {
const best = this.chain[this.height - 1];
const bytes = block.digest.slice(0, this.target);
const difficulty = Math.pow(2, (block.digest.match(/^0+/) || ['']).pop().length);
if (difficulty > (Math.pow(2, this.target) / 8 >> 0 )) {
console.log(`attempt: ${block.digest}, value: ${bytes}, difficulty: ${difficulty}, nonce: ${block.salt} (${FORK_ID})`);
}
if (bytes.split('').every(byte => byte === '0')) {
this.chain.push(block);
this.height += 1;
return true
}
return false
}
mine(blockTemplate) {
const start = Date.now();
this._mining = new Promise(resolve => {
let nonce = blockTemplate.nonce;
const best = this.chain[this.height - 1];
this._miningInterval = setInterval(() => {
let ticks = 0;
while(ticks++ < 5000) {
const block = new Block(best.digest, blockTemplate.data, nonce++);
const duration = `${Date.now() - start}ms`;
if (this.append(block)) {
clearInterval(this._miningInterval);
resolve({block, duration})
break;
}
}
}, 50);
});
return this._mining;
}
stop() {
clearInterval(this._miningInterval);
}
}
const genesisBlock = new Block();
const blockChain = new BlockChain(genesisBlock);
if (cluster.isWorker) {
const NONCE = process.env.NONCE;
const DIFF = process.env.DIFF;
const ALGO = process.env.ALGO;
console.log(`Mining block on difficulty ${DIFF} with algorithm ${ALGO} starting nonce ${NONCE}`);
blockChain.mine({nonce: NONCE, data: 'Сообщение блока бро'}).then(process.send.bind(process));
}
if (cluster.isMaster) {
let t = forks;
const workers = [];
while(t--) {
const startNonce = 10000000 * t;
const fork = cluster.fork({
NONCE: startNonce,
DIFF,
ALGO,
FORK_ID: t + 1,
});
fork.on('message', data => {
console.log(`Block mined successfully for ${data.duration} [${data.block.digest}] (fork: ${t+1})`);
workers.filter((worker, i) => i !== t).forEach(w => w.send('stop'));
});
workers.push(fork);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment