refactor: mining.

This commit is contained in:
Christopher Jeffrey 2017-03-10 03:57:44 -08:00
parent 50d47dd5a6
commit 945fa381c5
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
11 changed files with 1492 additions and 1147 deletions

View File

@ -17,4 +17,5 @@ exports.RPCClient = require('./rpcclient');
exports.Wallet = require('./wallet'); exports.Wallet = require('./wallet');
exports.Base = require('./base'); exports.Base = require('./base');
exports.RPC = require('./rpc'); exports.RPC = require('./rpc');
exports.RPCBase = require('./rpcbase');
exports.Server = require('./server'); exports.Server = require('./server');

View File

@ -20,7 +20,6 @@ var Block = require('../primitives/block');
var Headers = require('../primitives/headers'); var Headers = require('../primitives/headers');
var Input = require('../primitives/input'); var Input = require('../primitives/input');
var KeyRing = require('../primitives/keyring'); var KeyRing = require('../primitives/keyring');
var Lock = require('../utils/lock');
var MerkleBlock = require('../primitives/merkleblock'); var MerkleBlock = require('../primitives/merkleblock');
var MTX = require('../primitives/mtx'); var MTX = require('../primitives/mtx');
var Network = require('../protocol/network'); var Network = require('../protocol/network');
@ -64,7 +63,9 @@ function RPC(node) {
this.attempt = null; this.attempt = null;
this.lastActivity = 0; this.lastActivity = 0;
this.boundChain = false; this.boundChain = false;
this.nonces = {}; this.nonce1 = 0;
this.nonce2 = 0;
this.jobs = {};
this.pollers = []; this.pollers = [];
this.init(); this.init();
@ -920,38 +921,43 @@ RPC.prototype.submitWork = co(function* submitWork(data) {
RPC.prototype._submitWork = co(function* _submitWork(data) { RPC.prototype._submitWork = co(function* _submitWork(data) {
var attempt = this.attempt; var attempt = this.attempt;
var block, entry, header, nonce, ts, nonces; var header = Headers.fromAbbr(data);
var nonce = header.nonce;
var ts = header.ts;
var job, n1, n2, hash, block, entry;
if (!attempt) if (!attempt)
return false; return false;
block = attempt.block;
header = Headers.fromAbbr(data);
nonce = header.nonce;
ts = header.ts;
if (data.length !== 128) if (data.length !== 128)
throw new RPCError('Invalid work size.'); throw new RPCError('Invalid work size.');
data = data.slice(0, 80); data = data.slice(0, 80);
data = swap32(data); data = swap32(data);
if (header.prevBlock !== block.prevBlock if (header.prevBlock !== attempt.prevBlock
|| header.bits !== block.bits) { || header.bits !== attempt.bits) {
return false; return false;
} }
if (!header.verify()) if (!header.verify())
return false; return false;
nonces = this.nonces[header.merkleRoot]; job = this.jobs[header.merkleRoot];
if (!nonces) if (!job)
return false; return false;
if (!nonces.verify(attempt, nonce, ts)) n1 = job.nonce1;
n2 = job.nonce2;
hash = attempt.hash(n1, n2, ts, nonce);
if (!consensus.verifyPOW(hash, attempt.bits))
return false; return false;
block = attempt.commit(n1, n2, ts, nonce);
try { try {
entry = yield this.chain.add(block); entry = yield this.chain.add(block);
} catch (err) { } catch (err) {
@ -982,14 +988,17 @@ RPC.prototype.createWork = co(function* createWork(data) {
}); });
RPC.prototype._createWork = co(function* _createWork() { RPC.prototype._createWork = co(function* _createWork() {
var attempt = yield this.updateAttempt(); var attempt = yield this.updateWork();
var data, abbr; var n1 = this.nonce1;
var n2 = this.nonce2;
var ts = attempt.ts;
var data, head;
data = new Buffer(128); data = new Buffer(128);
data.fill(0); data.fill(0);
abbr = attempt.block.abbr(); head = attempt.getHeader(n1, n2, ts, 0);
abbr.copy(data, 0); head.copy(data, 0);
data[80] = 0x80; data[80] = 0x80;
data.writeUInt32BE(80 * 8, data.length - 4, true); data.writeUInt32BE(80 * 8, data.length - 4, true);
@ -1162,9 +1171,8 @@ RPC.prototype.createTemplate = co(function* createTemplate(version, coinbase, ru
}); });
RPC.prototype._createTemplate = co(function* _createTemplate(version, coinbase, rules) { RPC.prototype._createTemplate = co(function* _createTemplate(version, coinbase, rules) {
var attempt = yield this.getAttempt(); var attempt = yield this.getTemplate();
var scale = attempt.witness ? 1 : consensus.WITNESS_SCALE_FACTOR; var scale = attempt.witness ? 1 : consensus.WITNESS_SCALE_FACTOR;
var block = attempt.block;
var mutable = ['time', 'transactions', 'prevblock']; var mutable = ['time', 'transactions', 'prevblock'];
var txs = []; var txs = [];
var index = {}; var index = {};
@ -1217,11 +1225,11 @@ RPC.prototype._createTemplate = co(function* _createTemplate(version, coinbase,
case common.thresholdStates.FAILED: case common.thresholdStates.FAILED:
break; break;
case common.thresholdStates.LOCKED_IN: case common.thresholdStates.LOCKED_IN:
block.version |= 1 << deploy.bit; attempt.version |= 1 << deploy.bit;
case common.thresholdStates.STARTED: case common.thresholdStates.STARTED:
if (!deploy.force) { if (!deploy.force) {
if (rules.indexOf(name) === -1) if (rules.indexOf(name) === -1)
block.version &= ~(1 << deploy.bit); attempt.version &= ~(1 << deploy.bit);
name = '!' + name; name = '!' + name;
} }
vbavailable[name] = deploy.bit; vbavailable[name] = deploy.bit;
@ -1240,24 +1248,24 @@ RPC.prototype._createTemplate = co(function* _createTemplate(version, coinbase,
} }
} }
block.version >>>= 0; attempt.version >>>= 0;
json = { json = {
capabilities: ['proposal'], capabilities: ['proposal'],
mutable: mutable, mutable: mutable,
version: block.version, version: attempt.version,
rules: vbrules, rules: vbrules,
vbavailable: vbavailable, vbavailable: vbavailable,
vbrequired: 0, vbrequired: 0,
height: attempt.height, height: attempt.height,
previousblockhash: util.revHex(block.prevBlock), previousblockhash: util.revHex(attempt.prevBlock),
target: util.revHex(attempt.target.toString('hex')), target: util.revHex(attempt.target.toString('hex')),
bits: util.hex32(block.bits), bits: util.hex32(attempt.bits),
noncerange: '00000000ffffffff', noncerange: '00000000ffffffff',
curtime: block.ts, curtime: attempt.ts,
mintime: block.ts, mintime: attempt.ts,
maxtime: block.ts + 7200, maxtime: attempt.ts + 7200,
expires: block.ts + 7200, expires: attempt.ts + 7200,
sigoplimit: consensus.MAX_BLOCK_SIGOPS_COST / scale | 0, sigoplimit: consensus.MAX_BLOCK_SIGOPS_COST / scale | 0,
sizelimit: consensus.MAX_BLOCK_SIZE, sizelimit: consensus.MAX_BLOCK_SIZE,
weightlimit: undefined, weightlimit: undefined,
@ -1266,7 +1274,7 @@ RPC.prototype._createTemplate = co(function* _createTemplate(version, coinbase,
coinbaseaux: { coinbaseaux: {
flags: attempt.coinbaseFlags.toString('hex') flags: attempt.coinbaseFlags.toString('hex')
}, },
coinbasevalue: attempt.coinbase.getOutputValue(), coinbasevalue: attempt.getReward(),
coinbasetxn: undefined, coinbasetxn: undefined,
default_witness_commitment: undefined, default_witness_commitment: undefined,
transactions: txs transactions: txs
@ -1280,13 +1288,14 @@ RPC.prototype._createTemplate = co(function* _createTemplate(version, coinbase,
} }
if (coinbase) { if (coinbase) {
tx = attempt.coinbase; tx = attempt.toCoinbase();
// We don't include the commitment // We don't include the commitment
// output (see bip145). // output (see bip145).
if (attempt.witness) { if (attempt.witness) {
output = tx.outputs.pop(); output = tx.outputs.pop();
assert(output.script.isCommitment()); assert(output.script.isCommitment());
tx.refresh();
} }
json.coinbasetxn = { json.coinbasetxn = {
@ -1298,13 +1307,10 @@ RPC.prototype._createTemplate = co(function* _createTemplate(version, coinbase,
sigops: tx.getSigopsCost() / scale | 0, sigops: tx.getSigopsCost() / scale | 0,
weight: tx.getWeight() weight: tx.getWeight()
}; };
if (attempt.witness)
tx.outputs.push(output);
} }
if (attempt.witness) { if (attempt.witness) {
tx = attempt.coinbase; tx = attempt.toCoinbase();
output = tx.outputs[tx.outputs.length - 1]; output = tx.outputs[tx.outputs.length - 1];
assert(output.script.isCommitment()); assert(output.script.isCommitment());
json.default_witness_commitment = output.script.toJSON(); json.default_witness_commitment = output.script.toJSON();
@ -1315,21 +1321,19 @@ RPC.prototype._createTemplate = co(function* _createTemplate(version, coinbase,
RPC.prototype.getMiningInfo = co(function* getMiningInfo(args, help) { RPC.prototype.getMiningInfo = co(function* getMiningInfo(args, help) {
var attempt = this.attempt; var attempt = this.attempt;
var hashps; var scale = attempt.witness ? 1 : consensus.WITNESS_SCALE_FACTOR;
if (help || args.length !== 0) if (help || args.length !== 0)
throw new RPCError('getmininginfo'); throw new RPCError('getmininginfo');
hashps = yield this.getHashRate(120);
return { return {
blocks: this.chain.height, blocks: this.chain.height,
currentblocksize: attempt ? attempt.block.getBaseSize() : 0, currentblocksize: attempt ? attempt.weight / scale | 0 : 0,
currentblocktx: attempt ? attempt.block.txs.length : 0, currentblocktx: attempt ? attempt.items.length + 1 : 0,
difficulty: this.difficulty(), difficulty: this.difficulty(),
errors: '', errors: '',
genproclimit: this.procLimit, genproclimit: this.procLimit,
networkhashps: hashps, networkhashps: yield this.getHashRate(120),
pooledtx: this.totalTX(), pooledtx: this.totalTX(),
testnet: this.network !== Network.main, testnet: this.network !== Network.main,
chain: 'main', chain: 'main',
@ -2113,7 +2117,9 @@ RPC.prototype.refreshBlock = function refreshBlock() {
this.attempt = null; this.attempt = null;
this.lastActivity = 0; this.lastActivity = 0;
this.coinbase = {}; this.jobs = {};
this.nonce1 = 0;
this.nonce2 = 0;
this.pollers = []; this.pollers = [];
for (i = 0; i < pollers.length; i++) { for (i = 0; i < pollers.length; i++) {
@ -2149,7 +2155,7 @@ RPC.prototype.bindChain = function bindChain() {
}); });
}; };
RPC.prototype.getAttempt = co(function* getAttempt() { RPC.prototype.getTemplate = co(function* getTemplate() {
var attempt = this.attempt; var attempt = this.attempt;
this.bindChain(); this.bindChain();
@ -2163,22 +2169,31 @@ RPC.prototype.getAttempt = co(function* getAttempt() {
return attempt; return attempt;
}); });
RPC.prototype.updateAttempt = co(function* updateAttempt() { RPC.prototype.updateWork = co(function* updateWork() {
var attempt = this.attempt; var attempt = this.attempt;
var root;
this.bindChain(); this.bindChain();
if (attempt) { if (attempt) {
attempt.updateNonce(); if (++this.nonce1 === 0xffffffff) {
this.nonces[block.merkleRoot] = attempt.snapshot(); this.nonce1 = 0;
this.nonce2++;
}
root = attempt.getRoot(this.nonce1, this.nonce2);
root = root.toString('hex');
this.jobs[root] = new Nonces(this);
return attempt; return attempt;
} }
attempt = yield this.miner.createBlock(); attempt = yield this.miner.createBlock();
root = attempt.getRoot(this.nonce1, this.nonce2);
root = root.toString('hex');
this.attempt = attempt; this.attempt = attempt;
this.lastActivity = util.now(); this.lastActivity = util.now();
this.nonces[block.merkleRoot] = attempt.snapshot(); this.jobs[root] = new Nonces(this);
return attempt; return attempt;
}); });
@ -2530,6 +2545,11 @@ function toDeployment(id, version, status) {
}; };
} }
function Nonces(rpc) {
this.nonce1 = rpc.nonce1;
this.nonce2 = rpc.nonce2;
}
/* /*
* Expose * Expose
*/ */

562
lib/mining/cpuminer.js Normal file
View File

@ -0,0 +1,562 @@
/*!
* cpuminer.js - inefficient cpu miner for bcoin (because we can)
* Copyright (c) 2014-2015, Fedor Indutny (MIT License)
* Copyright (c) 2014-2017, Christopher Jeffrey (MIT License).
* https://github.com/bcoin-org/bcoin
*/
'use strict';
var assert = require('assert');
var util = require('../utils/util');
var co = require('../utils/co');
var AsyncObject = require('../utils/asyncobject');
var workerPool = require('../workers/workerpool').pool;
var mine = require('./mine');
/**
* CPU miner.
* @alias module:mining.CPUMiner
* @constructor
* @param {Miner} miner
* @emits CPUMiner#block
* @emits CPUMiner#status
*/
function CPUMiner(miner) {
if (!(this instanceof CPUMiner))
return new CPUMiner(miner);
AsyncObject.call(this);
this.miner = miner;
this.network = this.miner.network;
this.logger = this.miner.logger;
this.chain = this.miner.chain;
this.running = false;
this.stopping = false;
this.job = null;
this.since = 0;
this._init();
}
util.inherits(CPUMiner, AsyncObject);
/**
* Nonce range interval.
* @const {Number}
* @default
*/
CPUMiner.INTERVAL = 0xffffffff / 1500 | 0;
/**
* Initialize the miner.
* @private
*/
CPUMiner.prototype._init = function _init() {
var self = this;
this.chain.on('tip', function(tip) {
if (!self.job)
return;
if (self.job.attempt.prevBlock === tip.prevBlock)
self.job.destroy();
});
this.on('block', function(block, entry) {
// Emit the block hex as a failsafe (in case we can't send it)
self.logger.info('Found block: %d (%s).', entry.height, entry.rhash());
self.logger.debug('Raw: %s', block.toRaw().toString('hex'));
});
this.on('status', function(stat) {
// Send progress report.
self.logger.info(
'CPUMiner: hashrate=%dkhs hashes=%d target=%d height=%d best=%s',
stat.hashrate / 1000 | 0,
stat.hashes,
stat.target,
stat.height,
stat.best);
});
};
/**
* Open the miner.
* @method
* @alias module:mining.CPUMiner#open
* @returns {Promise}
*/
CPUMiner.prototype._open = co(function* open() {
});
/**
* Close the miner.
* @method
* @alias module:mining.CPUMiner#close
* @returns {Promise}
*/
CPUMiner.prototype._close = co(function* close() {
if (!this.running)
return;
if (this.stopping) {
yield this._onStop();
return;
}
yield this.stop();
});
/**
* Start mining.
* @method
* @returns {Promise}
*/
CPUMiner.prototype.start = co(function* start() {
var block, entry;
assert(!this.running, 'CPUMiner is already running.');
this.running = true;
this.stopping = false;
for (;;) {
this.job = null;
try {
this.job = yield this.createJob();
} catch (e) {
if (this.stopping)
break;
this.emit('error', e);
continue;
}
if (this.stopping)
break;
try {
block = yield this.mineAsync(this.job);
} catch (e) {
if (this.stopping)
break;
this.emit('error', e);
continue;
}
if (this.stopping)
break;
if (!block)
continue;
try {
entry = yield this.chain.add(block);
} catch (e) {
if (this.stopping)
break;
this.emit('error', e);
continue;
}
if (!entry) {
this.logger.warning('Mined a bad-prevblk (race condition?)');
continue;
}
if (this.stopping)
break;
this.emit('block', block, entry);
}
this.emit('done');
});
/**
* Stop mining.
* @method
* @returns {Promise}
*/
CPUMiner.prototype.stop = co(function* stop() {
assert(this.running, 'CPUMiner is not running.');
assert(!this.stopping, 'CPUMiner is already stopping.');
this.stopping = true;
yield this._onDone();
this.running = false;
this.stopping = false;
this.job = null;
this.emit('stop');
});
/**
* Wait for `done` event.
* @private
* @returns {Promise}
*/
CPUMiner.prototype._onDone = function _onDone() {
var self = this;
return new Promise(function(resolve, reject) {
self.once('done', resolve);
});
};
/**
* Wait for `stop` event.
* @private
* @returns {Promise}
*/
CPUMiner.prototype._onStop = function _onStop() {
var self = this;
return new Promise(function(resolve, reject) {
self.once('stop', resolve);
});
};
/**
* Create a mining job.
* @method
* @param {ChainEntry?} tip
* @param {Address?} address
* @returns {Promise} - Returns {@link Job}.
*/
CPUMiner.prototype.createJob = co(function* createJob(tip, address) {
var attempt = yield this.miner.createBlock(tip, address);
return new CPUJob(this, attempt);
});
/**
* Mine a single block.
* @method
* @param {ChainEntry?} tip
* @param {Address?} address
* @returns {Promise} - Returns [{@link Block}].
*/
CPUMiner.prototype.mineBlock = co(function* mineBlock(tip, address) {
var job = yield this.createJob(tip, address);
return yield this.mineAsync(job);
});
/**
* Notify the miner that a new
* tx has entered the mempool.
*/
CPUMiner.prototype.notifyEntry = function notifyEntry() {
if (!this.running)
return;
if (!this.job)
return;
if (++this.since > 20) {
this.since = 0;
this.job.destroy();
}
};
/**
* Hash until the nonce overflows.
* @param {CPUJob} job
* @returns {Number} nonce
*/
CPUMiner.prototype.findNonce = function findNonce(job) {
var data = job.getHeader(0);
var target = job.attempt.target;
var interval = CPUMiner.INTERVAL;
var min = 0;
var max = interval;
var nonce;
while (max <= 0xffffffff) {
nonce = mine(data, target, min, max);
if (nonce !== -1)
break;
this.sendStatus(job, max);
min += interval;
max += interval;
}
return nonce;
};
/**
* Hash until the nonce overflows.
* @method
* @param {CPUJob} job
* @returns {Promise} Returns Number.
*/
CPUMiner.prototype.findNonceAsync = co(function* findNonceAsync(job) {
var data = job.getHeader(0);
var target = job.attempt.target;
var interval = CPUMiner.INTERVAL;
var min = 0;
var max = interval;
var nonce;
while (max <= 0xffffffff) {
nonce = yield workerPool.mine(data, target, min, max);
if (nonce !== -1)
break;
if (job.destroyed)
return nonce;
this.sendStatus(job, max);
min += interval;
max += interval;
}
return nonce;
});
/**
* Mine synchronously until the block is found.
* @param {CPUJob} job
* @returns {Block}
*/
CPUMiner.prototype.mine = function mine(job) {
var nonce;
// Track how long we've been at it.
job.begin = util.now();
for (;;) {
nonce = this.findNonce(job);
if (nonce !== -1)
break;
this.iterate(job);
}
return job.commit(nonce);
};
/**
* Mine asynchronously until the block is found.
* @method
* @param {CPUJob} job
* @returns {Promise} - Returns {@link Block}.
*/
CPUMiner.prototype.mineAsync = co(function* mineAsync(job) {
var nonce;
// Track how long we've been at it.
job.begin = util.now();
for (;;) {
nonce = yield this.findNonceAsync(job);
if (nonce !== -1)
break;
if (job.destroyed)
return;
this.iterate(job);
}
return job.commit(nonce);
});
/**
* Increment extraNonce and send status.
* @param {CPUJob} job
*/
CPUMiner.prototype.iterate = function iterate(job) {
job.iterations++;
job.updateNonce();
this.sendStatus(job, 0);
};
/**
* Send a progress report (emits `status`).
* @param {CPUJob} job
* @param {Number} nonce
*/
CPUMiner.prototype.sendStatus = function sendStatus(job, nonce) {
this.emit('status', {
target: job.attempt.bits,
hashes: job.getHashes(),
hashrate: job.getRate(nonce),
height: job.attempt.height,
best: util.revHex(job.attempt.prevBlock)
});
};
/**
* Mining Job
* @constructor
* @ignore
* @param {CPUMiner} miner
* @param {BlockTemplate} attempt
*/
function CPUJob(miner, attempt) {
this.miner = miner;
this.attempt = attempt;
this.destroyed = false;
this.committed = false;
this.iterations = 0;
this.begin = 0;
this.nonce1 = 0;
this.nonce2 = 0;
this.refresh();
}
/**
* Get the raw block header.
* @param {Number} nonce
* @returns {Buffer}
*/
CPUJob.prototype.getHeader = function getHeader(nonce) {
var attempt = this.attempt;
var n1 = this.nonce1;
var n2 = this.nonce2;
var ts = attempt.ts;
return this.attempt.getHeader(n1, n2, ts, nonce);
};
/**
* Commit job and return a block.
* @param {Number} nonce
* @returns {Block}
*/
CPUJob.prototype.commit = function commit(nonce) {
var attempt = this.attempt;
var n1 = this.nonce1;
var n2 = this.nonce2;
var ts = attempt.ts;
assert(!this.committed, 'Job already committed.');
this.committed = true;
return this.attempt.commit(n1, n2, ts, nonce);
};
/**
* Mine block synchronously.
* @returns {Block}
*/
CPUJob.prototype.mine = function mine() {
return this.miner.mine(this);
};
/**
* Mine block asynchronously.
* @returns {Promise}
*/
CPUJob.prototype.mineAsync = function mineAsync() {
return this.miner.mineAsync(this);
};
/**
* Refresh the block template.
*/
CPUJob.prototype.refresh = function refresh() {
return this.attempt.refresh();
};
/**
* Increment the extraNonce.
*/
CPUJob.prototype.updateNonce = function() {
// Overflow the nonce and increment the extraNonce.
this.nonce1++;
// Wrap at 4 bytes.
if (this.nonce1 === 0xffffffff) {
this.nonce1 = 0;
this.nonce2++;
}
};
/**
* Destroy the job.
*/
CPUJob.prototype.destroy = function() {
assert(!this.destroyed, 'Job already destroyed.');
this.destroyed = true;
};
/**
* Calculate number of hashes.
* @returns {Number}
*/
CPUJob.prototype.getHashes = function() {
return this.iterations * 0xffffffff + this.block.nonce;
};
/**
* Calculate hashrate.
* @returns {Number}
*/
CPUJob.prototype.getRate = function(nonce) {
return (nonce / (util.now() - this.begin)) | 0;
};
/**
* Add a transaction to the block.
* @param {TX} tx
* @param {CoinView} view
*/
CPUJob.prototype.addTX = function(tx, view) {
return this.attempt.addTX(tx, view);
};
/**
* Add a transaction to the block
* (less verification than addTX).
* @param {TX} tx
* @param {CoinView?} view
*/
CPUJob.prototype.pushTX = function(tx, view) {
return this.attempt.pushTX(tx, view);
};
/*
* Expose
*/
module.exports = CPUMiner;

View File

@ -4,6 +4,7 @@
* @module mining * @module mining
*/ */
exports.BlockTemplate = require('./template');
exports.CPUMiner = require('./cpuminer');
exports.mine = require('./mine'); exports.mine = require('./mine');
exports.Miner = require('./miner'); exports.Miner = require('./miner');
exports.MinerBlock = require('./minerblock');

View File

@ -1,5 +1,5 @@
/*! /*!
* miner.js - inefficient miner for bcoin (because we can) * miner.js - block generator for bcoin
* Copyright (c) 2014-2015, Fedor Indutny (MIT License) * Copyright (c) 2014-2015, Fedor Indutny (MIT License)
* Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License).
* https://github.com/bcoin-org/bcoin * https://github.com/bcoin-org/bcoin
@ -14,23 +14,18 @@ var Heap = require('../utils/heap');
var AsyncObject = require('../utils/asyncobject'); var AsyncObject = require('../utils/asyncobject');
var Amount = require('../btc/amount'); var Amount = require('../btc/amount');
var Address = require('../primitives/address'); var Address = require('../primitives/address');
var MinerBlock = require('./minerblock'); var BlockTemplate = require('./template');
var Network = require('../protocol/network'); var Network = require('../protocol/network');
var consensus = require('../protocol/consensus'); var consensus = require('../protocol/consensus');
var policy = require('../protocol/policy'); var policy = require('../protocol/policy');
var BlockEntry = MinerBlock.BlockEntry; var CPUMiner = require('./cpuminer');
var BlockEntry = BlockTemplate.BlockEntry;
/** /**
* A bitcoin miner (supports mining witness blocks). * A bitcoin miner and block generator.
* @alias module:mining.Miner * @alias module:mining.Miner
* @constructor * @constructor
* @param {Object} options * @param {Object} options
* @param {Address} options.address - Payout address.
* @param {String} [options.coinbaseFlags="mined by bcoin"]
* @property {Boolean} running
* @property {MinerBlock} attempt
* @emits Miner#block
* @emits Miner#status
*/ */
function Miner(options) { function Miner(options) {
@ -40,58 +35,17 @@ function Miner(options) {
AsyncObject.call(this); AsyncObject.call(this);
this.options = new MinerOptions(options); this.options = new MinerOptions(options);
this.network = this.options.network; this.network = this.options.network;
this.logger = this.options.logger; this.logger = this.options.logger;
this.chain = this.options.chain; this.chain = this.options.chain;
this.mempool = this.options.mempool; this.mempool = this.options.mempool;
this.addresses = this.options.addresses; this.addresses = this.options.addresses;
this.locker = this.chain.locker; this.locker = this.chain.locker;
this.cpu = new CPUMiner(this);
this.running = false;
this.stopping = false;
this.attempt = null;
this.since = 0;
this._init();
} }
util.inherits(Miner, AsyncObject); util.inherits(Miner, AsyncObject);
/**
* Initialize the miner.
* @private
*/
Miner.prototype._init = function _init() {
var self = this;
this.chain.on('tip', function(tip) {
if (!self.attempt)
return;
if (self.attempt.block.prevBlock === tip.prevBlock)
self.attempt.destroy();
});
this.on('block', function(block, entry) {
// Emit the block hex as a failsafe (in case we can't send it)
self.logger.info('Found block: %d (%s).', entry.height, entry.rhash());
self.logger.debug('Raw: %s', block.toRaw().toString('hex'));
});
this.on('status', function(stat) {
self.logger.info(
'Miner: hashrate=%dkhs hashes=%d target=%d height=%d best=%s',
stat.hashrate / 1000 | 0,
stat.hashes,
stat.target,
stat.height,
stat.best);
});
};
/** /**
* Open the miner, wait for the chain and mempool to load. * Open the miner, wait for the chain and mempool to load.
* @method * @method
@ -105,6 +59,8 @@ Miner.prototype._open = co(function* open() {
if (this.mempool) if (this.mempool)
yield this.mempool.open(); yield this.mempool.open();
yield this.cpu.open();
this.logger.info('Miner loaded (flags=%s).', this.logger.info('Miner loaded (flags=%s).',
this.options.coinbaseFlags.toString('utf8')); this.options.coinbaseFlags.toString('utf8'));
@ -120,145 +76,15 @@ Miner.prototype._open = co(function* open() {
*/ */
Miner.prototype._close = co(function* close() { Miner.prototype._close = co(function* close() {
if (!this.running) yield this.cpu.close();
return;
if (this.stopping) {
yield this._onStop();
return;
}
yield this.stop();
}); });
/** /**
* Start mining. * Create a block template.
* @method * @method
* @param {Number?} version - Custom block version. * @param {ChainEntry?} tip
* @returns {Promise} * @param {Address?} address
*/ * @returns {Promise} - Returns {@link BlockTemplate}.
Miner.prototype.start = co(function* start() {
var self = this;
var block, entry;
assert(!this.running, 'Miner is already running.');
this.running = true;
this.stopping = false;
for (;;) {
this.attempt = null;
try {
this.attempt = yield this.createBlock();
} catch (e) {
if (this.stopping)
break;
this.emit('error', e);
continue;
}
if (this.stopping)
break;
this.attempt.on('status', function(status) {
self.emit('status', status);
});
try {
block = yield this.attempt.mineAsync();
} catch (e) {
if (this.stopping)
break;
this.emit('error', e);
continue;
}
if (this.stopping)
break;
if (!block)
continue;
try {
entry = yield this.chain.add(block);
} catch (e) {
if (this.stopping)
break;
this.emit('error', e);
continue;
}
if (!entry) {
this.logger.warning('Mined a bad-prevblk (race condition?)');
continue;
}
if (this.stopping)
break;
this.emit('block', block, entry);
}
this.emit('done');
});
/**
* Stop mining.
* @method
* @returns {Promise}
*/
Miner.prototype.stop = co(function* stop() {
assert(this.running, 'Miner is not running.');
assert(!this.stopping, 'Miner is already stopping.');
this.stopping = true;
if (this.attempt)
this.attempt.destroy();
yield this._onDone();
this.running = false;
this.stopping = false;
this.attempt = null;
this.emit('stop');
});
/**
* Wait for `done` event.
* @private
* @returns {Promise}
*/
Miner.prototype._onDone = function _onDone() {
var self = this;
return new Promise(function(resolve, reject) {
self.once('done', resolve);
});
};
/**
* Wait for `stop` event.
* @private
* @returns {Promise}
*/
Miner.prototype._onStop = function _onStop() {
var self = this;
return new Promise(function(resolve, reject) {
self.once('stop', resolve);
});
};
/**
* Create a block "attempt".
* @method
* @param {ChainEntry} tip
* @returns {Promise} - Returns {@link MinerBlock}.
*/ */
Miner.prototype.createBlock = co(function* createBlock(tip, address) { Miner.prototype.createBlock = co(function* createBlock(tip, address) {
@ -271,16 +97,17 @@ Miner.prototype.createBlock = co(function* createBlock(tip, address) {
}); });
/** /**
* Create a block "attempt" (without a lock). * Create a block template (without a lock).
* @method * @method
* @private * @private
* @param {ChainEntry} tip * @param {ChainEntry?} tip
* @returns {Promise} - Returns {@link MinerBlock}. * @param {Address?} address
* @returns {Promise} - Returns {@link BlockTemplate}.
*/ */
Miner.prototype._createBlock = co(function* createBlock(tip, address) { Miner.prototype._createBlock = co(function* createBlock(tip, address) {
var version = this.options.version; var version = this.options.version;
var ts, locktime, target, attempt; var ts, mtp, locktime, target, attempt, block;
if (!tip) if (!tip)
tip = this.chain.tip; tip = this.chain.tip;
@ -291,19 +118,18 @@ Miner.prototype._createBlock = co(function* createBlock(tip, address) {
if (version === -1) if (version === -1)
version = yield this.chain.computeBlockVersion(tip); version = yield this.chain.computeBlockVersion(tip);
if (this.chain.state.hasMTP()) { mtp = yield tip.getMedianTime();
locktime = yield tip.getMedianTime(); ts = Math.max(this.network.now(), mtp + 1);
ts = Math.max(this.network.now(), locktime + 1); locktime = ts;
} else {
ts = Math.max(this.network.now(), tip.ts + 1); if (this.chain.state.hasMTP())
locktime = ts; locktime = mtp;
}
target = yield this.chain.getTarget(ts, tip); target = yield this.chain.getTarget(ts, tip);
attempt = new MinerBlock({ attempt = new BlockTemplate({
network: this.network, prevBlock: tip.hash,
tip: tip, height: tip.height + 1,
version: version, version: version,
ts: ts, ts: ts,
bits: target, bits: target,
@ -312,11 +138,12 @@ Miner.prototype._createBlock = co(function* createBlock(tip, address) {
address: address, address: address,
coinbaseFlags: this.options.coinbaseFlags, coinbaseFlags: this.options.coinbaseFlags,
witness: this.chain.state.hasWitness(), witness: this.chain.state.hasWitness(),
halvingInterval: this.network.halvingInterval,
weight: this.options.reservedWeight, weight: this.options.reservedWeight,
sigops: this.options.reservedSigops sigops: this.options.reservedSigops
}); });
this.build(attempt); this.assemble(attempt);
this.logger.debug( this.logger.debug(
'Created miner block (height=%d, weight=%d, fees=%d, txs=%s).', 'Created miner block (height=%d, weight=%d, fees=%d, txs=%s).',
@ -326,8 +153,10 @@ Miner.prototype._createBlock = co(function* createBlock(tip, address) {
attempt.items.length + 1); attempt.items.length + 1);
if (this.options.preverify) { if (this.options.preverify) {
block = attempt.toBlock();
try { try {
yield this.chain._verifyBlock(attempt.block); yield this.chain._verifyBlock(block);
} catch (e) { } catch (e) {
if (e.type === 'VerifyError') { if (e.type === 'VerifyError') {
this.logger.warning('Miner created invalid block!'); this.logger.warning('Miner created invalid block!');
@ -346,34 +175,28 @@ Miner.prototype._createBlock = co(function* createBlock(tip, address) {
}); });
/** /**
* Mine a single block. * Create a cpu miner job.
* @method * @method
* @param {ChainEntry} tip * @param {ChainEntry?} tip
* @returns {Promise} - Returns [{@link Block}]. * @param {Address?} address
* @returns {Promise} Returns {@link CPUJob}.
*/ */
Miner.prototype.mineBlock = co(function* mineBlock(tip, address) { Miner.prototype.createJob = co(function* createJob(tip, address) {
var attempt = yield this.createBlock(tip, address); return yield this.cpu.createJob(tip, address);
return yield attempt.mineAsync();
}); });
/** /**
* Notify the miner that a new tx has entered the mempool. * Mine a single block.
* @param {MempoolEntry} entry * @method
* @param {ChainEntry?} tip
* @param {Address?} address
* @returns {Promise} Returns {@link Block}.
*/ */
Miner.prototype.notifyEntry = function notifyEntry() { Miner.prototype.mineBlock = co(function* mineBlock(tip, address) {
if (!this.running) return yield this.cpu.mineBlock(tip, address);
return; });
if (!this.attempt)
return;
if (++this.since > 20) {
this.since = 0;
this.attempt.destroy();
}
};
/** /**
* Add an address to the address list. * Add an address to the address list.
@ -391,23 +214,23 @@ Miner.prototype.addAddress = function addAddress(address) {
Miner.prototype.getAddress = function getAddress() { Miner.prototype.getAddress = function getAddress() {
if (this.addresses.length === 0) if (this.addresses.length === 0)
return; return new Address();
return this.addresses[Math.random() * this.addresses.length | 0]; return this.addresses[Math.random() * this.addresses.length | 0];
}; };
/** /**
* Get mempool entries, sort by dependency order. * Get mempool entries, sort by dependency order.
* Prioritize by priority and fee rates. * Prioritize by priority and fee rates.
* @param {BlockTemplate} attempt
* @returns {MempoolEntry[]} * @returns {MempoolEntry[]}
*/ */
Miner.prototype.build = function build(attempt) { Miner.prototype.assemble = function assemble(attempt) {
var depMap = {}; var depMap = {};
var block = attempt.block;
var queue = new Heap(cmpRate); var queue = new Heap(cmpRate);
var priority = this.options.priorityWeight > 0; var priority = this.options.priorityWeight > 0;
var i, j, entry, item, tx, hash, input; var i, j, entry, item, tx, hash, input;
var prev, deps, hashes, weight, sigops; var prev, deps, hashes, weight, sigops, block;
if (priority) if (priority)
queue.set(cmpPriority); queue.set(cmpPriority);
@ -492,8 +315,6 @@ Miner.prototype.build = function build(attempt) {
attempt.fees += item.fee; attempt.fees += item.fee;
attempt.items.push(item); attempt.items.push(item);
block.txs.push(tx);
deps = depMap[hash]; deps = depMap[hash];
if (!deps) if (!deps)
@ -508,10 +329,18 @@ Miner.prototype.build = function build(attempt) {
attempt.refresh(); attempt.refresh();
assert(block.getWeight() <= attempt.weight, assert(attempt.weight <= consensus.MAX_BLOCK_WEIGHT,
'Block exceeds reserved weight!'); 'Block exceeds reserved weight!');
assert(block.getBaseSize() <= consensus.MAX_BLOCK_SIZE,
'Block exceeds max block size.'); if (this.options.preverify) {
block = attempt.toBlock();
assert(block.getWeight() <= attempt.weight,
'Block exceeds reserved weight!');
assert(block.getBaseSize() <= consensus.MAX_BLOCK_SIZE,
'Block exceeds max block size.');
}
}; };
/** /**

View File

@ -1,719 +0,0 @@
/*!
* minerblock.js - miner block object for bcoin (because we can)
* Copyright (c) 2014-2015, Fedor Indutny (MIT License)
* Copyright (c) 2014-2017, Christopher Jeffrey (MIT License).
* https://github.com/bcoin-org/bcoin
*/
'use strict';
var assert = require('assert');
var EventEmitter = require('events').EventEmitter;
var BN = require('bn.js');
var util = require('../utils/util');
var co = require('../utils/co');
var StaticWriter = require('../utils/staticwriter');
var Network = require('../protocol/network');
var Address = require('../primitives/address');
var TX = require('../primitives/tx');
var Block = require('../primitives/block');
var Input = require('../primitives/input');
var Output = require('../primitives/output');
var mine = require('./mine');
var workerPool = require('../workers/workerpool').pool;
var consensus = require('../protocol/consensus');
var policy = require('../protocol/policy');
var encoding = require('../utils/encoding');
/**
* MinerBlock (block attempt)
* @alias module:mining.MinerBlock
* @constructor
* @param {Object} options
* @param {ChainEntry} options.tip
* @param {Number} options.height
* @param {Number} options.target - Compact form.
* @param {Base58Address} options.address - Payout address.
* @param {Boolean} options.witness - Allow witness
* transactions, mine a witness block.
* @param {String} options.coinbaseFlags
* @property {Block} block
* @property {TX} coinbase
* @property {BN} hashes - Number of hashes attempted.
* @property {Number} rate - Hash rate.
* @emits MinerBlock#status
*/
function MinerBlock(options) {
if (!(this instanceof MinerBlock))
return new MinerBlock(options);
EventEmitter.call(this);
this.network = Network.get(options.network);
this.tip = options.tip;
this.version = options.version;
this.height = options.tip.height + 1;
this.ts = options.ts;
this.bits = options.bits;
this.target = consensus.fromCompact(this.bits).toArrayLike(Buffer, 'le', 32);
this.locktime = options.locktime;
this.flags = options.flags;
this.nonce1 = 0;
this.nonce2 = 0;
this.iterations = 0;
this.coinbaseFlags = options.coinbaseFlags;
this.witness = options.witness;
this.address = options.address;
this.reward = consensus.getReward(this.height, this.network.halvingInterval);
this.destroyed = false;
this.committed = false;
this.sigops = options.sigops;
this.weight = options.weight;
this.fees = 0;
this.items = [];
this.coinbase = new TX();
this.coinbase.mutable = true;
this.block = new Block();
this.block.mutable = true;
this._init();
}
util.inherits(MinerBlock, EventEmitter);
/**
* Nonce range interval.
* @const {Number}
* @default
*/
MinerBlock.INTERVAL = 0xffffffff / 1500 | 0;
/**
* Calculate number of hashes.
* @returns {Number}
*/
MinerBlock.prototype.getHashes = function() {
return this.iterations * 0xffffffff + this.block.nonce;
};
/**
* Calculate hashrate.
* @returns {Number}
*/
MinerBlock.prototype.getRate = function() {
return (this.block.nonce / (util.now() - this.begin)) | 0;
};
/**
* Initialize the block.
* @private
*/
MinerBlock.prototype._init = function _init() {
var scale = consensus.WITNESS_SCALE_FACTOR;
var hash = encoding.ZERO_HASH;
var block = this.block;
var cb = this.coinbase;
var weight = 0;
var sigops = 0;
var input, output, commit, padding;
assert(this.coinbaseFlags.length <= 20);
// Setup our block.
block.version = this.version;
block.prevBlock = this.tip.hash;
block.merkleRoot = encoding.NULL_HASH;
block.ts = this.ts;
block.bits = this.bits;
block.nonce = 0;
// Coinbase input.
input = new Input();
// Height (required in v2+ blocks)
input.script.set(0, new BN(this.height));
// Let the world know this little
// miner succeeded.
input.script.set(1, encoding.ZERO_HASH160);
// Smaller nonce for good measure.
input.script.set(2, util.nonce().slice(0, 4));
// extraNonce - incremented when
// the nonce overflows.
input.script.set(3, this.extraNonce());
input.script.compile();
// Set up the witness nonce.
if (this.witness) {
input.witness.set(0, block.createWitnessNonce());
input.witness.compile();
}
cb.inputs.push(input);
// Reward output.
output = new Output();
output.script.fromPubkeyhash(encoding.ZERO_HASH160);
cb.outputs.push(output);
// If we're using segwit, we
// need to set up the commitment.
if (this.witness) {
// Commitment output.
commit = new Output();
commit.script.fromCommitment(hash);
cb.outputs.push(commit);
}
block.txs.push(cb);
// Initialize weight.
weight = block.getWeight();
// 4 extra bytes for varint tx count.
weight += 4 * scale;
// Padding for the CB height (constant size).
padding = 5 - input.script.code[0].getSize();
assert(padding >= 0);
weight += padding * scale;
// Reserved size.
// Without segwit:
// Block weight = 840
// Block stripped size = 210
// Block size = 210
// CB weight = 500
// CB stripped size = 125
// CB size = 125
// Sigops cost = 4
// With segwit:
// Block weight = 1064
// Block stripped size = 257
// Block size = 293
// CB weight = 724
// CB stripped size = 172
// CB size = 208
// Sigops cost = 4
if (!this.witness) {
assert.equal(weight, 840);
assert.equal(block.getBaseSize() + 4 + padding, 210);
assert.equal(block.getSize() + 4 + padding, 210);
assert.equal(cb.getWeight() + padding * scale, 500);
assert.equal(cb.getBaseSize() + padding, 125);
assert.equal(cb.getSize() + padding, 125);
} else {
assert.equal(weight, 1064);
assert.equal(block.getBaseSize() + 4 + padding, 257);
assert.equal(block.getSize() + 4 + padding, 293);
assert.equal(cb.getWeight() + padding * scale, 724);
assert.equal(cb.getBaseSize() + padding, 172);
assert.equal(cb.getSize() + padding, 208);
}
// Initialize sigops weight.
sigops = 4;
// Setup coinbase flags (variable size).
input.script.set(1, this.coinbaseFlags);
input.script.compile();
// Setup output script (variable size).
if (this.address) {
output.script.clear();
output.script.fromAddress(this.address);
}
// Update commitments.
this.refresh();
// Ensure the variable size
// stuff didn't break anything.
assert(block.getWeight() <= weight,
'Coinbase exceeds reserved size!');
assert(cb.getSigopsCost(null, this.flags) <= sigops,
'Coinbase exceeds reserved sigops!');
assert(this.weight >= weight,
'Coinbase exceeds reserved size!');
assert(this.sigops >= sigops,
'Coinbase exceeds reserved sigops!');
};
/**
* Update coinbase, witness
* commitment, and merkle root.
*/
MinerBlock.prototype.refresh = function refresh() {
// Update coinbase.
this.updateCoinbase();
// Witness commitment.
if (this.witness)
this.updateCommitment();
// Create our merkle root.
this.updateMerkle();
};
/**
* Update the commitment output for segwit.
*/
MinerBlock.prototype.updateCommitment = function updateCommitment() {
var output = this.coinbase.outputs[1];
var hash;
// Recalculate witness merkle root.
hash = this.block.createCommitmentHash();
// Update commitment.
output.script.clear();
output.script.fromCommitment(hash);
};
/**
* Update the extra nonce and coinbase reward.
*/
MinerBlock.prototype.updateCoinbase = function updateCoinbase() {
var input = this.coinbase.inputs[0];
var output = this.coinbase.outputs[0];
// Update extra nonce.
input.script.set(3, this.extraNonce());
input.script.compile();
// Update reward.
output.value = this.reward + this.fees;
};
/**
* Increment the extraNonce.
*/
MinerBlock.prototype.updateNonce = function updateNonce() {
// Overflow the nonce and increment the extraNonce.
this.block.nonce = 0;
this.nonce1++;
// Wrap at 4 bytes.
if (this.nonce1 === 0xffffffff) {
this.nonce1 = 0;
this.nonce2++;
}
// We incremented the extraNonce, need to update coinbase.
this.updateCoinbase();
// We changed the coinbase, need to update merkleRoot.
this.updateMerkle();
};
/**
* Rebuild the merkle tree and update merkle root.
*/
MinerBlock.prototype.updateMerkle = function updateMerkle() {
// Recalculate merkle root.
this.block.merkleRoot = this.block.createMerkleRoot('hex');
};
/**
* Render extraNonce.
* @returns {Buffer}
*/
MinerBlock.prototype.extraNonce = function extraNonce() {
var bw = new StaticWriter(8);
bw.writeU32BE(this.nonce1);
bw.writeU32BE(this.nonce2);
return bw.render();
};
/**
* Set the reward output address.
* @param {Address} address
*/
MinerBlock.prototype.setAddress = function setAddress(address) {
var output = this.coinbase.outputs[0];
this.address = Address(address);
output.script.clear();
output.script.fromAddress(this.address);
// Update commitments.
this.refresh();
};
/**
* Add a transaction to the block. Rebuilds the merkle tree,
* updates coinbase and commitment.
* @param {TX} tx
* @returns {Boolean} Whether the transaction was successfully added.
*/
MinerBlock.prototype.addTX = function addTX(tx, view) {
var hash = tx.hash('hex');
var item, weight, sigops;
assert(!tx.mutable, 'Cannot add mutable TX to block.');
if (this.block.hasTX(hash))
return false;
item = BlockEntry.fromTX(tx, view, this);
weight = item.tx.getWeight();
sigops = item.sigops;
if (!tx.isFinal(this.height, this.locktime))
return false;
if (this.weight + weight > consensus.MAX_BLOCK_WEIGHT)
return false;
if (this.sigops + sigops > consensus.MAX_BLOCK_SIGOPS_COST)
return false;
if (!this.witness && tx.hasWitness())
return false;
this.weight += weight;
this.sigops += sigops;
this.fees += item.fee;
// Add the tx to our block
this.block.txs.push(tx);
this.items.push(item);
// Update commitments.
this.refresh();
return true;
};
/**
* Hash until the nonce overflows.
* @returns {Boolean} Whether the nonce was found.
*/
MinerBlock.prototype.findNonce = function findNonce() {
var block = this.block;
var target = this.target;
var data = block.abbr();
var interval = MinerBlock.INTERVAL;
var min = 0;
var max = interval;
var nonce;
while (max <= 0xffffffff) {
nonce = mine(data, target, min, max);
if (nonce !== -1)
break;
block.nonce = max;
min += interval;
max += interval;
this.sendStatus();
}
return nonce;
};
/**
* Hash until the nonce overflows.
* @method
* @returns {Promise} - Returns Boolean.
*/
MinerBlock.prototype.findNonceAsync = co(function* findNonceAsync() {
var block = this.block;
var target = this.target;
var interval = MinerBlock.INTERVAL;
var min = 0;
var max = interval;
var data, nonce;
while (max <= 0xffffffff) {
data = block.abbr();
nonce = yield workerPool.mine(data, target, min, max);
if (nonce !== -1)
break;
if (this.destroyed)
return nonce;
block.nonce = max;
min += interval;
max += interval;
this.sendStatus();
}
return nonce;
});
/**
* Mine synchronously until the block is found.
* @returns {Block}
*/
MinerBlock.prototype.mine = function mine() {
var nonce;
// Track how long we've been at it.
this.begin = util.now();
for (;;) {
nonce = this.findNonce();
if (nonce !== -1)
break;
this.iterate();
}
this.commit(nonce);
return this.block;
};
/**
* Mine asynchronously until the block is found.
* @method
* @returns {Promise} - Returns {@link Block}.
*/
MinerBlock.prototype.mineAsync = co(function* mineAsync() {
var nonce;
// Track how long we've been at it.
this.begin = util.now();
for (;;) {
nonce = yield this.findNonceAsync();
if (nonce !== -1)
break;
if (this.destroyed)
return;
this.iterate();
}
this.commit(nonce);
return this.block;
});
/**
* Increment extraNonce, rebuild merkletree.
*/
MinerBlock.prototype.iterate = function iterate() {
// Keep track of our iterations.
this.iterations++;
// Send progress report.
this.sendStatus();
// Overflow the nonce and increment the extraNonce.
this.updateNonce();
};
/**
* Commit and finalize mined block.
* @returns {Block}
*/
MinerBlock.prototype.commit = function commit(nonce) {
assert(!this.committed, 'Block is already committed.');
this.committed = true;
this.block.nonce = nonce;
this.block.mutable = false;
this.coinbase.mutable = false;
return this.block;
};
/**
* Snapshot the nonces.
* @returns {Nonces}
*/
MinerBlock.prototype.snapshot = function snapshot() {
return new Nonces(this.nonce1, this.nonce2);
};
/**
* Send a progress report (emits `status`).
*/
MinerBlock.prototype.sendStatus = function sendStatus() {
this.emit('status', {
block: this.block,
target: this.block.bits,
hashes: this.getHashes(),
hashrate: this.getRate(),
height: this.height,
best: util.revHex(this.tip.hash)
});
};
/**
* Destroy the minerblock. Stop mining.
*/
MinerBlock.prototype.destroy = function destroy() {
this.destroyed = true;
};
/**
* BlockEntry
* @alias module:mining.BlockEntry
* @constructor
* @param {TX} tx
* @property {TX} tx
* @property {Hash} hash
* @property {Amount} fee
* @property {Rate} rate
* @property {Number} priority
* @property {Boolean} free
* @property {Sigops} sigops
* @property {Number} depCount
*/
function BlockEntry(tx) {
this.tx = tx;
this.hash = tx.hash('hex');
this.fee = 0;
this.rate = 0;
this.priority = 0;
this.free = false;
this.sigops = 0;
this.descRate = 0;
this.depCount = 0;
}
/**
* Instantiate block entry from transaction.
* @param {TX} tx
* @param {CoinView} view
* @param {MinerBlock} attempt
* @returns {BlockEntry}
*/
BlockEntry.fromTX = function fromTX(tx, view, attempt) {
var item = new BlockEntry(tx);
item.fee = tx.getFee(view);
item.rate = tx.getRate(view);
item.priority = tx.getPriority(view, attempt.height);
item.free = false;
item.sigops = tx.getSigopsCost(view, attempt.flags);
item.descRate = item.rate;
return item;
};
/**
* Instantiate block entry from mempool entry.
* @param {MempoolEntry} entry
* @param {MinerBlock} attempt
* @returns {BlockEntry}
*/
BlockEntry.fromEntry = function fromEntry(entry, attempt) {
var item = new BlockEntry(entry.tx);
item.fee = entry.getFee();
item.rate = entry.getRate();
item.priority = entry.getPriority(attempt.height);
item.free = item.fee < policy.getMinFee(entry.size);
item.sigops = entry.sigops;
item.descRate = entry.getDescRate();
return item;
};
/**
* Nonces
* @constructor
* @ignore
*/
function Nonces(nonce1, nonce2) {
this.nonce1 = nonce1;
this.nonce2 = nonce2;
}
/**
* Inject nonces into miner block.
* @param {MinerBlock} attempt
* @param {Number} nonce
* @param {Number} ts
*/
Nonces.prototype.inject = function inject(attempt, nonce, ts) {
attempt.block.ts = ts;
attempt.block.nonce = nonce;
attempt.nonce1 = this.nonce1;
attempt.nonce2 = this.nonce2;
attempt.updateCoinbase();
attempt.updateMerkle();
};
/**
* Attempt to verify POW.
* @param {MinerBlock} attempt
* @param {Number} nonce
* @param {Number} ts
*/
Nonces.prototype.verify = function verify(attempt, nonce, ts) {
var block = attempt.block;
var bts = block.ts;
var bnonce = block.nonce;
var nonce1 = attempt.nonce1;
var nonce2 = attempt.nonce2;
var result;
this.inject(attempt, nonce, ts);
if (consensus.verifyPOW(block.hash(), block.bits))
return true;
this.inject(attempt, bnonce, bts);
return false;
};
/*
* Expose
*/
exports = MinerBlock;
exports.MinerBlock = MinerBlock;
exports.BlockEntry = BlockEntry;
module.exports = exports;

621
lib/mining/template.js Normal file
View File

@ -0,0 +1,621 @@
/*!
* template.js - block template object for bcoin
* Copyright (c) 2014-2015, Fedor Indutny (MIT License)
* Copyright (c) 2014-2017, Christopher Jeffrey (MIT License).
* https://github.com/bcoin-org/bcoin
*/
'use strict';
var assert = require('assert');
var BN = require('bn.js');
var util = require('../utils/util');
var crypto = require('../crypto/crypto');
var StaticWriter = require('../utils/staticwriter');
var Address = require('../primitives/address');
var TX = require('../primitives/tx');
var Block = require('../primitives/block');
var Input = require('../primitives/input');
var Output = require('../primitives/output');
var consensus = require('../protocol/consensus');
var policy = require('../protocol/policy');
var encoding = require('../utils/encoding');
var CoinView = require('../coins/coinview');
var DUMMY = new Buffer(0);
/**
* Block Template
* @alias module:mining.BlockTemplate
* @constructor
* @param {Object} options
*/
function BlockTemplate(options) {
if (!(this instanceof BlockTemplate))
return new BlockTemplate(options);
this.prevBlock = options.prevBlock;
this.version = options.version;
this.height = options.height;
this.ts = options.ts;
this.bits = options.bits;
this.target = consensus.fromCompact(this.bits).toArrayLike(Buffer, 'le', 32);
this.locktime = options.locktime;
this.flags = options.flags;
this.coinbaseFlags = options.coinbaseFlags;
this.witness = options.witness;
this.address = options.address;
this.sigops = options.sigops;
this.weight = options.weight;
this.reward = consensus.getReward(this.height, options.halvingInterval);
this.tree = new MerkleTree();
this.left = DUMMY;
this.right = DUMMY;
this.fees = 0;
this.items = [];
}
/**
* Create witness commitment hash.
* @returns {Buffer}
*/
BlockTemplate.prototype.commitmentHash = function commitmentHash() {
var nonce = encoding.ZERO_HASH;
var leaves = [];
var i, item, root, data;
leaves.push(encoding.ZERO_HASH);
for (i = 0; i < this.items.length; i++) {
item = this.items[i];
leaves.push(item.tx.witnessHash());
}
root = crypto.createMerkleRoot(leaves);
assert(!root.malleated);
data = util.concat(root.hash, nonce);
return crypto.hash256(data);
};
/**
* Calculate the block reward.
* @returns {Amount}
*/
BlockTemplate.prototype.getReward = function getReward() {
return this.reward + this.fees;
};
/**
* Initialize the default coinbase.
* @returns {TX}
*/
BlockTemplate.prototype.createCoinbase = function createCoinbase() {
var scale = consensus.WITNESS_SCALE_FACTOR;
var cb = new TX();
var padding = 0;
var input, output, commit, hash;
// Coinbase input.
input = new Input();
// Height (required in v2+ blocks)
input.script.set(0, new BN(this.height));
// Let the world know this little
// miner succeeded.
input.script.set(1, encoding.ZERO_HASH160);
// Smaller nonce for good measure.
input.script.set(2, util.nonce().slice(0, 4));
// extraNonce - incremented when
// the nonce overflows.
input.script.set(3, extraNonce(0, 0));
input.script.compile();
// Set up the witness nonce.
if (this.witness) {
input.witness.set(0, encoding.ZERO_HASH);
input.witness.compile();
}
cb.inputs.push(input);
// Reward output.
output = new Output();
output.script.fromPubkeyhash(encoding.ZERO_HASH160);
output.value = this.getReward();
cb.outputs.push(output);
// If we're using segwit, we
// need to set up the commitment.
if (this.witness) {
// Commitment output.
commit = new Output();
hash = this.commitmentHash();
commit.script.fromCommitment(hash);
cb.outputs.push(commit);
}
// Padding for the CB height (constant size).
padding = 5 - input.script.code[0].getSize();
assert(padding >= 0);
// Reserved size.
// Without segwit:
// CB weight = 500
// CB stripped size = 125
// CB size = 125
// Sigops cost = 4
// With segwit:
// CB weight = 724
// CB stripped size = 172
// CB size = 208
// Sigops cost = 4
if (!this.witness) {
assert.equal(cb.getWeight() + padding * scale, 500);
assert.equal(cb.getBaseSize() + padding, 125);
assert.equal(cb.getSize() + padding, 125);
} else {
assert.equal(cb.getWeight() + padding * scale, 724);
assert.equal(cb.getBaseSize() + padding, 172);
assert.equal(cb.getSize() + padding, 208);
}
// Setup coinbase flags (variable size).
input.script.set(1, this.coinbaseFlags);
input.script.compile();
// Setup output script (variable size).
output.script.clear();
output.script.fromAddress(this.address);
cb.refresh();
return cb;
};
/**
* Refresh the coinbase and merkle tree.
*/
BlockTemplate.prototype.refresh = function refresh() {
var cb = this.createCoinbase();
var raw = cb.toNormal();
var size = 0;
var left, right;
size += 4; // version
size += 1; // varint inputs length
size += cb.inputs[0].getSize(); // input size
size -= 4 + 4 + 4; // -(nonce1 + nonce2 + sequence)
// Cut off right after the nonce
// push and before the sequence.
left = raw.slice(0, size);
// Include the sequence.
size += 4 + 4; // nonce1 + nonce2
right = raw.slice(size);
this.left = left;
this.right = right;
this.tree = MerkleTree.fromItems(this.items);
};
/**
* Get raw coinbase with desired nonces.
* @param {Number} nonce1
* @param {Number} nonce2
* @returns {Buffer}
*/
BlockTemplate.prototype.getCoinbase = function getCoinbase(nonce1, nonce2) {
var size = 0;
var bw;
size += this.left.length;
size += 4 + 4;
size += this.right.length;
bw = new StaticWriter(size);
bw.writeBytes(this.left);
bw.writeU32BE(nonce1);
bw.writeU32BE(nonce2);
bw.writeBytes(this.right);
return bw.render();
};
/**
* Calculate the merkle root with given nonces.
* @param {Number} nonce1
* @param {Number} nonce2
* @returns {Buffer}
*/
BlockTemplate.prototype.getRoot = function getRoot(nonce1, nonce2) {
var raw = this.getCoinbase(nonce1, nonce2);
var hash = crypto.hash256(raw);
return this.tree.withFirst(hash);
};
/**
* Create raw block header with given parameters.
* @param {Number} nonce1
* @param {Number} nonce2
* @param {Number} ts
* @param {Number} nonce
* @returns {Buffer}
*/
BlockTemplate.prototype.getHeader = function getHeader(nonce1, nonce2, ts, nonce) {
var bw = new StaticWriter(80);
var root = this.getRoot(nonce1, nonce2);
bw.writeU32(this.version);
bw.writeHash(this.prevBlock);
bw.writeHash(root);
bw.writeU32(ts);
bw.writeU32(this.bits);
bw.writeU32(nonce);
return bw.render();
};
/**
* Calculate block hash with given parameters.
* @param {Number} nonce1
* @param {Number} nonce2
* @param {Number} ts
* @param {Number} nonce
* @returns {Buffer}
*/
BlockTemplate.prototype.hash = function hash(nonce1, nonce2, ts, nonce) {
var data = this.getHeader(nonce1, nonce2, ts, nonce);
return crypto.hash256(data);
};
/**
* Create coinbase from given parameters.
* @param {Number} nonce1
* @param {Number} nonce2
* @returns {TX}
*/
BlockTemplate.prototype.coinbase = function coinbase(nonce1, nonce2) {
var raw = this.getCoinbase(nonce1, nonce2);
var tx = TX.fromRaw(raw);
var input;
if (this.witness) {
input = tx.inputs[0];
input.witness.push(encoding.ZERO_HASH);
input.witness.compile();
tx.refresh();
}
return tx;
};
/**
* Create block from given parameters.
* @param {Number} nonce1
* @param {Number} nonce2
* @param {Number} ts
* @param {Number} nonce
* @returns {Block}
*/
BlockTemplate.prototype.commit = function commit(nonce1, nonce2, ts, nonce) {
var tx = this.coinbase(nonce1, nonce2);
var root = this.tree.withFirst(tx.hash());
var block = new Block();
var i, item;
block.version = this.version;
block.prevBlock = this.prevBlock;
block.merkleRoot = root.toString('hex');
block.ts = ts;
block.bits = this.bits;
block.nonce = nonce;
block.txs.push(tx);
for (i = 0; i < this.items.length; i++) {
item = this.items[i];
block.txs.push(item.tx);
}
return block;
};
/**
* Quick and dirty way to
* get a coinbase tx object.
* @returns {TX}
*/
BlockTemplate.prototype.toCoinbase = function toCoinbase() {
return this.coinbase(0, 0);
};
/**
* Quick and dirty way to get a block
* object (most likely to be an invalid one).
* @returns {Block}
*/
BlockTemplate.prototype.toBlock = function toBlock() {
return this.commit(0, 0, this.ts, 0);
};
/**
* Set the reward output
* address and refresh.
* @param {Address} address
*/
BlockTemplate.prototype.setAddress = function setAddress(address) {
this.address = Address(address);
this.refresh();
};
/**
* Add a transaction to the template.
* @param {TX} tx
* @param {CoinView} view
*/
BlockTemplate.prototype.addTX = function addTX(tx, view) {
var item, weight, sigops;
assert(!tx.mutable, 'Cannot add mutable TX to block.');
item = BlockEntry.fromTX(tx, view, this);
weight = item.tx.getWeight();
sigops = item.sigops;
if (!tx.isFinal(this.height, this.locktime))
return false;
if (this.weight + weight > consensus.MAX_BLOCK_WEIGHT)
return false;
if (this.sigops + sigops > consensus.MAX_BLOCK_SIGOPS_COST)
return false;
if (!this.witness && tx.hasWitness())
return false;
this.weight += weight;
this.sigops += sigops;
this.fees += item.fee;
// Add the tx to our block
this.items.push(item);
return true;
};
/**
* Add a transaction to the template
* (less verification than addTX).
* @param {TX} tx
* @param {CoinView?} view
*/
BlockTemplate.prototype.pushTX = function pushTX(tx, view) {
var item, weight, sigops;
assert(!tx.mutable, 'Cannot add mutable TX to block.');
if (!view)
view = new CoinView();
item = BlockEntry.fromTX(tx, view, this);
weight = item.tx.getWeight();
sigops = item.sigops;
this.weight += weight;
this.sigops += sigops;
this.fees += item.fee;
// Add the tx to our block
this.items.push(item);
return true;
};
/**
* BlockEntry
* @alias module:mining.BlockEntry
* @constructor
* @param {TX} tx
* @property {TX} tx
* @property {Hash} hash
* @property {Amount} fee
* @property {Rate} rate
* @property {Number} priority
* @property {Boolean} free
* @property {Sigops} sigops
* @property {Number} depCount
*/
function BlockEntry(tx) {
this.tx = tx;
this.hash = tx.hash('hex');
this.fee = 0;
this.rate = 0;
this.priority = 0;
this.free = false;
this.sigops = 0;
this.descRate = 0;
this.depCount = 0;
}
/**
* Instantiate block entry from transaction.
* @param {TX} tx
* @param {CoinView} view
* @param {BlockTemplate} attempt
* @returns {BlockEntry}
*/
BlockEntry.fromTX = function fromTX(tx, view, attempt) {
var item = new BlockEntry(tx);
item.fee = tx.getFee(view);
item.rate = tx.getRate(view);
item.priority = tx.getPriority(view, attempt.height);
item.free = false;
item.sigops = tx.getSigopsCost(view, attempt.flags);
item.descRate = item.rate;
return item;
};
/**
* Instantiate block entry from mempool entry.
* @param {MempoolEntry} entry
* @param {BlockTemplate} attempt
* @returns {BlockEntry}
*/
BlockEntry.fromEntry = function fromEntry(entry, attempt) {
var item = new BlockEntry(entry.tx);
item.fee = entry.getFee();
item.rate = entry.getRate();
item.priority = entry.getPriority(attempt.height);
item.free = item.fee < policy.getMinFee(entry.size);
item.sigops = entry.sigops;
item.descRate = entry.getDescRate();
return item;
};
/*
* MerkleTree
* @constructor
* @property {Hash[]} steps
*/
function MerkleTree() {
this.steps = [];
}
MerkleTree.prototype.withFirst = function withFirst(hash) {
var i, step, data;
for (i = 0; i < this.steps.length; i++) {
step = this.steps[i];
data = util.concat(hash, step);
hash = crypto.hash256(data);
}
return hash;
};
MerkleTree.prototype.toJSON = function toJSON() {
var steps = [];
var i, step;
for (i = 0; i < this.steps.length; i++) {
step = this.steps[i];
steps.push(step.toString('hex'));
}
return steps;
};
MerkleTree.prototype.fromItems = function fromItems(items) {
var leaves = [];
var i, item;
leaves.push(encoding.ZERO_HASH);
for (i = 0; i < items.length; i++) {
item = items[i];
leaves.push(item.tx.hash());
}
return this.fromLeaves(leaves);
};
MerkleTree.fromItems = function fromItems(items) {
return new MerkleTree().fromItems(items);
};
MerkleTree.prototype.fromBlock = function fromBlock(txs) {
var leaves = [];
var i, tx;
leaves.push(encoding.ZERO_HASH);
for (i = 1; i < txs.length; i++) {
tx = txs[i];
leaves.push(tx.hash());
}
return this.fromLeaves(leaves);
};
MerkleTree.fromBlock = function fromBlock(txs) {
return new MerkleTree().fromBlock(txs);
};
MerkleTree.prototype.fromLeaves = function fromLeaves(leaves) {
var len = leaves.length;
var i, hashes, data, hash;
while (len > 1) {
this.steps.push(leaves[1]);
if (len % 2)
leaves.push(leaves[len - 1]);
hashes = [null];
for (i = 2; i < len; i += 2) {
data = util.concat(leaves[i], leaves[i + 1]);
hash = crypto.hash256(data);
hashes.push(hash);
}
leaves = hashes;
len = leaves.length;
}
return this;
};
MerkleTree.fromLeaves = function fromLeaves(leaves) {
return new MerkleTree().fromLeaves(leaves);
};
/*
* Helpers
*/
function extraNonce(nonce1, nonce2) {
var bw = new StaticWriter(8);
bw.writeU32BE(nonce1);
bw.writeU32BE(nonce2);
return bw.render();
};
/*
* Expose
*/
exports = BlockTemplate;
exports.BlockTemplate = BlockTemplate;
exports.BlockEntry = BlockEntry;
module.exports = exports;

View File

@ -12,6 +12,7 @@ exports.Bloom = require('./bloom');
exports.RollingFilter = exports.Bloom.Rolling; exports.RollingFilter = exports.Bloom.Rolling;
exports.co = require('./co'); exports.co = require('./co');
exports.encoding = require('./encoding'); exports.encoding = require('./encoding');
exports.fs = require('./fs');
exports.Heap = require('./heap'); exports.Heap = require('./heap');
exports.IP = require('./ip'); exports.IP = require('./ip');
exports.lazy = require('./lazy'); exports.lazy = require('./lazy');

View File

@ -15,7 +15,6 @@ var Amount = require('../btc/amount');
var Script = require('../script/script'); var Script = require('../script/script');
var Address = require('../primitives/address'); var Address = require('../primitives/address');
var KeyRing = require('../primitives/keyring'); var KeyRing = require('../primitives/keyring');
var Lock = require('../utils/lock');
var MerkleBlock = require('../primitives/merkleblock'); var MerkleBlock = require('../primitives/merkleblock');
var MTX = require('../primitives/mtx'); var MTX = require('../primitives/mtx');
var Outpoint = require('../primitives/outpoint'); var Outpoint = require('../primitives/outpoint');

View File

@ -14,6 +14,7 @@ var MemWallet = require('./util/memwallet');
var Network = require('../lib/protocol/network'); var Network = require('../lib/protocol/network');
var Output = require('../lib/primitives/output'); var Output = require('../lib/primitives/output');
var util = require('../lib/utils/util'); var util = require('../lib/utils/util');
var common = require('../lib/blockchain/common');
var opcodes = Script.opcodes; var opcodes = Script.opcodes;
describe('Chain', function() { describe('Chain', function() {
@ -22,16 +23,16 @@ describe('Chain', function() {
var miner = new Miner({ chain: chain, version: 4 }); var miner = new Miner({ chain: chain, version: 4 });
var wallet = new MemWallet({ network: network }); var wallet = new MemWallet({ network: network });
var wwallet = new MemWallet({ network: network, witness: true }); var wwallet = new MemWallet({ network: network, witness: true });
var tip1, tip2, addBlock, mineCSV; var cpu = miner.cpu;
var tip1, tip2, addBlock, mineBlock, mineCSV;
this.timeout(45000); this.timeout(45000);
addBlock = co(function* addBlock(attempt) { addBlock = co(function* addBlock(block, flags) {
var block = yield attempt.mineAsync();
var entry; var entry;
try { try {
entry = yield chain.add(block); entry = yield chain.add(block, flags);
} catch (e) { } catch (e) {
assert(e.type === 'VerifyError'); assert(e.type === 'VerifyError');
return e.reason; return e.reason;
@ -43,8 +44,13 @@ describe('Chain', function() {
return 'OK'; return 'OK';
}); });
mineBlock = co(function* mineBlock(job, flags) {
var block = yield job.mineAsync();
return yield addBlock(block, flags);
});
mineCSV = co(function* mineCSV(tx) { mineCSV = co(function* mineCSV(tx) {
var attempt = yield miner.createBlock(); var job = yield cpu.createJob();
var rtx; var rtx;
rtx = new MTX(); rtx = new MTX();
@ -63,9 +69,10 @@ describe('Chain', function() {
wallet.sign(rtx); wallet.sign(rtx);
attempt.addTX(rtx.toTX(), rtx.view); job.addTX(rtx.toTX(), rtx.view);
job.refresh();
return yield attempt.mineAsync(); return yield job.mineAsync();
}); });
chain.on('connect', function(entry, block) { chain.on('connect', function(entry, block) {
@ -90,7 +97,7 @@ describe('Chain', function() {
var i, block; var i, block;
for (i = 0; i < 200; i++) { for (i = 0; i < 200; i++) {
block = yield miner.mineBlock(); block = yield cpu.mineBlock();
assert(block); assert(block);
assert(yield chain.add(block)); assert(yield chain.add(block));
} }
@ -99,11 +106,11 @@ describe('Chain', function() {
})); }));
it('should mine competing chains', co(function* () { it('should mine competing chains', co(function* () {
var i, mtx, at1, at2, blk1, blk2, hash1, hash2; var i, mtx, job1, job2, blk1, blk2, hash1, hash2;
for (i = 0; i < 10; i++) { for (i = 0; i < 10; i++) {
at1 = yield miner.createBlock(tip1); job1 = yield cpu.createJob(tip1);
at2 = yield miner.createBlock(tip2); job2 = yield cpu.createJob(tip2);
mtx = yield wallet.create({ mtx = yield wallet.create({
outputs: [{ outputs: [{
@ -112,11 +119,14 @@ describe('Chain', function() {
}] }]
}); });
at1.addTX(mtx.toTX(), mtx.view); job1.addTX(mtx.toTX(), mtx.view);
at2.addTX(mtx.toTX(), mtx.view); job2.addTX(mtx.toTX(), mtx.view);
blk1 = yield at1.mineAsync(); job1.refresh();
blk2 = yield at2.mineAsync(); job2.refresh();
blk1 = yield job1.mineAsync();
blk2 = yield job2.mineAsync();
hash1 = blk1.hash('hex'); hash1 = blk1.hash('hex');
hash2 = blk2.hash('hex'); hash2 = blk2.hash('hex');
@ -156,7 +166,7 @@ describe('Chain', function() {
assert(entry); assert(entry);
assert(chain.height === entry.height); assert(chain.height === entry.height);
block = yield miner.mineBlock(entry); block = yield cpu.mineBlock(entry);
assert(block); assert(block);
chain.once('reorganize', function() { chain.once('reorganize', function() {
@ -186,7 +196,7 @@ describe('Chain', function() {
})); }));
it('should mine a block after a reorg', co(function* () { it('should mine a block after a reorg', co(function* () {
var block = yield miner.mineBlock(); var block = yield cpu.mineBlock();
var hash, entry, result; var hash, entry, result;
assert(yield chain.add(block)); assert(yield chain.add(block));
@ -202,7 +212,7 @@ describe('Chain', function() {
})); }));
it('should prevent double spend on new chain', co(function* () { it('should prevent double spend on new chain', co(function* () {
var attempt = yield miner.createBlock(); var job = yield cpu.createJob();
var mtx, block; var mtx, block;
mtx = yield wallet.create({ mtx = yield wallet.create({
@ -212,37 +222,40 @@ describe('Chain', function() {
}] }]
}); });
attempt.addTX(mtx.toTX(), mtx.view); job.addTX(mtx.toTX(), mtx.view);
job.refresh();
block = yield attempt.mineAsync(); block = yield job.mineAsync();
assert(yield chain.add(block)); assert(yield chain.add(block));
attempt = yield miner.createBlock(); job = yield cpu.createJob();
assert(mtx.outputs.length > 1); assert(mtx.outputs.length > 1);
mtx.outputs.pop(); mtx.outputs.pop();
attempt.addTX(mtx.toTX(), mtx.view); job.addTX(mtx.toTX(), mtx.view);
job.refresh();
assert.equal(yield addBlock(attempt), 'bad-txns-inputs-missingorspent'); assert.equal(yield mineBlock(job), 'bad-txns-inputs-missingorspent');
})); }));
it('should fail to connect coins on an alternate chain', co(function* () { it('should fail to connect coins on an alternate chain', co(function* () {
var block = yield chain.db.getBlock(tip1.hash); var block = yield chain.db.getBlock(tip1.hash);
var cb = block.txs[0]; var cb = block.txs[0];
var mtx = new MTX(); var mtx = new MTX();
var attempt; var job;
mtx.addTX(cb, 0); mtx.addTX(cb, 0);
mtx.addOutput(wallet.getAddress(), 10 * 1e8); mtx.addOutput(wallet.getAddress(), 10 * 1e8);
wallet.sign(mtx); wallet.sign(mtx);
attempt = yield miner.createBlock(); job = yield cpu.createJob();
attempt.addTX(mtx.toTX(), mtx.view); job.addTX(mtx.toTX(), mtx.view);
job.refresh();
assert.equal(yield addBlock(attempt), 'bad-txns-inputs-missingorspent'); assert.equal(yield mineBlock(job), 'bad-txns-inputs-missingorspent');
})); }));
it('should have correct chain value', function() { it('should have correct chain value', function() {
@ -252,7 +265,7 @@ describe('Chain', function() {
}); });
it('should get coin', co(function* () { it('should get coin', co(function* () {
var mtx, attempt, block, tx, output, coin; var mtx, job, block, tx, output, coin;
mtx = yield wallet.send({ mtx = yield wallet.send({
outputs: [ outputs: [
@ -271,10 +284,11 @@ describe('Chain', function() {
] ]
}); });
attempt = yield miner.createBlock(); job = yield cpu.createJob();
attempt.addTX(mtx.toTX(), mtx.view); job.addTX(mtx.toTX(), mtx.view);
job.refresh();
block = yield attempt.mineAsync(); block = yield job.mineAsync();
assert(yield chain.add(block)); assert(yield chain.add(block));
tx = block.txs[1]; tx = block.txs[1];
@ -330,7 +344,7 @@ describe('Chain', function() {
assert.equal(state, 1); assert.equal(state, 1);
for (i = 0; i < 417; i++) { for (i = 0; i < 417; i++) {
block = yield miner.mineBlock(); block = yield cpu.mineBlock();
assert(yield chain.add(block)); assert(yield chain.add(block));
switch (chain.height) { switch (chain.height) {
case 288: case 288:
@ -371,7 +385,7 @@ describe('Chain', function() {
it('should test csv', co(function* () { it('should test csv', co(function* () {
var tx = (yield chain.db.getBlock(chain.height - 100)).txs[0]; var tx = (yield chain.db.getBlock(chain.height - 100)).txs[0];
var block = yield mineCSV(tx); var block = yield mineCSV(tx);
var csv, attempt, rtx; var csv, job, rtx;
assert(yield chain.add(block)); assert(yield chain.add(block));
@ -390,11 +404,12 @@ describe('Chain', function() {
rtx.addTX(csv, 0); rtx.addTX(csv, 0);
rtx.setSequence(0, 1, false); rtx.setSequence(0, 1, false);
attempt = yield miner.createBlock(); job = yield cpu.createJob();
attempt.addTX(rtx.toTX(), rtx.view); job.addTX(rtx.toTX(), rtx.view);
job.refresh();
block = yield attempt.mineAsync(); block = yield job.mineAsync();
assert(yield chain.add(block)); assert(yield chain.add(block));
})); }));
@ -402,7 +417,7 @@ describe('Chain', function() {
it('should fail csv with bad sequence', co(function* () { it('should fail csv with bad sequence', co(function* () {
var csv = (yield chain.db.getBlock(chain.height - 100)).txs[0]; var csv = (yield chain.db.getBlock(chain.height - 100)).txs[0];
var rtx = new MTX(); var rtx = new MTX();
var attempt; var job;
rtx.addOutput({ rtx.addOutput({
script: [ script: [
@ -415,14 +430,15 @@ describe('Chain', function() {
rtx.addTX(csv, 0); rtx.addTX(csv, 0);
rtx.setSequence(0, 1, false); rtx.setSequence(0, 1, false);
attempt = yield miner.createBlock(); job = yield cpu.createJob();
attempt.addTX(rtx.toTX(), rtx.view); job.addTX(rtx.toTX(), rtx.view);
job.refresh();
assert.equal(yield addBlock(attempt), 'mandatory-script-verify-flag-failed'); assert.equal(yield mineBlock(job), 'mandatory-script-verify-flag-failed');
})); }));
it('should mine a block', co(function* () { it('should mine a block', co(function* () {
var block = yield miner.mineBlock(); var block = yield cpu.mineBlock();
assert(block); assert(block);
assert(yield chain.add(block)); assert(yield chain.add(block));
})); }));
@ -430,7 +446,7 @@ describe('Chain', function() {
it('should fail csv lock checks', co(function* () { it('should fail csv lock checks', co(function* () {
var tx = (yield chain.db.getBlock(chain.height - 100)).txs[0]; var tx = (yield chain.db.getBlock(chain.height - 100)).txs[0];
var block = yield mineCSV(tx); var block = yield mineCSV(tx);
var csv, attempt, rtx; var csv, job, rtx;
assert(yield chain.add(block)); assert(yield chain.add(block));
@ -449,10 +465,11 @@ describe('Chain', function() {
rtx.addTX(csv, 0); rtx.addTX(csv, 0);
rtx.setSequence(0, 2, false); rtx.setSequence(0, 2, false);
attempt = yield miner.createBlock(); job = yield cpu.createJob();
attempt.addTX(rtx.toTX(), rtx.view); job.addTX(rtx.toTX(), rtx.view);
job.refresh();
assert.equal(yield addBlock(attempt), 'bad-txns-nonfinal'); assert.equal(yield mineBlock(job), 'bad-txns-nonfinal');
})); }));
it('should have correct wallet balance', co(function* () { it('should have correct wallet balance', co(function* () {
@ -460,72 +477,72 @@ describe('Chain', function() {
})); }));
it('should fail to connect bad bits', co(function* () { it('should fail to connect bad bits', co(function* () {
var attempt = yield miner.createBlock(); var job = yield cpu.createJob();
attempt.block.bits = 553713663; job.attempt.bits = 553713663;
assert.equal(yield addBlock(attempt), 'bad-diffbits'); assert.equal(yield mineBlock(job), 'bad-diffbits');
})); }));
it('should fail to connect bad MTP', co(function* () { it('should fail to connect bad MTP', co(function* () {
var mtp = yield chain.tip.getMedianTime(); var mtp = yield chain.tip.getMedianTime();
var attempt = yield miner.createBlock(); var job = yield cpu.createJob();
attempt.block.ts = mtp - 1; job.attempt.ts = mtp - 1;
assert.equal(yield addBlock(attempt), 'time-too-old'); assert.equal(yield mineBlock(job), 'time-too-old');
})); }));
it('should fail to connect bad time', co(function* () { it('should fail to connect bad time', co(function* () {
var attempt = yield miner.createBlock(); var job = yield cpu.createJob();
var now = network.now() + 3 * 60 * 60; var now = network.now() + 3 * 60 * 60;
attempt.block.ts = now; job.attempt.ts = now;
assert.equal(yield addBlock(attempt), 'time-too-new'); assert.equal(yield mineBlock(job), 'time-too-new');
})); }));
it('should fail to connect bad locktime', co(function* () { it('should fail to connect bad locktime', co(function* () {
var attempt = yield miner.createBlock(); var job = yield cpu.createJob();
var tx = yield wallet.send({ locktime: 100000 }); var tx = yield wallet.send({ locktime: 100000 });
attempt.block.txs.push(tx.toTX()); job.pushTX(tx.toTX());
attempt.refresh(); job.refresh();
assert.equal(yield addBlock(attempt), 'bad-txns-nonfinal'); assert.equal(yield mineBlock(job), 'bad-txns-nonfinal');
})); }));
it('should fail to connect bad cb height', co(function* () { it('should fail to connect bad cb height', co(function* () {
var bip34height = network.block.bip34height; var bip34height = network.block.bip34height;
var attempt = yield miner.createBlock(); var job = yield cpu.createJob();
var tx = attempt.block.txs[0];
var input = tx.inputs[0];
input.script.set(0, new BN(10)); job.attempt.height = 10;
input.script.compile(); job.attempt.refresh();
attempt.refresh();
try { try {
network.block.bip34height = 0; network.block.bip34height = 0;
assert.equal(yield addBlock(attempt), 'bad-cb-height'); assert.equal(yield mineBlock(job), 'bad-cb-height');
} finally { } finally {
network.block.bip34height = bip34height; network.block.bip34height = bip34height;
} }
})); }));
it('should fail to connect bad witness nonce size', co(function* () { it('should fail to connect bad witness nonce size', co(function* () {
var attempt = yield miner.createBlock(); var block = yield cpu.mineBlock();
var tx = attempt.block.txs[0]; var tx = block.txs[0];
var input = tx.inputs[0]; var input = tx.inputs[0];
input.witness.set(0, new Buffer(33)); input.witness.set(0, new Buffer(33));
input.witness.compile(); input.witness.compile();
assert.equal(yield addBlock(attempt), 'bad-witness-nonce-size'); block.refresh(true);
assert.equal(yield addBlock(block), 'bad-witness-nonce-size');
})); }));
it('should fail to connect bad witness nonce', co(function* () { it('should fail to connect bad witness nonce', co(function* () {
var attempt = yield miner.createBlock(); var block = yield cpu.mineBlock();
var tx = attempt.block.txs[0]; var tx = block.txs[0];
var input = tx.inputs[0]; var input = tx.inputs[0];
input.witness.set(0, encoding.ONE_HASH); input.witness.set(0, encoding.ONE_HASH);
input.witness.compile(); input.witness.compile();
assert.equal(yield addBlock(attempt), 'bad-witness-merkle-match'); block.refresh(true);
assert.equal(yield addBlock(block), 'bad-witness-merkle-match');
})); }));
it('should fail to connect bad witness commitment', co(function* () { it('should fail to connect bad witness commitment', co(function* () {
var attempt = yield miner.createBlock(); var flags = common.flags.DEFAULT_FLAGS & ~common.flags.VERIFY_POW;
var tx = attempt.block.txs[0]; var block = yield cpu.mineBlock();
var tx = block.txs[0];
var output = tx.outputs[1]; var output = tx.outputs[1];
var commit; var commit;
@ -536,18 +553,26 @@ describe('Chain', function() {
output.script.set(1, commit); output.script.set(1, commit);
output.script.compile(); output.script.compile();
attempt.updateMerkle(); block.refresh(true);
assert.equal(yield addBlock(attempt), 'bad-witness-merkle-match'); block.merkleRoot = block.createMerkleRoot('hex');
assert.equal(yield addBlock(block, flags), 'bad-witness-merkle-match');
})); }));
it('should fail to connect unexpected witness', co(function* () { it('should fail to connect unexpected witness', co(function* () {
var attempt = yield miner.createBlock(); var flags = common.flags.DEFAULT_FLAGS & ~common.flags.VERIFY_POW;
var tx = attempt.block.txs[0]; var block = yield cpu.mineBlock();
var tx = block.txs[0];
var output = tx.outputs[1]; var output = tx.outputs[1];
assert(output.script.isCommitment()); assert(output.script.isCommitment());
tx.outputs.pop(); tx.outputs.pop();
attempt.updateMerkle();
assert.equal(yield addBlock(attempt), 'unexpected-witness'); block.refresh(true);
block.merkleRoot = block.createMerkleRoot('hex');
assert.equal(yield addBlock(block, flags), 'unexpected-witness');
})); }));
it('should add wit addrs to miner', co(function* () { it('should add wit addrs to miner', co(function* () {
@ -560,7 +585,7 @@ describe('Chain', function() {
var i, block; var i, block;
for (i = 0; i < 2001; i++) { for (i = 0; i < 2001; i++) {
block = yield miner.mineBlock(); block = yield cpu.mineBlock();
assert(block); assert(block);
assert(yield chain.add(block)); assert(yield chain.add(block));
} }
@ -572,17 +597,18 @@ describe('Chain', function() {
var block = yield chain.db.getBlock(chain.height - 2000); var block = yield chain.db.getBlock(chain.height - 2000);
var cb = block.txs[0]; var cb = block.txs[0];
var mtx = new MTX(); var mtx = new MTX();
var attempt; var job;
mtx.addTX(cb, 0); mtx.addTX(cb, 0);
mtx.addOutput(wwallet.getAddress(), 1000); mtx.addOutput(wwallet.getAddress(), 1000);
wwallet.sign(mtx); wwallet.sign(mtx);
attempt = yield miner.createBlock(); job = yield cpu.createJob();
attempt.addTX(mtx.toTX(), mtx.view); job.addTX(mtx.toTX(), mtx.view);
job.refresh();
block = yield attempt.mineAsync(); block = yield job.mineAsync();
assert(yield chain.add(block)); assert(yield chain.add(block));
})); }));
@ -590,7 +616,7 @@ describe('Chain', function() {
it('should mine fail to connect too much weight', co(function* () { it('should mine fail to connect too much weight', co(function* () {
var start = chain.height - 2000; var start = chain.height - 2000;
var end = chain.height - 200; var end = chain.height - 200;
var attempt = yield miner.createBlock(); var job = yield cpu.createJob();
var mtx = new MTX(); var mtx = new MTX();
var i, j, block, cb; var i, j, block, cb;
@ -606,18 +632,18 @@ describe('Chain', function() {
wwallet.sign(mtx); wwallet.sign(mtx);
attempt.block.txs.push(mtx.toTX()); job.pushTX(mtx.toTX());
} }
attempt.refresh(); job.refresh();
assert.equal(yield addBlock(attempt), 'bad-blk-weight'); assert.equal(yield mineBlock(job), 'bad-blk-weight');
})); }));
it('should mine fail to connect too much size', co(function* () { it('should mine fail to connect too much size', co(function* () {
var start = chain.height - 2000; var start = chain.height - 2000;
var end = chain.height - 200; var end = chain.height - 200;
var attempt = yield miner.createBlock(); var job = yield cpu.createJob();
var mtx = new MTX(); var mtx = new MTX();
var i, j, block, cb; var i, j, block, cb;
@ -633,18 +659,18 @@ describe('Chain', function() {
wwallet.sign(mtx); wwallet.sign(mtx);
attempt.block.txs.push(mtx.toTX()); job.pushTX(mtx.toTX());
} }
attempt.refresh(); job.refresh();
assert.equal(yield addBlock(attempt), 'bad-blk-length'); assert.equal(yield mineBlock(job), 'bad-blk-length');
})); }));
it('should mine a big block', co(function* () { it('should mine a big block', co(function* () {
var start = chain.height - 2000; var start = chain.height - 2000;
var end = chain.height - 200; var end = chain.height - 200;
var attempt = yield miner.createBlock(); var job = yield cpu.createJob();
var mtx = new MTX(); var mtx = new MTX();
var i, j, block, cb; var i, j, block, cb;
@ -660,34 +686,34 @@ describe('Chain', function() {
wwallet.sign(mtx); wwallet.sign(mtx);
attempt.block.txs.push(mtx.toTX()); job.pushTX(mtx.toTX());
} }
attempt.refresh(); job.refresh();
assert.equal(yield addBlock(attempt), 'OK'); assert.equal(yield mineBlock(job), 'OK');
})); }));
it('should fail to connect bad versions', co(function* () { it('should fail to connect bad versions', co(function* () {
var i, attempt; var i, job;
for (i = 0; i <= 3; i++) { for (i = 0; i <= 3; i++) {
attempt = yield miner.createBlock(); job = yield cpu.createJob();
attempt.block.version = i; job.attempt.version = i;
assert.equal(yield addBlock(attempt), 'bad-version'); assert.equal(yield mineBlock(job), 'bad-version');
} }
})); }));
it('should fail to connect bad amount', co(function* () { it('should fail to connect bad amount', co(function* () {
var attempt = yield miner.createBlock(); var job = yield cpu.createJob();
attempt.block.txs[0].outputs[0].value += 1; job.attempt.reward += 1;
attempt.updateMerkle(); job.refresh();
assert.equal(yield addBlock(attempt), 'bad-cb-amount'); assert.equal(yield mineBlock(job), 'bad-cb-amount');
})); }));
it('should fail to connect premature cb spend', co(function* () { it('should fail to connect premature cb spend', co(function* () {
var attempt = yield miner.createBlock(); var job = yield cpu.createJob();
var block = yield chain.db.getBlock(chain.height - 98); var block = yield chain.db.getBlock(chain.height - 98);
var cb = block.txs[0]; var cb = block.txs[0];
var mtx = new MTX(); var mtx = new MTX();
@ -697,14 +723,15 @@ describe('Chain', function() {
wwallet.sign(mtx); wwallet.sign(mtx);
attempt.addTX(mtx.toTX(), mtx.view); job.addTX(mtx.toTX(), mtx.view);
job.refresh();
assert.equal(yield addBlock(attempt), assert.equal(yield mineBlock(job),
'bad-txns-premature-spend-of-coinbase'); 'bad-txns-premature-spend-of-coinbase');
})); }));
it('should fail to connect vout belowout', co(function* () { it('should fail to connect vout belowout', co(function* () {
var attempt = yield miner.createBlock(); var job = yield cpu.createJob();
var block = yield chain.db.getBlock(chain.height - 99); var block = yield chain.db.getBlock(chain.height - 99);
var cb = block.txs[0]; var cb = block.txs[0];
var mtx = new MTX(); var mtx = new MTX();
@ -714,15 +741,15 @@ describe('Chain', function() {
wwallet.sign(mtx); wwallet.sign(mtx);
attempt.block.txs.push(mtx.toTX()); job.pushTX(mtx.toTX());
attempt.refresh(); job.refresh();
assert.equal(yield addBlock(attempt), assert.equal(yield mineBlock(job),
'bad-txns-in-belowout'); 'bad-txns-in-belowout');
})); }));
it('should fail to connect outtotal toolarge', co(function* () { it('should fail to connect outtotal toolarge', co(function* () {
var attempt = yield miner.createBlock(); var job = yield cpu.createJob();
var block = yield chain.db.getBlock(chain.height - 99); var block = yield chain.db.getBlock(chain.height - 99);
var cb = block.txs[0]; var cb = block.txs[0];
var mtx = new MTX(); var mtx = new MTX();
@ -734,15 +761,16 @@ describe('Chain', function() {
wwallet.sign(mtx); wwallet.sign(mtx);
attempt.block.txs.push(mtx.toTX()); job.pushTX(mtx.toTX());
attempt.refresh(); job.refresh();
assert.equal(yield addBlock(attempt), assert.equal(yield mineBlock(job),
'bad-txns-txouttotal-toolarge'); 'bad-txns-txouttotal-toolarge');
})); }));
it('should mine 111 multisig blocks', co(function* () { it('should mine 111 multisig blocks', co(function* () {
var i, j, script, attempt, cb, output, val, block; var flags = common.flags.DEFAULT_FLAGS & ~common.flags.VERIFY_POW;
var i, j, script, job, cb, output, val, block;
script = new Script(); script = new Script();
script.push(new BN(20)); script.push(new BN(20));
@ -757,8 +785,8 @@ describe('Chain', function() {
script = Script.fromScripthash(script.hash160()); script = Script.fromScripthash(script.hash160());
for (i = 0; i < 111; i++) { for (i = 0; i < 111; i++) {
attempt = yield miner.createBlock(); block = yield cpu.mineBlock();
cb = attempt.block.txs[0]; cb = block.txs[0];
val = cb.outputs[0].value; val = cb.outputs[0].value;
cb.outputs[0].value = 0; cb.outputs[0].value = 0;
@ -771,10 +799,10 @@ describe('Chain', function() {
cb.outputs.push(output); cb.outputs.push(output);
} }
attempt.updateMerkle(); block.refresh(true);
block.merkleRoot = block.createMerkleRoot('hex');
block = yield attempt.mineAsync(); assert(yield chain.add(block, flags));
assert(yield chain.add(block));
} }
assert.equal(chain.height, 2749); assert.equal(chain.height, 2749);
@ -783,7 +811,7 @@ describe('Chain', function() {
it('should fail to connect too many sigops', co(function* () { it('should fail to connect too many sigops', co(function* () {
var start = chain.height - 110; var start = chain.height - 110;
var end = chain.height - 100; var end = chain.height - 100;
var attempt = yield miner.createBlock(); var job = yield cpu.createJob();
var i, j, mtx, script, block, cb; var i, j, mtx, script, block, cb;
script = new Script(); script = new Script();
@ -812,12 +840,12 @@ describe('Chain', function() {
mtx.addOutput(wwallet.getAddress(), 1); mtx.addOutput(wwallet.getAddress(), 1);
attempt.block.txs.push(mtx.toTX()); job.pushTX(mtx.toTX());
} }
attempt.refresh(); job.refresh();
assert.equal(yield addBlock(attempt), 'bad-blk-sigops'); assert.equal(yield mineBlock(job), 'bad-blk-sigops');
})); }));
it('should cleanup', co(function* () { it('should cleanup', co(function* () {

View File

@ -8,7 +8,6 @@ var Coin = require('../lib/primitives/coin');
var Script = require('../lib/script/script'); var Script = require('../lib/script/script');
var FullNode = require('../lib/node/fullnode'); var FullNode = require('../lib/node/fullnode');
var MTX = require('../lib/primitives/mtx'); var MTX = require('../lib/primitives/mtx');
// var Client = require('../lib/wallet/client');
describe('Node', function() { describe('Node', function() {
var node = new FullNode({ var node = new FullNode({
@ -23,18 +22,16 @@ describe('Node', function() {
var miner = node.miner; var miner = node.miner;
var wallet, tip1, tip2, cb1, cb2, mineBlock; var wallet, tip1, tip2, cb1, cb2, mineBlock;
// walletdb.client = new Client({ apiKey: 'foo', network: 'regtest' });
node.on('error', function() {}); node.on('error', function() {});
this.timeout(5000); this.timeout(5000);
mineBlock = co(function* mineBlock(tip, tx) { mineBlock = co(function* mineBlock(tip, tx) {
var attempt = yield miner.createBlock(tip); var job = yield miner.createJob(tip);
var rtx; var rtx;
if (!tx) if (!tx)
return yield attempt.mineAsync(); return yield job.mineAsync();
rtx = new MTX(); rtx = new MTX();
@ -47,9 +44,10 @@ describe('Node', function() {
yield wallet.sign(rtx); yield wallet.sign(rtx);
attempt.addTX(rtx.toTX(), rtx.view); job.addTX(rtx.toTX(), rtx.view);
job.refresh();
return yield attempt.mineAsync(); return yield job.mineAsync();
}); });
it('should open chain and miner', co(function* () { it('should open chain and miner', co(function* () {
@ -312,7 +310,7 @@ describe('Node', function() {
})); }));
var mineCSV = co(function* mineCSV(tx) { var mineCSV = co(function* mineCSV(tx) {
var attempt = yield miner.createBlock(); var job = yield miner.createJob();
var redeemer; var redeemer;
redeemer = new MTX(); redeemer = new MTX();
@ -331,15 +329,16 @@ describe('Node', function() {
yield wallet.sign(redeemer); yield wallet.sign(redeemer);
attempt.addTX(redeemer.toTX(), redeemer.view); job.addTX(redeemer.toTX(), redeemer.view);
job.refresh();
return yield attempt.mineAsync(); return yield job.mineAsync();
}); });
it('should test csv', co(function* () { it('should test csv', co(function* () {
var tx = (yield chain.db.getBlock(chain.height)).txs[0]; var tx = (yield chain.db.getBlock(chain.height)).txs[0];
var block = yield mineCSV(tx); var block = yield mineCSV(tx);
var csv, attempt, redeemer; var csv, job, redeemer;
yield chain.add(block); yield chain.add(block);
@ -358,18 +357,19 @@ describe('Node', function() {
redeemer.addTX(csv, 0); redeemer.addTX(csv, 0);
redeemer.setSequence(0, 1, false); redeemer.setSequence(0, 1, false);
attempt = yield miner.createBlock(); job = yield miner.createJob();
attempt.addTX(redeemer.toTX(), redeemer.view); job.addTX(redeemer.toTX(), redeemer.view);
job.refresh();
block = yield attempt.mineAsync(); block = yield job.mineAsync();
yield chain.add(block); yield chain.add(block);
})); }));
it('should fail csv with bad sequence', co(function* () { it('should fail csv with bad sequence', co(function* () {
var csv = (yield chain.db.getBlock(chain.height)).txs[1]; var csv = (yield chain.db.getBlock(chain.height)).txs[1];
var block, attempt, redeemer, err; var block, job, redeemer, err;
redeemer = new MTX(); redeemer = new MTX();
@ -384,11 +384,12 @@ describe('Node', function() {
redeemer.addTX(csv, 0); redeemer.addTX(csv, 0);
redeemer.setSequence(0, 1, false); redeemer.setSequence(0, 1, false);
attempt = yield miner.createBlock(); job = yield miner.createJob();
attempt.addTX(redeemer.toTX(), redeemer.view); job.addTX(redeemer.toTX(), redeemer.view);
job.refresh();
block = yield attempt.mineAsync(); block = yield job.mineAsync();
try { try {
yield chain.add(block); yield chain.add(block);
@ -409,7 +410,7 @@ describe('Node', function() {
it('should fail csv lock checks', co(function* () { it('should fail csv lock checks', co(function* () {
var tx = (yield chain.db.getBlock(chain.height)).txs[0]; var tx = (yield chain.db.getBlock(chain.height)).txs[0];
var block = yield mineCSV(tx); var block = yield mineCSV(tx);
var csv, attempt, redeemer, err; var csv, job, redeemer, err;
yield chain.add(block); yield chain.add(block);
@ -428,11 +429,12 @@ describe('Node', function() {
redeemer.addTX(csv, 0); redeemer.addTX(csv, 0);
redeemer.setSequence(0, 2, false); redeemer.setSequence(0, 2, false);
attempt = yield miner.createBlock(); job = yield miner.createJob();
attempt.addTX(redeemer.toTX(), redeemer.view); job.addTX(redeemer.toTX(), redeemer.view);
job.refresh();
block = yield attempt.mineAsync(); block = yield job.mineAsync();
try { try {
yield chain.add(block); yield chain.add(block);