add mempool packet. miner refactor.
This commit is contained in:
parent
ff08f73fa9
commit
d0d5ab2c3b
@ -381,6 +381,13 @@ Block.prototype.__defineGetter__('fee', function() {
|
|||||||
return this.getReward().fee;
|
return this.getReward().fee;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Block.prototype.__defineGetter__('coinbase', function() {
|
||||||
|
var tx = this.txs[0];
|
||||||
|
if (!tx || !tx.isCoinbase())
|
||||||
|
return;
|
||||||
|
return tx;
|
||||||
|
});
|
||||||
|
|
||||||
Block.prototype.inspect = function inspect() {
|
Block.prototype.inspect = function inspect() {
|
||||||
var copy = bcoin.block(this, this.subtype);
|
var copy = bcoin.block(this, this.subtype);
|
||||||
copy.__proto__ = null;
|
copy.__proto__ = null;
|
||||||
|
|||||||
@ -1,212 +1,310 @@
|
|||||||
/**
|
/**
|
||||||
* miner.js - simple miner for bcoin
|
* miner.js - inefficient miner for bcoin (because we can)
|
||||||
* Copyright (c) 2014-2015, Fedor Indutny (MIT License)
|
* Copyright (c) 2014-2015, Fedor Indutny (MIT License)
|
||||||
* https://github.com/indutny/bcoin
|
* https://github.com/indutny/bcoin
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var bcoin = require('../bcoin');
|
var bcoin = require('../bcoin');
|
||||||
var network = bcoin.protocol.network;
|
|
||||||
var constants = bcoin.protocol.constants;
|
|
||||||
var utils = bcoin.utils;
|
var utils = bcoin.utils;
|
||||||
|
var assert = utils.assert;
|
||||||
|
var constants = bcoin.protocol.constants;
|
||||||
|
|
||||||
var bn = require('bn.js');
|
var bn = require('bn.js');
|
||||||
var inherits = require('inherits');
|
var inherits = require('inherits');
|
||||||
var EventEmitter = require('events').EventEmitter;
|
var EventEmitter = require('events').EventEmitter;
|
||||||
var assert = utils.assert;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Miner
|
* Miner
|
||||||
*/
|
*/
|
||||||
|
|
||||||
exports.miner = function miner(options, callback) {
|
function Miner(options) {
|
||||||
var chain = bcoin.chain.global;
|
if (!(this instanceof Miner))
|
||||||
var e = new EventEmitter;
|
return new Miner(options);
|
||||||
var fee = new bn(0);
|
|
||||||
var last = chain.getTip();
|
|
||||||
var nextBlock, timeout, blockChecker;
|
|
||||||
|
|
||||||
function addBlock(b) {
|
if (!options)
|
||||||
var block = b || chain.getTip();
|
options = {};
|
||||||
if (block.height > last.height) {
|
|
||||||
last = block;
|
|
||||||
// Somebody found the next block before us,
|
|
||||||
// start over with new target.
|
|
||||||
stop();
|
|
||||||
start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function addTX(tx) {
|
this.options = options;
|
||||||
if (tx.inputs[0].output && !tx.verify())
|
this.address = this.options.address;
|
||||||
return;
|
this.msg = this.options.msg || 'mined by bcoin';
|
||||||
|
|
||||||
if (tx.height !== -1)
|
this.chain = options.chain || bcoin.chain.global;
|
||||||
return;
|
this.pool = options.pool || bcoin.pool.global;
|
||||||
|
|
||||||
if (block.size() + tx.size() > constants.blocks.maxSize)
|
this.running = false;
|
||||||
return;
|
this.timeout = null;
|
||||||
|
this.interval = null;
|
||||||
|
|
||||||
nextBlock.txs.push(tx);
|
this.fee = new bn(0);
|
||||||
|
this.last = this.chain.getTip();
|
||||||
|
this.block = null;
|
||||||
|
this.rate = 0;
|
||||||
|
}
|
||||||
|
|
||||||
fee.iadd(tx.getFee());
|
inherits(Miner, EventEmitter);
|
||||||
|
|
||||||
// Update coinbase value
|
Miner.prototype._init = function _init() {
|
||||||
updateCoinbase(nextBlock);
|
var self = this;
|
||||||
// Update merkle root for new coinbase and new tx
|
|
||||||
updateMerkle(nextBlock);
|
|
||||||
}
|
|
||||||
|
|
||||||
function newBlock() {
|
this.pool.on('tx', function(tx) {
|
||||||
var coinbase, block, target;
|
self.addTX(tx);
|
||||||
|
});
|
||||||
|
|
||||||
coinbase = bcoin.tx();
|
this.chain.on('block', function(block) {
|
||||||
|
self.addBlock(block);
|
||||||
|
});
|
||||||
|
|
||||||
coinbase.input({
|
// this.chain.on('tip', function(entry) {
|
||||||
out: {
|
// self.addBlock(entry);
|
||||||
hash: constants.zeroHash,
|
// });
|
||||||
index: 0xffffffff
|
|
||||||
},
|
|
||||||
script: [
|
|
||||||
new bn(last.height + 1).toArray().reverse(),
|
|
||||||
[],
|
|
||||||
utils.ascii2array(options.msg || 'mined by bcoin')
|
|
||||||
],
|
|
||||||
seq: 0xffffffff
|
|
||||||
});
|
|
||||||
|
|
||||||
coinbase.output({
|
this.on('block', function(block) {
|
||||||
address: options.address,
|
self.emit('debug',
|
||||||
value: new bn(0)
|
'Found block: %d (%s)',
|
||||||
});
|
self.last.height + 1,
|
||||||
|
block.hash('hex'));
|
||||||
|
self.pool.sendBlock(block);
|
||||||
|
});
|
||||||
|
|
||||||
target = chain.target(last);
|
this.on('status', function(stat) {
|
||||||
|
self.emit('debug', 'Hashes per second: %s', stat.hashrate);
|
||||||
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) {
|
Miner.prototype.start = function start() {
|
||||||
var options, miner;
|
var mempool = this.pool.loadMempool.bind(this.pool);
|
||||||
|
|
||||||
options = {
|
this.stop();
|
||||||
address: options.address,
|
|
||||||
msg: options.msg || 'mined by bcoin',
|
this.running = true;
|
||||||
log: options.log
|
|
||||||
|
// Ask our peers for mempool txs every so often.
|
||||||
|
this.interval = setInterval(mempool, 60 * 1000);
|
||||||
|
|
||||||
|
// Create a new block and start hashing
|
||||||
|
this.block = this.createBlock();
|
||||||
|
this.iterate();
|
||||||
|
};
|
||||||
|
|
||||||
|
Miner.prototype.stop = function stop() {
|
||||||
|
if (!this.running)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.running = false;
|
||||||
|
|
||||||
|
clearTimeout(this.timeout);
|
||||||
|
this.timeout = null;
|
||||||
|
|
||||||
|
clearInterval(this.interval);
|
||||||
|
this.interval = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
Miner.prototype.add = function add(msg) {
|
||||||
|
if (msg.type === 'tx')
|
||||||
|
return this.addTX(msg);
|
||||||
|
return this.addBlock(msg);
|
||||||
|
};
|
||||||
|
|
||||||
|
Miner.prototype.addBlock = function addBlock(block) {
|
||||||
|
if (!block)
|
||||||
|
block = this.chain.getTip();
|
||||||
|
|
||||||
|
// Somebody found the next block before
|
||||||
|
// us, start over with the new target.
|
||||||
|
if (block.height > this.last.height) {
|
||||||
|
this.last = block.verify
|
||||||
|
? this.chain.getBlock(block)
|
||||||
|
: block;
|
||||||
|
assert(this.last);
|
||||||
|
this.start();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Miner.prototype.addTX = function addTX(tx) {
|
||||||
|
var full = this.index.inputs.every(function(input) {
|
||||||
|
return !!input.out.tx;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Cannot calculate fee if we don't have the prev_out.
|
||||||
|
// Could possibly just burn some coins.
|
||||||
|
if (this.options.burn === false) {
|
||||||
|
if (!full)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pretty important
|
||||||
|
if (!tx.verify())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Ignore if it's already in a block
|
||||||
|
if (tx.height !== -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Deliver me from the block size debate, please
|
||||||
|
if (this.block.size() + tx.size() > constants.blocks.maxSize)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Add the tx to our block
|
||||||
|
this.block.txs.push(tx);
|
||||||
|
|
||||||
|
// Calulcate our new reward fee
|
||||||
|
if (full)
|
||||||
|
this.fee.iadd(tx.getFee());
|
||||||
|
|
||||||
|
// Update coinbase value
|
||||||
|
this.updateCoinbase();
|
||||||
|
|
||||||
|
// Update merkle root for new coinbase and new tx
|
||||||
|
this.updateMerkle();
|
||||||
|
};
|
||||||
|
|
||||||
|
Miner.prototype.createBlock = function createBlock(tx) {
|
||||||
|
var target, coinbase, headers, block;
|
||||||
|
|
||||||
|
// Update target
|
||||||
|
target = this.chain.target(this.last);
|
||||||
|
|
||||||
|
// Create a coinbase
|
||||||
|
coinbase = bcoin.tx();
|
||||||
|
|
||||||
|
coinbase.input({
|
||||||
|
out: {
|
||||||
|
hash: utils.toHex(constants.zeroHash),
|
||||||
|
index: 0xffffffff
|
||||||
|
},
|
||||||
|
script: [
|
||||||
|
new bn(this.last.height + 1).toArray().reverse(),
|
||||||
|
[],
|
||||||
|
utils.ascii2array(this.msg || 'mined by bcoin')
|
||||||
|
],
|
||||||
|
seq: 0xffffffff
|
||||||
|
});
|
||||||
|
|
||||||
|
coinbase.output({
|
||||||
|
address: this.address,
|
||||||
|
value: new bn(0)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create our block
|
||||||
|
headers = {
|
||||||
|
version: 4,
|
||||||
|
prevBlock: this.last.verify
|
||||||
|
? this.last.hash('hex')
|
||||||
|
: this.last.hash,
|
||||||
|
merkleRoot: utils.toHex(constants.zeroHash.slice()),
|
||||||
|
ts: utils.now(),
|
||||||
|
bits: utils.toCompact(target),
|
||||||
|
nonce: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
miner = exports.miner(options);
|
block = bcoin.block(headers, 'block');
|
||||||
|
|
||||||
// Use mempool
|
delete block.valid;
|
||||||
pool.on('tx', function(tx) {
|
|
||||||
miner.addTX(tx);
|
|
||||||
});
|
|
||||||
|
|
||||||
pool.chain.on('tip', function(block) {
|
block.txs.push(coinbase);
|
||||||
miner.addBlock(block);
|
|
||||||
});
|
|
||||||
|
|
||||||
miner.on('block', function(block) {
|
// Update coinbase since our coinbase was added.
|
||||||
pool.sendBlock(block);
|
this.updateCoinbase(block);
|
||||||
});
|
|
||||||
|
|
||||||
miner.on('status', function(stat) {
|
// Create our merkle root.
|
||||||
if (options.log)
|
this.updateMerkle(block);
|
||||||
console.log('Hashes per second: %s', stat.hashrate);
|
|
||||||
});
|
|
||||||
|
|
||||||
miner.start();
|
block.target = target;
|
||||||
|
block.extraNonce = new bn(0);
|
||||||
|
|
||||||
return miner;
|
return block;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Miner.prototype.updateCoinbase = function updateCoinbase(block) {
|
||||||
|
var coinbase = block.coinbase;
|
||||||
|
|
||||||
|
assert(coinbase);
|
||||||
|
|
||||||
|
if (!block)
|
||||||
|
block = this.block;
|
||||||
|
|
||||||
|
coinbase.inputs[0].script[1] = block.extraNonce.toArray();
|
||||||
|
coinbase.outputs[0].value = bcoin.block.reward(this.last.height + 1).add(fee);
|
||||||
|
};
|
||||||
|
|
||||||
|
Miner.prototype.updateMerkle = function updateMerkle(block) {
|
||||||
|
if (!block)
|
||||||
|
block = this.block;
|
||||||
|
|
||||||
|
block.ts = utils.now();
|
||||||
|
block.merkleRoot = block.getMerkleRoot();
|
||||||
|
};
|
||||||
|
|
||||||
|
Miner.prototype.iterate = function iterate() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
this.timeout = setTimeout(function() {
|
||||||
|
var hash;
|
||||||
|
|
||||||
|
// Try to find a block: do one iteration of extraNonce
|
||||||
|
if (!self.findNonce())
|
||||||
|
return self.iterate();
|
||||||
|
|
||||||
|
hash = self.block.hash('hex');
|
||||||
|
|
||||||
|
// Make sure our block is valid
|
||||||
|
if (!self.block.verify())
|
||||||
|
return self.emit('debug', '%s did not verify.', hash);
|
||||||
|
|
||||||
|
// Add our block to the chain
|
||||||
|
res = self.chain.add(self.block);
|
||||||
|
if (res > 0)
|
||||||
|
return self.emit('debug', '%s could not be added to chain.', hash);
|
||||||
|
|
||||||
|
// Emit our newly found block
|
||||||
|
self.emit('block', self.block);
|
||||||
|
|
||||||
|
// Try to find a new block
|
||||||
|
self.last = self.chain.getBlock(self.block);
|
||||||
|
assert(self.last);
|
||||||
|
self.block = self.createBlock();
|
||||||
|
|
||||||
|
return self.iterate();
|
||||||
|
}, 10);
|
||||||
|
};
|
||||||
|
|
||||||
|
Miner.prototype.findNonce = function findNonce() {
|
||||||
|
var begin = utils.now();
|
||||||
|
|
||||||
|
// The heart and soul of the miner: match the target.
|
||||||
|
while (this.block.nonce <= 0xffffffff) {
|
||||||
|
if (utils.testTarget(this.block.target, this.block.hash()))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
this.block.nonce++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate our terrible hashrate
|
||||||
|
this.rate = (0xffffffff / (utils.now() - begin)) * 2;
|
||||||
|
|
||||||
|
// Overflow the nonce and increment the extraNonce.
|
||||||
|
this.block.nonce = 0;
|
||||||
|
this.block.extraNonce.iaddn(1);
|
||||||
|
|
||||||
|
// We incremented the extraNonce, need to update coinbase.
|
||||||
|
this.updateCoinbase();
|
||||||
|
|
||||||
|
// We changed the coinbase, need to update merkleRoot.
|
||||||
|
this.updateMerkle();
|
||||||
|
|
||||||
|
// Send progress report
|
||||||
|
this.emit('status', {
|
||||||
|
block: this.block,
|
||||||
|
target: this.block.target,
|
||||||
|
hashes: this.block.extraNonce.mul(0xffffffff).toString(10),
|
||||||
|
hashrate: this.rate
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expose
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = Miner;
|
||||||
|
|||||||
@ -563,6 +563,13 @@ Peer.prototype.loadItems = function loadItems(hashes, stop) {
|
|||||||
return this.loadBlocks(hashes, stop);
|
return this.loadBlocks(hashes, stop);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Peer.prototype.loadMempool = function loadMempool() {
|
||||||
|
this.emit('debug',
|
||||||
|
'Requesting inv packet from %s with mempool',
|
||||||
|
this.host);
|
||||||
|
this._write(this.framer.mempool());
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expose
|
* Expose
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -596,6 +596,12 @@ Pool.prototype._load = function _load() {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Pool.prototype.loadMempool = function loadMempool() {
|
||||||
|
this.pool.peers.block.forEach(function(peer) {
|
||||||
|
peer.loadMempool();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
Pool.prototype._addPeer = function _addPeer(backoff) {
|
Pool.prototype._addPeer = function _addPeer(backoff) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var peer;
|
var peer;
|
||||||
|
|||||||
@ -390,6 +390,10 @@ Framer.prototype.addr = function addr(peers) {
|
|||||||
return this.packet('addr', p);
|
return this.packet('addr', p);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Framer.prototype.mempool = function mempool() {
|
||||||
|
return this.packet('mempool', []);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expose
|
* Expose
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -515,6 +515,13 @@ Parser.prototype.parseAddr = function parseAddr(p) {
|
|||||||
return addrs;
|
return addrs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Parser.prototype.parseMempool = function parseMempool(p) {
|
||||||
|
if (p.length > 0)
|
||||||
|
return this._error('Invalid mempool size');
|
||||||
|
|
||||||
|
return {};
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expose
|
* Expose
|
||||||
*/
|
*/
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user