fcoin/lib/bcoin/miner.js
2016-01-04 05:07:53 -08:00

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;
};