213 lines
4.5 KiB
JavaScript
213 lines
4.5 KiB
JavaScript
/**
|
|
* miner.js - simple miner for bcoin
|
|
* Copyright (c) 2014-2015, Fedor Indutny (MIT License)
|
|
* https://github.com/indutny/bcoin
|
|
*/
|
|
|
|
var bcoin = require('../bcoin');
|
|
var network = bcoin.protocol.network;
|
|
var constants = bcoin.protocol.constants;
|
|
var utils = bcoin.utils;
|
|
var bn = require('bn.js');
|
|
var inherits = require('inherits');
|
|
var EventEmitter = require('events').EventEmitter;
|
|
var assert = utils.assert;
|
|
|
|
/**
|
|
* Miner
|
|
*/
|
|
|
|
exports.miner = function miner(options, callback) {
|
|
var chain = bcoin.chain.global;
|
|
var e = new EventEmitter;
|
|
var fee = new bn(0);
|
|
var last = chain.getTip();
|
|
var nextBlock, timeout, blockChecker;
|
|
|
|
function addBlock(b) {
|
|
var block = b || chain.getTip();
|
|
if (block.height > last.height) {
|
|
last = block;
|
|
// Somebody found the next block before us,
|
|
// start over with new target.
|
|
stop();
|
|
start();
|
|
}
|
|
}
|
|
|
|
function addTX(tx) {
|
|
if (tx.inputs[0].output && !tx.verify())
|
|
return;
|
|
|
|
if (tx.height !== -1)
|
|
return;
|
|
|
|
if (block.size() + tx.size() > constants.blocks.maxSize)
|
|
return;
|
|
|
|
nextBlock.txs.push(tx);
|
|
|
|
fee.iadd(tx.getFee());
|
|
|
|
// Update coinbase value
|
|
updateCoinbase(nextBlock);
|
|
// Update merkle root for new coinbase and new tx
|
|
updateMerkle(nextBlock);
|
|
}
|
|
|
|
function newBlock() {
|
|
var coinbase, block, target;
|
|
|
|
coinbase = bcoin.tx();
|
|
|
|
coinbase.input({
|
|
out: {
|
|
hash: constants.zeroHash,
|
|
index: 0xffffffff
|
|
},
|
|
script: [
|
|
new bn(last.height + 1).toArray().reverse(),
|
|
[],
|
|
utils.ascii2array(options.msg || 'mined by bcoin')
|
|
],
|
|
seq: 0xffffffff
|
|
});
|
|
|
|
coinbase.output({
|
|
address: options.address,
|
|
value: new bn(0)
|
|
});
|
|
|
|
target = chain.target(last);
|
|
|
|
block = {
|
|
version: 4,
|
|
prevBlock: last.verify ? last.hash('hex') : last.hash,
|
|
merkleRoot: constants.zeroHash.slice(),
|
|
ts: utils.now(),
|
|
bits: utils.toCompact(target),
|
|
nonce: 0
|
|
};
|
|
|
|
block = bcoin.block(block, 'block');
|
|
delete block.valid;
|
|
block.verify = block._verify;
|
|
|
|
block.target = target;
|
|
block.extraNonce = new bn(0);
|
|
|
|
block.txs.push(coinbase);
|
|
|
|
// Update coinbase since our coinbase was added.
|
|
updateCoinbase(block);
|
|
// Create our merkle root.
|
|
updateMerkle(block);
|
|
|
|
return block;
|
|
}
|
|
|
|
function updateCoinbase(block) {
|
|
var coinbase = block.txs[0];
|
|
coinbase.inputs[0].script[1] = block.extraNonce.toArray();
|
|
coinbase.outputs[0].value = bcoin.block.reward(last.height + 1).add(fee);
|
|
}
|
|
|
|
function updateMerkle(block) {
|
|
block.ts = utils.now();
|
|
block.merkleRoot = block.getMerkleRoot();
|
|
}
|
|
|
|
function iter(block) {
|
|
timeout = setTimeout(function() {
|
|
var hash, begin, rate;
|
|
|
|
begin = utils.now();
|
|
|
|
while (block.nonce <= 0xffffffff) {
|
|
if (utils.testTarget(block.target, block.hash())) {
|
|
e.emit('block', block);
|
|
last = block;
|
|
nextBlock = newBlock();
|
|
return iter(nextBlock);
|
|
}
|
|
block.nonce++;
|
|
}
|
|
|
|
rate = (0xffffffff / (utils.now() - begin)) * 2;
|
|
|
|
block.nonce = 0;
|
|
block.extraNonce.iaddn(1);
|
|
|
|
// We incremented the extraNonce, need to update coinbase.
|
|
updateCoinbase(block);
|
|
// We changed the coinbase, need to update merkleRoot.
|
|
updateMerkle(block);
|
|
|
|
e.emit('status', {
|
|
block: block,
|
|
target: utils.fromCompact(block.bits),
|
|
hashes: block.extraNonce.mul(0xffffffff),
|
|
hashrate: rate
|
|
});
|
|
|
|
return iter(block);
|
|
}, 10);
|
|
}
|
|
|
|
function start() {
|
|
blockChecker = setInterval(addBlock, 1000);
|
|
nextBlock = newBlock();
|
|
iter(nextBlock);
|
|
}
|
|
|
|
function stop() {
|
|
if (!timeout)
|
|
return;
|
|
clearTimeout(timeout);
|
|
timeout = null;
|
|
clearInterval(blockChecker);
|
|
blockChecker = null;
|
|
}
|
|
|
|
e.addBlock = addBlock;
|
|
e.add = e.addTX = addTX;
|
|
e.start = start;
|
|
e.stop = stop;
|
|
|
|
return e;
|
|
};
|
|
|
|
module.exports = function mine(pool, options) {
|
|
var options, miner;
|
|
|
|
options = {
|
|
address: options.address,
|
|
msg: options.msg || 'mined by bcoin',
|
|
log: options.log
|
|
};
|
|
|
|
miner = exports.miner(options);
|
|
|
|
// Use mempool
|
|
pool.on('tx', function(tx) {
|
|
miner.addTX(tx);
|
|
});
|
|
|
|
pool.chain.on('tip', function(block) {
|
|
miner.addBlock(block);
|
|
});
|
|
|
|
miner.on('block', function(block) {
|
|
pool.sendBlock(block);
|
|
});
|
|
|
|
miner.on('status', function(stat) {
|
|
if (options.log)
|
|
console.log('Hashes per second: %s', stat.hashrate);
|
|
});
|
|
|
|
miner.start();
|
|
|
|
return miner;
|
|
};
|