Last active
July 29, 2023 17:42
-
-
Save zefirka/7d0ada64a9ac3159b02475da6728cdba to your computer and use it in GitHub Desktop.
This file contains hidden or 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
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