refactor: mining.
This commit is contained in:
parent
50d47dd5a6
commit
945fa381c5
@ -17,4 +17,5 @@ exports.RPCClient = require('./rpcclient');
|
||||
exports.Wallet = require('./wallet');
|
||||
exports.Base = require('./base');
|
||||
exports.RPC = require('./rpc');
|
||||
exports.RPCBase = require('./rpcbase');
|
||||
exports.Server = require('./server');
|
||||
|
||||
114
lib/http/rpc.js
114
lib/http/rpc.js
@ -20,7 +20,6 @@ var Block = require('../primitives/block');
|
||||
var Headers = require('../primitives/headers');
|
||||
var Input = require('../primitives/input');
|
||||
var KeyRing = require('../primitives/keyring');
|
||||
var Lock = require('../utils/lock');
|
||||
var MerkleBlock = require('../primitives/merkleblock');
|
||||
var MTX = require('../primitives/mtx');
|
||||
var Network = require('../protocol/network');
|
||||
@ -64,7 +63,9 @@ function RPC(node) {
|
||||
this.attempt = null;
|
||||
this.lastActivity = 0;
|
||||
this.boundChain = false;
|
||||
this.nonces = {};
|
||||
this.nonce1 = 0;
|
||||
this.nonce2 = 0;
|
||||
this.jobs = {};
|
||||
this.pollers = [];
|
||||
|
||||
this.init();
|
||||
@ -920,38 +921,43 @@ RPC.prototype.submitWork = co(function* submitWork(data) {
|
||||
|
||||
RPC.prototype._submitWork = co(function* _submitWork(data) {
|
||||
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)
|
||||
return false;
|
||||
|
||||
block = attempt.block;
|
||||
header = Headers.fromAbbr(data);
|
||||
nonce = header.nonce;
|
||||
ts = header.ts;
|
||||
|
||||
if (data.length !== 128)
|
||||
throw new RPCError('Invalid work size.');
|
||||
|
||||
data = data.slice(0, 80);
|
||||
data = swap32(data);
|
||||
|
||||
if (header.prevBlock !== block.prevBlock
|
||||
|| header.bits !== block.bits) {
|
||||
if (header.prevBlock !== attempt.prevBlock
|
||||
|| header.bits !== attempt.bits) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!header.verify())
|
||||
return false;
|
||||
|
||||
nonces = this.nonces[header.merkleRoot];
|
||||
job = this.jobs[header.merkleRoot];
|
||||
|
||||
if (!nonces)
|
||||
if (!job)
|
||||
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;
|
||||
|
||||
block = attempt.commit(n1, n2, ts, nonce);
|
||||
|
||||
try {
|
||||
entry = yield this.chain.add(block);
|
||||
} catch (err) {
|
||||
@ -982,14 +988,17 @@ RPC.prototype.createWork = co(function* createWork(data) {
|
||||
});
|
||||
|
||||
RPC.prototype._createWork = co(function* _createWork() {
|
||||
var attempt = yield this.updateAttempt();
|
||||
var data, abbr;
|
||||
var attempt = yield this.updateWork();
|
||||
var n1 = this.nonce1;
|
||||
var n2 = this.nonce2;
|
||||
var ts = attempt.ts;
|
||||
var data, head;
|
||||
|
||||
data = new Buffer(128);
|
||||
data.fill(0);
|
||||
|
||||
abbr = attempt.block.abbr();
|
||||
abbr.copy(data, 0);
|
||||
head = attempt.getHeader(n1, n2, ts, 0);
|
||||
head.copy(data, 0);
|
||||
|
||||
data[80] = 0x80;
|
||||
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) {
|
||||
var attempt = yield this.getAttempt();
|
||||
var attempt = yield this.getTemplate();
|
||||
var scale = attempt.witness ? 1 : consensus.WITNESS_SCALE_FACTOR;
|
||||
var block = attempt.block;
|
||||
var mutable = ['time', 'transactions', 'prevblock'];
|
||||
var txs = [];
|
||||
var index = {};
|
||||
@ -1217,11 +1225,11 @@ RPC.prototype._createTemplate = co(function* _createTemplate(version, coinbase,
|
||||
case common.thresholdStates.FAILED:
|
||||
break;
|
||||
case common.thresholdStates.LOCKED_IN:
|
||||
block.version |= 1 << deploy.bit;
|
||||
attempt.version |= 1 << deploy.bit;
|
||||
case common.thresholdStates.STARTED:
|
||||
if (!deploy.force) {
|
||||
if (rules.indexOf(name) === -1)
|
||||
block.version &= ~(1 << deploy.bit);
|
||||
attempt.version &= ~(1 << deploy.bit);
|
||||
name = '!' + name;
|
||||
}
|
||||
vbavailable[name] = deploy.bit;
|
||||
@ -1240,24 +1248,24 @@ RPC.prototype._createTemplate = co(function* _createTemplate(version, coinbase,
|
||||
}
|
||||
}
|
||||
|
||||
block.version >>>= 0;
|
||||
attempt.version >>>= 0;
|
||||
|
||||
json = {
|
||||
capabilities: ['proposal'],
|
||||
mutable: mutable,
|
||||
version: block.version,
|
||||
version: attempt.version,
|
||||
rules: vbrules,
|
||||
vbavailable: vbavailable,
|
||||
vbrequired: 0,
|
||||
height: attempt.height,
|
||||
previousblockhash: util.revHex(block.prevBlock),
|
||||
previousblockhash: util.revHex(attempt.prevBlock),
|
||||
target: util.revHex(attempt.target.toString('hex')),
|
||||
bits: util.hex32(block.bits),
|
||||
bits: util.hex32(attempt.bits),
|
||||
noncerange: '00000000ffffffff',
|
||||
curtime: block.ts,
|
||||
mintime: block.ts,
|
||||
maxtime: block.ts + 7200,
|
||||
expires: block.ts + 7200,
|
||||
curtime: attempt.ts,
|
||||
mintime: attempt.ts,
|
||||
maxtime: attempt.ts + 7200,
|
||||
expires: attempt.ts + 7200,
|
||||
sigoplimit: consensus.MAX_BLOCK_SIGOPS_COST / scale | 0,
|
||||
sizelimit: consensus.MAX_BLOCK_SIZE,
|
||||
weightlimit: undefined,
|
||||
@ -1266,7 +1274,7 @@ RPC.prototype._createTemplate = co(function* _createTemplate(version, coinbase,
|
||||
coinbaseaux: {
|
||||
flags: attempt.coinbaseFlags.toString('hex')
|
||||
},
|
||||
coinbasevalue: attempt.coinbase.getOutputValue(),
|
||||
coinbasevalue: attempt.getReward(),
|
||||
coinbasetxn: undefined,
|
||||
default_witness_commitment: undefined,
|
||||
transactions: txs
|
||||
@ -1280,13 +1288,14 @@ RPC.prototype._createTemplate = co(function* _createTemplate(version, coinbase,
|
||||
}
|
||||
|
||||
if (coinbase) {
|
||||
tx = attempt.coinbase;
|
||||
tx = attempt.toCoinbase();
|
||||
|
||||
// We don't include the commitment
|
||||
// output (see bip145).
|
||||
if (attempt.witness) {
|
||||
output = tx.outputs.pop();
|
||||
assert(output.script.isCommitment());
|
||||
tx.refresh();
|
||||
}
|
||||
|
||||
json.coinbasetxn = {
|
||||
@ -1298,13 +1307,10 @@ RPC.prototype._createTemplate = co(function* _createTemplate(version, coinbase,
|
||||
sigops: tx.getSigopsCost() / scale | 0,
|
||||
weight: tx.getWeight()
|
||||
};
|
||||
|
||||
if (attempt.witness)
|
||||
tx.outputs.push(output);
|
||||
}
|
||||
|
||||
if (attempt.witness) {
|
||||
tx = attempt.coinbase;
|
||||
tx = attempt.toCoinbase();
|
||||
output = tx.outputs[tx.outputs.length - 1];
|
||||
assert(output.script.isCommitment());
|
||||
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) {
|
||||
var attempt = this.attempt;
|
||||
var hashps;
|
||||
var scale = attempt.witness ? 1 : consensus.WITNESS_SCALE_FACTOR;
|
||||
|
||||
if (help || args.length !== 0)
|
||||
throw new RPCError('getmininginfo');
|
||||
|
||||
hashps = yield this.getHashRate(120);
|
||||
|
||||
return {
|
||||
blocks: this.chain.height,
|
||||
currentblocksize: attempt ? attempt.block.getBaseSize() : 0,
|
||||
currentblocktx: attempt ? attempt.block.txs.length : 0,
|
||||
currentblocksize: attempt ? attempt.weight / scale | 0 : 0,
|
||||
currentblocktx: attempt ? attempt.items.length + 1 : 0,
|
||||
difficulty: this.difficulty(),
|
||||
errors: '',
|
||||
genproclimit: this.procLimit,
|
||||
networkhashps: hashps,
|
||||
networkhashps: yield this.getHashRate(120),
|
||||
pooledtx: this.totalTX(),
|
||||
testnet: this.network !== Network.main,
|
||||
chain: 'main',
|
||||
@ -2113,7 +2117,9 @@ RPC.prototype.refreshBlock = function refreshBlock() {
|
||||
|
||||
this.attempt = null;
|
||||
this.lastActivity = 0;
|
||||
this.coinbase = {};
|
||||
this.jobs = {};
|
||||
this.nonce1 = 0;
|
||||
this.nonce2 = 0;
|
||||
this.pollers = [];
|
||||
|
||||
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;
|
||||
|
||||
this.bindChain();
|
||||
@ -2163,22 +2169,31 @@ RPC.prototype.getAttempt = co(function* getAttempt() {
|
||||
return attempt;
|
||||
});
|
||||
|
||||
RPC.prototype.updateAttempt = co(function* updateAttempt() {
|
||||
RPC.prototype.updateWork = co(function* updateWork() {
|
||||
var attempt = this.attempt;
|
||||
var root;
|
||||
|
||||
this.bindChain();
|
||||
|
||||
if (attempt) {
|
||||
attempt.updateNonce();
|
||||
this.nonces[block.merkleRoot] = attempt.snapshot();
|
||||
if (++this.nonce1 === 0xffffffff) {
|
||||
this.nonce1 = 0;
|
||||
this.nonce2++;
|
||||
}
|
||||
root = attempt.getRoot(this.nonce1, this.nonce2);
|
||||
root = root.toString('hex');
|
||||
this.jobs[root] = new Nonces(this);
|
||||
return attempt;
|
||||
}
|
||||
|
||||
attempt = yield this.miner.createBlock();
|
||||
|
||||
root = attempt.getRoot(this.nonce1, this.nonce2);
|
||||
root = root.toString('hex');
|
||||
|
||||
this.attempt = attempt;
|
||||
this.lastActivity = util.now();
|
||||
this.nonces[block.merkleRoot] = attempt.snapshot();
|
||||
this.jobs[root] = new Nonces(this);
|
||||
|
||||
return attempt;
|
||||
});
|
||||
@ -2530,6 +2545,11 @@ function toDeployment(id, version, status) {
|
||||
};
|
||||
}
|
||||
|
||||
function Nonces(rpc) {
|
||||
this.nonce1 = rpc.nonce1;
|
||||
this.nonce2 = rpc.nonce2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Expose
|
||||
*/
|
||||
|
||||
562
lib/mining/cpuminer.js
Normal file
562
lib/mining/cpuminer.js
Normal 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;
|
||||
@ -4,6 +4,7 @@
|
||||
* @module mining
|
||||
*/
|
||||
|
||||
exports.BlockTemplate = require('./template');
|
||||
exports.CPUMiner = require('./cpuminer');
|
||||
exports.mine = require('./mine');
|
||||
exports.Miner = require('./miner');
|
||||
exports.MinerBlock = require('./minerblock');
|
||||
|
||||
@ -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-2017, Christopher Jeffrey (MIT License).
|
||||
* https://github.com/bcoin-org/bcoin
|
||||
@ -14,23 +14,18 @@ var Heap = require('../utils/heap');
|
||||
var AsyncObject = require('../utils/asyncobject');
|
||||
var Amount = require('../btc/amount');
|
||||
var Address = require('../primitives/address');
|
||||
var MinerBlock = require('./minerblock');
|
||||
var BlockTemplate = require('./template');
|
||||
var Network = require('../protocol/network');
|
||||
var consensus = require('../protocol/consensus');
|
||||
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
|
||||
* @constructor
|
||||
* @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) {
|
||||
@ -40,58 +35,17 @@ function Miner(options) {
|
||||
AsyncObject.call(this);
|
||||
|
||||
this.options = new MinerOptions(options);
|
||||
|
||||
this.network = this.options.network;
|
||||
this.logger = this.options.logger;
|
||||
this.chain = this.options.chain;
|
||||
this.mempool = this.options.mempool;
|
||||
this.addresses = this.options.addresses;
|
||||
|
||||
this.locker = this.chain.locker;
|
||||
|
||||
this.running = false;
|
||||
this.stopping = false;
|
||||
this.attempt = null;
|
||||
this.since = 0;
|
||||
|
||||
this._init();
|
||||
this.cpu = new CPUMiner(this);
|
||||
}
|
||||
|
||||
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.
|
||||
* @method
|
||||
@ -105,6 +59,8 @@ Miner.prototype._open = co(function* open() {
|
||||
if (this.mempool)
|
||||
yield this.mempool.open();
|
||||
|
||||
yield this.cpu.open();
|
||||
|
||||
this.logger.info('Miner loaded (flags=%s).',
|
||||
this.options.coinbaseFlags.toString('utf8'));
|
||||
|
||||
@ -120,145 +76,15 @@ Miner.prototype._open = co(function* open() {
|
||||
*/
|
||||
|
||||
Miner.prototype._close = co(function* close() {
|
||||
if (!this.running)
|
||||
return;
|
||||
|
||||
if (this.stopping) {
|
||||
yield this._onStop();
|
||||
return;
|
||||
}
|
||||
|
||||
yield this.stop();
|
||||
yield this.cpu.close();
|
||||
});
|
||||
|
||||
/**
|
||||
* Start mining.
|
||||
* Create a block template.
|
||||
* @method
|
||||
* @param {Number?} version - Custom block version.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
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}.
|
||||
* @param {ChainEntry?} tip
|
||||
* @param {Address?} address
|
||||
* @returns {Promise} - Returns {@link BlockTemplate}.
|
||||
*/
|
||||
|
||||
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
|
||||
* @private
|
||||
* @param {ChainEntry} tip
|
||||
* @returns {Promise} - Returns {@link MinerBlock}.
|
||||
* @param {ChainEntry?} tip
|
||||
* @param {Address?} address
|
||||
* @returns {Promise} - Returns {@link BlockTemplate}.
|
||||
*/
|
||||
|
||||
Miner.prototype._createBlock = co(function* createBlock(tip, address) {
|
||||
var version = this.options.version;
|
||||
var ts, locktime, target, attempt;
|
||||
var ts, mtp, locktime, target, attempt, block;
|
||||
|
||||
if (!tip)
|
||||
tip = this.chain.tip;
|
||||
@ -291,19 +118,18 @@ Miner.prototype._createBlock = co(function* createBlock(tip, address) {
|
||||
if (version === -1)
|
||||
version = yield this.chain.computeBlockVersion(tip);
|
||||
|
||||
if (this.chain.state.hasMTP()) {
|
||||
locktime = yield tip.getMedianTime();
|
||||
ts = Math.max(this.network.now(), locktime + 1);
|
||||
} else {
|
||||
ts = Math.max(this.network.now(), tip.ts + 1);
|
||||
locktime = ts;
|
||||
}
|
||||
mtp = yield tip.getMedianTime();
|
||||
ts = Math.max(this.network.now(), mtp + 1);
|
||||
locktime = ts;
|
||||
|
||||
if (this.chain.state.hasMTP())
|
||||
locktime = mtp;
|
||||
|
||||
target = yield this.chain.getTarget(ts, tip);
|
||||
|
||||
attempt = new MinerBlock({
|
||||
network: this.network,
|
||||
tip: tip,
|
||||
attempt = new BlockTemplate({
|
||||
prevBlock: tip.hash,
|
||||
height: tip.height + 1,
|
||||
version: version,
|
||||
ts: ts,
|
||||
bits: target,
|
||||
@ -312,11 +138,12 @@ Miner.prototype._createBlock = co(function* createBlock(tip, address) {
|
||||
address: address,
|
||||
coinbaseFlags: this.options.coinbaseFlags,
|
||||
witness: this.chain.state.hasWitness(),
|
||||
halvingInterval: this.network.halvingInterval,
|
||||
weight: this.options.reservedWeight,
|
||||
sigops: this.options.reservedSigops
|
||||
});
|
||||
|
||||
this.build(attempt);
|
||||
this.assemble(attempt);
|
||||
|
||||
this.logger.debug(
|
||||
'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);
|
||||
|
||||
if (this.options.preverify) {
|
||||
block = attempt.toBlock();
|
||||
|
||||
try {
|
||||
yield this.chain._verifyBlock(attempt.block);
|
||||
yield this.chain._verifyBlock(block);
|
||||
} catch (e) {
|
||||
if (e.type === 'VerifyError') {
|
||||
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
|
||||
* @param {ChainEntry} tip
|
||||
* @returns {Promise} - Returns [{@link Block}].
|
||||
* @param {ChainEntry?} tip
|
||||
* @param {Address?} address
|
||||
* @returns {Promise} Returns {@link CPUJob}.
|
||||
*/
|
||||
|
||||
Miner.prototype.mineBlock = co(function* mineBlock(tip, address) {
|
||||
var attempt = yield this.createBlock(tip, address);
|
||||
return yield attempt.mineAsync();
|
||||
Miner.prototype.createJob = co(function* createJob(tip, address) {
|
||||
return yield this.cpu.createJob(tip, address);
|
||||
});
|
||||
|
||||
/**
|
||||
* Notify the miner that a new tx has entered the mempool.
|
||||
* @param {MempoolEntry} entry
|
||||
* Mine a single block.
|
||||
* @method
|
||||
* @param {ChainEntry?} tip
|
||||
* @param {Address?} address
|
||||
* @returns {Promise} Returns {@link Block}.
|
||||
*/
|
||||
|
||||
Miner.prototype.notifyEntry = function notifyEntry() {
|
||||
if (!this.running)
|
||||
return;
|
||||
|
||||
if (!this.attempt)
|
||||
return;
|
||||
|
||||
if (++this.since > 20) {
|
||||
this.since = 0;
|
||||
this.attempt.destroy();
|
||||
}
|
||||
};
|
||||
Miner.prototype.mineBlock = co(function* mineBlock(tip, address) {
|
||||
return yield this.cpu.mineBlock(tip, address);
|
||||
});
|
||||
|
||||
/**
|
||||
* Add an address to the address list.
|
||||
@ -391,23 +214,23 @@ Miner.prototype.addAddress = function addAddress(address) {
|
||||
|
||||
Miner.prototype.getAddress = function getAddress() {
|
||||
if (this.addresses.length === 0)
|
||||
return;
|
||||
return new Address();
|
||||
return this.addresses[Math.random() * this.addresses.length | 0];
|
||||
};
|
||||
|
||||
/**
|
||||
* Get mempool entries, sort by dependency order.
|
||||
* Prioritize by priority and fee rates.
|
||||
* @param {BlockTemplate} attempt
|
||||
* @returns {MempoolEntry[]}
|
||||
*/
|
||||
|
||||
Miner.prototype.build = function build(attempt) {
|
||||
Miner.prototype.assemble = function assemble(attempt) {
|
||||
var depMap = {};
|
||||
var block = attempt.block;
|
||||
var queue = new Heap(cmpRate);
|
||||
var priority = this.options.priorityWeight > 0;
|
||||
var i, j, entry, item, tx, hash, input;
|
||||
var prev, deps, hashes, weight, sigops;
|
||||
var prev, deps, hashes, weight, sigops, block;
|
||||
|
||||
if (priority)
|
||||
queue.set(cmpPriority);
|
||||
@ -492,8 +315,6 @@ Miner.prototype.build = function build(attempt) {
|
||||
attempt.fees += item.fee;
|
||||
attempt.items.push(item);
|
||||
|
||||
block.txs.push(tx);
|
||||
|
||||
deps = depMap[hash];
|
||||
|
||||
if (!deps)
|
||||
@ -508,10 +329,18 @@ Miner.prototype.build = function build(attempt) {
|
||||
|
||||
attempt.refresh();
|
||||
|
||||
assert(block.getWeight() <= attempt.weight,
|
||||
assert(attempt.weight <= consensus.MAX_BLOCK_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.');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -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
621
lib/mining/template.js
Normal 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;
|
||||
@ -12,6 +12,7 @@ exports.Bloom = require('./bloom');
|
||||
exports.RollingFilter = exports.Bloom.Rolling;
|
||||
exports.co = require('./co');
|
||||
exports.encoding = require('./encoding');
|
||||
exports.fs = require('./fs');
|
||||
exports.Heap = require('./heap');
|
||||
exports.IP = require('./ip');
|
||||
exports.lazy = require('./lazy');
|
||||
|
||||
@ -15,7 +15,6 @@ var Amount = require('../btc/amount');
|
||||
var Script = require('../script/script');
|
||||
var Address = require('../primitives/address');
|
||||
var KeyRing = require('../primitives/keyring');
|
||||
var Lock = require('../utils/lock');
|
||||
var MerkleBlock = require('../primitives/merkleblock');
|
||||
var MTX = require('../primitives/mtx');
|
||||
var Outpoint = require('../primitives/outpoint');
|
||||
|
||||
@ -14,6 +14,7 @@ var MemWallet = require('./util/memwallet');
|
||||
var Network = require('../lib/protocol/network');
|
||||
var Output = require('../lib/primitives/output');
|
||||
var util = require('../lib/utils/util');
|
||||
var common = require('../lib/blockchain/common');
|
||||
var opcodes = Script.opcodes;
|
||||
|
||||
describe('Chain', function() {
|
||||
@ -22,16 +23,16 @@ describe('Chain', function() {
|
||||
var miner = new Miner({ chain: chain, version: 4 });
|
||||
var wallet = new MemWallet({ network: network });
|
||||
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);
|
||||
|
||||
addBlock = co(function* addBlock(attempt) {
|
||||
var block = yield attempt.mineAsync();
|
||||
addBlock = co(function* addBlock(block, flags) {
|
||||
var entry;
|
||||
|
||||
try {
|
||||
entry = yield chain.add(block);
|
||||
entry = yield chain.add(block, flags);
|
||||
} catch (e) {
|
||||
assert(e.type === 'VerifyError');
|
||||
return e.reason;
|
||||
@ -43,8 +44,13 @@ describe('Chain', function() {
|
||||
return 'OK';
|
||||
});
|
||||
|
||||
mineBlock = co(function* mineBlock(job, flags) {
|
||||
var block = yield job.mineAsync();
|
||||
return yield addBlock(block, flags);
|
||||
});
|
||||
|
||||
mineCSV = co(function* mineCSV(tx) {
|
||||
var attempt = yield miner.createBlock();
|
||||
var job = yield cpu.createJob();
|
||||
var rtx;
|
||||
|
||||
rtx = new MTX();
|
||||
@ -63,9 +69,10 @@ describe('Chain', function() {
|
||||
|
||||
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) {
|
||||
@ -90,7 +97,7 @@ describe('Chain', function() {
|
||||
var i, block;
|
||||
|
||||
for (i = 0; i < 200; i++) {
|
||||
block = yield miner.mineBlock();
|
||||
block = yield cpu.mineBlock();
|
||||
assert(block);
|
||||
assert(yield chain.add(block));
|
||||
}
|
||||
@ -99,11 +106,11 @@ describe('Chain', 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++) {
|
||||
at1 = yield miner.createBlock(tip1);
|
||||
at2 = yield miner.createBlock(tip2);
|
||||
job1 = yield cpu.createJob(tip1);
|
||||
job2 = yield cpu.createJob(tip2);
|
||||
|
||||
mtx = yield wallet.create({
|
||||
outputs: [{
|
||||
@ -112,11 +119,14 @@ describe('Chain', function() {
|
||||
}]
|
||||
});
|
||||
|
||||
at1.addTX(mtx.toTX(), mtx.view);
|
||||
at2.addTX(mtx.toTX(), mtx.view);
|
||||
job1.addTX(mtx.toTX(), mtx.view);
|
||||
job2.addTX(mtx.toTX(), mtx.view);
|
||||
|
||||
blk1 = yield at1.mineAsync();
|
||||
blk2 = yield at2.mineAsync();
|
||||
job1.refresh();
|
||||
job2.refresh();
|
||||
|
||||
blk1 = yield job1.mineAsync();
|
||||
blk2 = yield job2.mineAsync();
|
||||
|
||||
hash1 = blk1.hash('hex');
|
||||
hash2 = blk2.hash('hex');
|
||||
@ -156,7 +166,7 @@ describe('Chain', function() {
|
||||
assert(entry);
|
||||
assert(chain.height === entry.height);
|
||||
|
||||
block = yield miner.mineBlock(entry);
|
||||
block = yield cpu.mineBlock(entry);
|
||||
assert(block);
|
||||
|
||||
chain.once('reorganize', function() {
|
||||
@ -186,7 +196,7 @@ describe('Chain', 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;
|
||||
|
||||
assert(yield chain.add(block));
|
||||
@ -202,7 +212,7 @@ describe('Chain', function() {
|
||||
}));
|
||||
|
||||
it('should prevent double spend on new chain', co(function* () {
|
||||
var attempt = yield miner.createBlock();
|
||||
var job = yield cpu.createJob();
|
||||
var mtx, block;
|
||||
|
||||
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));
|
||||
|
||||
attempt = yield miner.createBlock();
|
||||
job = yield cpu.createJob();
|
||||
|
||||
assert(mtx.outputs.length > 1);
|
||||
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* () {
|
||||
var block = yield chain.db.getBlock(tip1.hash);
|
||||
var cb = block.txs[0];
|
||||
var mtx = new MTX();
|
||||
var attempt;
|
||||
var job;
|
||||
|
||||
mtx.addTX(cb, 0);
|
||||
mtx.addOutput(wallet.getAddress(), 10 * 1e8);
|
||||
|
||||
wallet.sign(mtx);
|
||||
|
||||
attempt = yield miner.createBlock();
|
||||
attempt.addTX(mtx.toTX(), mtx.view);
|
||||
job = yield cpu.createJob();
|
||||
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() {
|
||||
@ -252,7 +265,7 @@ describe('Chain', 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({
|
||||
outputs: [
|
||||
@ -271,10 +284,11 @@ describe('Chain', function() {
|
||||
]
|
||||
});
|
||||
|
||||
attempt = yield miner.createBlock();
|
||||
attempt.addTX(mtx.toTX(), mtx.view);
|
||||
job = yield cpu.createJob();
|
||||
job.addTX(mtx.toTX(), mtx.view);
|
||||
job.refresh();
|
||||
|
||||
block = yield attempt.mineAsync();
|
||||
block = yield job.mineAsync();
|
||||
assert(yield chain.add(block));
|
||||
|
||||
tx = block.txs[1];
|
||||
@ -330,7 +344,7 @@ describe('Chain', function() {
|
||||
assert.equal(state, 1);
|
||||
|
||||
for (i = 0; i < 417; i++) {
|
||||
block = yield miner.mineBlock();
|
||||
block = yield cpu.mineBlock();
|
||||
assert(yield chain.add(block));
|
||||
switch (chain.height) {
|
||||
case 288:
|
||||
@ -371,7 +385,7 @@ describe('Chain', function() {
|
||||
it('should test csv', co(function* () {
|
||||
var tx = (yield chain.db.getBlock(chain.height - 100)).txs[0];
|
||||
var block = yield mineCSV(tx);
|
||||
var csv, attempt, rtx;
|
||||
var csv, job, rtx;
|
||||
|
||||
assert(yield chain.add(block));
|
||||
|
||||
@ -390,11 +404,12 @@ describe('Chain', function() {
|
||||
rtx.addTX(csv, 0);
|
||||
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));
|
||||
}));
|
||||
@ -402,7 +417,7 @@ describe('Chain', function() {
|
||||
it('should fail csv with bad sequence', co(function* () {
|
||||
var csv = (yield chain.db.getBlock(chain.height - 100)).txs[0];
|
||||
var rtx = new MTX();
|
||||
var attempt;
|
||||
var job;
|
||||
|
||||
rtx.addOutput({
|
||||
script: [
|
||||
@ -415,14 +430,15 @@ describe('Chain', function() {
|
||||
rtx.addTX(csv, 0);
|
||||
rtx.setSequence(0, 1, false);
|
||||
|
||||
attempt = yield miner.createBlock();
|
||||
attempt.addTX(rtx.toTX(), rtx.view);
|
||||
job = yield cpu.createJob();
|
||||
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* () {
|
||||
var block = yield miner.mineBlock();
|
||||
var block = yield cpu.mineBlock();
|
||||
assert(block);
|
||||
assert(yield chain.add(block));
|
||||
}));
|
||||
@ -430,7 +446,7 @@ describe('Chain', function() {
|
||||
it('should fail csv lock checks', co(function* () {
|
||||
var tx = (yield chain.db.getBlock(chain.height - 100)).txs[0];
|
||||
var block = yield mineCSV(tx);
|
||||
var csv, attempt, rtx;
|
||||
var csv, job, rtx;
|
||||
|
||||
assert(yield chain.add(block));
|
||||
|
||||
@ -449,10 +465,11 @@ describe('Chain', function() {
|
||||
rtx.addTX(csv, 0);
|
||||
rtx.setSequence(0, 2, false);
|
||||
|
||||
attempt = yield miner.createBlock();
|
||||
attempt.addTX(rtx.toTX(), rtx.view);
|
||||
job = yield cpu.createJob();
|
||||
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* () {
|
||||
@ -460,72 +477,72 @@ describe('Chain', function() {
|
||||
}));
|
||||
|
||||
it('should fail to connect bad bits', co(function* () {
|
||||
var attempt = yield miner.createBlock();
|
||||
attempt.block.bits = 553713663;
|
||||
assert.equal(yield addBlock(attempt), 'bad-diffbits');
|
||||
var job = yield cpu.createJob();
|
||||
job.attempt.bits = 553713663;
|
||||
assert.equal(yield mineBlock(job), 'bad-diffbits');
|
||||
}));
|
||||
|
||||
it('should fail to connect bad MTP', co(function* () {
|
||||
var mtp = yield chain.tip.getMedianTime();
|
||||
var attempt = yield miner.createBlock();
|
||||
attempt.block.ts = mtp - 1;
|
||||
assert.equal(yield addBlock(attempt), 'time-too-old');
|
||||
var job = yield cpu.createJob();
|
||||
job.attempt.ts = mtp - 1;
|
||||
assert.equal(yield mineBlock(job), 'time-too-old');
|
||||
}));
|
||||
|
||||
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;
|
||||
attempt.block.ts = now;
|
||||
assert.equal(yield addBlock(attempt), 'time-too-new');
|
||||
job.attempt.ts = now;
|
||||
assert.equal(yield mineBlock(job), 'time-too-new');
|
||||
}));
|
||||
|
||||
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 });
|
||||
attempt.block.txs.push(tx.toTX());
|
||||
attempt.refresh();
|
||||
assert.equal(yield addBlock(attempt), 'bad-txns-nonfinal');
|
||||
job.pushTX(tx.toTX());
|
||||
job.refresh();
|
||||
assert.equal(yield mineBlock(job), 'bad-txns-nonfinal');
|
||||
}));
|
||||
|
||||
it('should fail to connect bad cb height', co(function* () {
|
||||
var bip34height = network.block.bip34height;
|
||||
var attempt = yield miner.createBlock();
|
||||
var tx = attempt.block.txs[0];
|
||||
var input = tx.inputs[0];
|
||||
var job = yield cpu.createJob();
|
||||
|
||||
input.script.set(0, new BN(10));
|
||||
input.script.compile();
|
||||
attempt.refresh();
|
||||
job.attempt.height = 10;
|
||||
job.attempt.refresh();
|
||||
|
||||
try {
|
||||
network.block.bip34height = 0;
|
||||
assert.equal(yield addBlock(attempt), 'bad-cb-height');
|
||||
assert.equal(yield mineBlock(job), 'bad-cb-height');
|
||||
} finally {
|
||||
network.block.bip34height = bip34height;
|
||||
}
|
||||
}));
|
||||
|
||||
it('should fail to connect bad witness nonce size', co(function* () {
|
||||
var attempt = yield miner.createBlock();
|
||||
var tx = attempt.block.txs[0];
|
||||
var block = yield cpu.mineBlock();
|
||||
var tx = block.txs[0];
|
||||
var input = tx.inputs[0];
|
||||
input.witness.set(0, new Buffer(33));
|
||||
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* () {
|
||||
var attempt = yield miner.createBlock();
|
||||
var tx = attempt.block.txs[0];
|
||||
var block = yield cpu.mineBlock();
|
||||
var tx = block.txs[0];
|
||||
var input = tx.inputs[0];
|
||||
input.witness.set(0, encoding.ONE_HASH);
|
||||
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* () {
|
||||
var attempt = yield miner.createBlock();
|
||||
var tx = attempt.block.txs[0];
|
||||
var flags = common.flags.DEFAULT_FLAGS & ~common.flags.VERIFY_POW;
|
||||
var block = yield cpu.mineBlock();
|
||||
var tx = block.txs[0];
|
||||
var output = tx.outputs[1];
|
||||
var commit;
|
||||
|
||||
@ -536,18 +553,26 @@ describe('Chain', function() {
|
||||
output.script.set(1, commit);
|
||||
output.script.compile();
|
||||
|
||||
attempt.updateMerkle();
|
||||
assert.equal(yield addBlock(attempt), 'bad-witness-merkle-match');
|
||||
block.refresh(true);
|
||||
block.merkleRoot = block.createMerkleRoot('hex');
|
||||
|
||||
assert.equal(yield addBlock(block, flags), 'bad-witness-merkle-match');
|
||||
}));
|
||||
|
||||
it('should fail to connect unexpected witness', co(function* () {
|
||||
var attempt = yield miner.createBlock();
|
||||
var tx = attempt.block.txs[0];
|
||||
var flags = common.flags.DEFAULT_FLAGS & ~common.flags.VERIFY_POW;
|
||||
var block = yield cpu.mineBlock();
|
||||
var tx = block.txs[0];
|
||||
var output = tx.outputs[1];
|
||||
|
||||
assert(output.script.isCommitment());
|
||||
|
||||
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* () {
|
||||
@ -560,7 +585,7 @@ describe('Chain', function() {
|
||||
var i, block;
|
||||
|
||||
for (i = 0; i < 2001; i++) {
|
||||
block = yield miner.mineBlock();
|
||||
block = yield cpu.mineBlock();
|
||||
assert(block);
|
||||
assert(yield chain.add(block));
|
||||
}
|
||||
@ -572,17 +597,18 @@ describe('Chain', function() {
|
||||
var block = yield chain.db.getBlock(chain.height - 2000);
|
||||
var cb = block.txs[0];
|
||||
var mtx = new MTX();
|
||||
var attempt;
|
||||
var job;
|
||||
|
||||
mtx.addTX(cb, 0);
|
||||
mtx.addOutput(wwallet.getAddress(), 1000);
|
||||
|
||||
wwallet.sign(mtx);
|
||||
|
||||
attempt = yield miner.createBlock();
|
||||
attempt.addTX(mtx.toTX(), mtx.view);
|
||||
job = yield cpu.createJob();
|
||||
job.addTX(mtx.toTX(), mtx.view);
|
||||
job.refresh();
|
||||
|
||||
block = yield attempt.mineAsync();
|
||||
block = yield job.mineAsync();
|
||||
|
||||
assert(yield chain.add(block));
|
||||
}));
|
||||
@ -590,7 +616,7 @@ describe('Chain', function() {
|
||||
it('should mine fail to connect too much weight', co(function* () {
|
||||
var start = chain.height - 2000;
|
||||
var end = chain.height - 200;
|
||||
var attempt = yield miner.createBlock();
|
||||
var job = yield cpu.createJob();
|
||||
var mtx = new MTX();
|
||||
var i, j, block, cb;
|
||||
|
||||
@ -606,18 +632,18 @@ describe('Chain', function() {
|
||||
|
||||
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* () {
|
||||
var start = chain.height - 2000;
|
||||
var end = chain.height - 200;
|
||||
var attempt = yield miner.createBlock();
|
||||
var job = yield cpu.createJob();
|
||||
var mtx = new MTX();
|
||||
var i, j, block, cb;
|
||||
|
||||
@ -633,18 +659,18 @@ describe('Chain', function() {
|
||||
|
||||
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* () {
|
||||
var start = chain.height - 2000;
|
||||
var end = chain.height - 200;
|
||||
var attempt = yield miner.createBlock();
|
||||
var job = yield cpu.createJob();
|
||||
var mtx = new MTX();
|
||||
var i, j, block, cb;
|
||||
|
||||
@ -660,34 +686,34 @@ describe('Chain', function() {
|
||||
|
||||
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* () {
|
||||
var i, attempt;
|
||||
var i, job;
|
||||
|
||||
for (i = 0; i <= 3; i++) {
|
||||
attempt = yield miner.createBlock();
|
||||
attempt.block.version = i;
|
||||
assert.equal(yield addBlock(attempt), 'bad-version');
|
||||
job = yield cpu.createJob();
|
||||
job.attempt.version = i;
|
||||
assert.equal(yield mineBlock(job), 'bad-version');
|
||||
}
|
||||
}));
|
||||
|
||||
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;
|
||||
attempt.updateMerkle();
|
||||
assert.equal(yield addBlock(attempt), 'bad-cb-amount');
|
||||
job.attempt.reward += 1;
|
||||
job.refresh();
|
||||
assert.equal(yield mineBlock(job), 'bad-cb-amount');
|
||||
}));
|
||||
|
||||
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 cb = block.txs[0];
|
||||
var mtx = new MTX();
|
||||
@ -697,14 +723,15 @@ describe('Chain', function() {
|
||||
|
||||
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');
|
||||
}));
|
||||
|
||||
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 cb = block.txs[0];
|
||||
var mtx = new MTX();
|
||||
@ -714,15 +741,15 @@ describe('Chain', function() {
|
||||
|
||||
wwallet.sign(mtx);
|
||||
|
||||
attempt.block.txs.push(mtx.toTX());
|
||||
attempt.refresh();
|
||||
job.pushTX(mtx.toTX());
|
||||
job.refresh();
|
||||
|
||||
assert.equal(yield addBlock(attempt),
|
||||
assert.equal(yield mineBlock(job),
|
||||
'bad-txns-in-belowout');
|
||||
}));
|
||||
|
||||
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 cb = block.txs[0];
|
||||
var mtx = new MTX();
|
||||
@ -734,15 +761,16 @@ describe('Chain', function() {
|
||||
|
||||
wwallet.sign(mtx);
|
||||
|
||||
attempt.block.txs.push(mtx.toTX());
|
||||
attempt.refresh();
|
||||
job.pushTX(mtx.toTX());
|
||||
job.refresh();
|
||||
|
||||
assert.equal(yield addBlock(attempt),
|
||||
assert.equal(yield mineBlock(job),
|
||||
'bad-txns-txouttotal-toolarge');
|
||||
}));
|
||||
|
||||
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.push(new BN(20));
|
||||
@ -757,8 +785,8 @@ describe('Chain', function() {
|
||||
script = Script.fromScripthash(script.hash160());
|
||||
|
||||
for (i = 0; i < 111; i++) {
|
||||
attempt = yield miner.createBlock();
|
||||
cb = attempt.block.txs[0];
|
||||
block = yield cpu.mineBlock();
|
||||
cb = block.txs[0];
|
||||
val = cb.outputs[0].value;
|
||||
|
||||
cb.outputs[0].value = 0;
|
||||
@ -771,10 +799,10 @@ describe('Chain', function() {
|
||||
cb.outputs.push(output);
|
||||
}
|
||||
|
||||
attempt.updateMerkle();
|
||||
block.refresh(true);
|
||||
block.merkleRoot = block.createMerkleRoot('hex');
|
||||
|
||||
block = yield attempt.mineAsync();
|
||||
assert(yield chain.add(block));
|
||||
assert(yield chain.add(block, flags));
|
||||
}
|
||||
|
||||
assert.equal(chain.height, 2749);
|
||||
@ -783,7 +811,7 @@ describe('Chain', function() {
|
||||
it('should fail to connect too many sigops', co(function* () {
|
||||
var start = chain.height - 110;
|
||||
var end = chain.height - 100;
|
||||
var attempt = yield miner.createBlock();
|
||||
var job = yield cpu.createJob();
|
||||
var i, j, mtx, script, block, cb;
|
||||
|
||||
script = new Script();
|
||||
@ -812,12 +840,12 @@ describe('Chain', function() {
|
||||
|
||||
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* () {
|
||||
|
||||
@ -8,7 +8,6 @@ var Coin = require('../lib/primitives/coin');
|
||||
var Script = require('../lib/script/script');
|
||||
var FullNode = require('../lib/node/fullnode');
|
||||
var MTX = require('../lib/primitives/mtx');
|
||||
// var Client = require('../lib/wallet/client');
|
||||
|
||||
describe('Node', function() {
|
||||
var node = new FullNode({
|
||||
@ -23,18 +22,16 @@ describe('Node', function() {
|
||||
var miner = node.miner;
|
||||
var wallet, tip1, tip2, cb1, cb2, mineBlock;
|
||||
|
||||
// walletdb.client = new Client({ apiKey: 'foo', network: 'regtest' });
|
||||
|
||||
node.on('error', function() {});
|
||||
|
||||
this.timeout(5000);
|
||||
|
||||
mineBlock = co(function* mineBlock(tip, tx) {
|
||||
var attempt = yield miner.createBlock(tip);
|
||||
var job = yield miner.createJob(tip);
|
||||
var rtx;
|
||||
|
||||
if (!tx)
|
||||
return yield attempt.mineAsync();
|
||||
return yield job.mineAsync();
|
||||
|
||||
rtx = new MTX();
|
||||
|
||||
@ -47,9 +44,10 @@ describe('Node', function() {
|
||||
|
||||
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* () {
|
||||
@ -312,7 +310,7 @@ describe('Node', function() {
|
||||
}));
|
||||
|
||||
var mineCSV = co(function* mineCSV(tx) {
|
||||
var attempt = yield miner.createBlock();
|
||||
var job = yield miner.createJob();
|
||||
var redeemer;
|
||||
|
||||
redeemer = new MTX();
|
||||
@ -331,15 +329,16 @@ describe('Node', function() {
|
||||
|
||||
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* () {
|
||||
var tx = (yield chain.db.getBlock(chain.height)).txs[0];
|
||||
var block = yield mineCSV(tx);
|
||||
var csv, attempt, redeemer;
|
||||
var csv, job, redeemer;
|
||||
|
||||
yield chain.add(block);
|
||||
|
||||
@ -358,18 +357,19 @@ describe('Node', function() {
|
||||
redeemer.addTX(csv, 0);
|
||||
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);
|
||||
}));
|
||||
|
||||
it('should fail csv with bad sequence', co(function* () {
|
||||
var csv = (yield chain.db.getBlock(chain.height)).txs[1];
|
||||
var block, attempt, redeemer, err;
|
||||
var block, job, redeemer, err;
|
||||
|
||||
redeemer = new MTX();
|
||||
|
||||
@ -384,11 +384,12 @@ describe('Node', function() {
|
||||
redeemer.addTX(csv, 0);
|
||||
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 {
|
||||
yield chain.add(block);
|
||||
@ -409,7 +410,7 @@ describe('Node', function() {
|
||||
it('should fail csv lock checks', co(function* () {
|
||||
var tx = (yield chain.db.getBlock(chain.height)).txs[0];
|
||||
var block = yield mineCSV(tx);
|
||||
var csv, attempt, redeemer, err;
|
||||
var csv, job, redeemer, err;
|
||||
|
||||
yield chain.add(block);
|
||||
|
||||
@ -428,11 +429,12 @@ describe('Node', function() {
|
||||
redeemer.addTX(csv, 0);
|
||||
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 {
|
||||
yield chain.add(block);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user