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