mempool/miner: mining and mempool refactor.
This commit is contained in:
parent
9e1428a8d5
commit
bc00697adb
155
lib/http/rpc.js
155
lib/http/rpc.js
@ -338,8 +338,8 @@ RPC.prototype.getinfo = co(function* getinfo(args) {
|
||||
keypoololdest: 0,
|
||||
keypoolsize: 0,
|
||||
unlocked_until: this.wallet.master.until,
|
||||
paytxfee: +utils.btc(this.network.getRate()),
|
||||
relayfee: +utils.btc(this.network.getMinRelay()),
|
||||
paytxfee: +utils.btc(this.network.feeRate),
|
||||
relayfee: +utils.btc(this.network.minRelay),
|
||||
errors: ''
|
||||
};
|
||||
});
|
||||
@ -1312,6 +1312,15 @@ RPC.prototype.verifychain = function verifychain(args) {
|
||||
*/
|
||||
|
||||
RPC.prototype._submitwork = co(function* _submitwork(data) {
|
||||
var unlock = yield this.locker.lock();
|
||||
try {
|
||||
return yield this.__submitwork(data);
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
});
|
||||
|
||||
RPC.prototype.__submitwork = co(function* _submitwork(data) {
|
||||
var attempt = this.attempt;
|
||||
var block, header, cb, cur;
|
||||
|
||||
@ -1368,7 +1377,16 @@ RPC.prototype._submitwork = co(function* _submitwork(data) {
|
||||
return true;
|
||||
});
|
||||
|
||||
RPC.prototype._getwork = co(function* _getwork() {
|
||||
RPC.prototype._creatework = co(function* _creatework(data) {
|
||||
var unlock = yield this.locker.lock();
|
||||
try {
|
||||
return yield this.__creatework(data);
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
});
|
||||
|
||||
RPC.prototype.__creatework = co(function* _creatework() {
|
||||
var attempt = yield this._getAttempt(true);
|
||||
var data, abbr;
|
||||
|
||||
@ -1392,55 +1410,32 @@ RPC.prototype._getwork = co(function* _getwork() {
|
||||
|
||||
RPC.prototype.getworklp = co(function* getworklp(args) {
|
||||
yield this._onBlock();
|
||||
return yield this._getwork();
|
||||
return yield this._creatework();
|
||||
});
|
||||
|
||||
RPC.prototype.getwork = co(function* getwork(args) {
|
||||
var unlock = yield this.locker.lock();
|
||||
var data, result;
|
||||
var data;
|
||||
|
||||
if (args.length > 1) {
|
||||
unlock();
|
||||
if (args.length > 1)
|
||||
throw new RPCError('getwork ( "data" )');
|
||||
}
|
||||
|
||||
if (args.length === 1) {
|
||||
if (!utils.isHex(args[0])) {
|
||||
unlock();
|
||||
if (!utils.isHex(args[0]))
|
||||
throw new RPCError('Invalid parameter.');
|
||||
}
|
||||
|
||||
data = new Buffer(args[0], 'hex');
|
||||
|
||||
try {
|
||||
result = yield this._submitwork(data);
|
||||
} catch (e) {
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
|
||||
return result;
|
||||
return yield this._submitwork(data);
|
||||
}
|
||||
|
||||
try {
|
||||
result = yield this._getwork();
|
||||
} catch (e) {
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
|
||||
unlock();
|
||||
return result;
|
||||
return yield this._creatework();
|
||||
});
|
||||
|
||||
RPC.prototype.submitblock = co(function* submitblock(args) {
|
||||
var unlock = yield this.locker.lock();
|
||||
var block;
|
||||
|
||||
if (args.help || args.length < 1 || args.length > 2) {
|
||||
unlock();
|
||||
if (args.help || args.length < 1 || args.length > 2)
|
||||
throw new RPCError('submitblock "hexdata" ( "jsonparametersobject" )');
|
||||
}
|
||||
|
||||
block = Block.fromRaw(toString(args[0]), 'hex');
|
||||
|
||||
@ -1448,6 +1443,15 @@ RPC.prototype.submitblock = co(function* submitblock(args) {
|
||||
});
|
||||
|
||||
RPC.prototype._submitblock = co(function* submitblock(block) {
|
||||
var unlock = yield this.locker.lock();
|
||||
try {
|
||||
return yield this.__submitblock(block);
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
});
|
||||
|
||||
RPC.prototype.__submitblock = co(function* submitblock(block) {
|
||||
if (block.prevBlock !== this.chain.tip.hash)
|
||||
return 'rejected: inconclusive-not-best-prevblk';
|
||||
|
||||
@ -1528,36 +1532,43 @@ RPC.prototype.getblocktemplate = co(function* getblocktemplate(args) {
|
||||
|
||||
yield this._poll(lpid);
|
||||
|
||||
return yield this._tmpl(version, coinbase, rules);
|
||||
return yield this._template(version, coinbase, rules);
|
||||
});
|
||||
|
||||
RPC.prototype._tmpl = co(function* _tmpl(version, coinbase, rules) {
|
||||
RPC.prototype._template = co(function* _template(version, coinbase, rules) {
|
||||
var unlock = yield this.locker.lock();
|
||||
try {
|
||||
return yield this.__template(version, coinbase, rules);
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
});
|
||||
|
||||
RPC.prototype.__template = co(function* _template(version, coinbase, rules) {
|
||||
var txs = [];
|
||||
var txIndex = {};
|
||||
var i, j, tx, deps, input, dep, block, output, raw, rwhash;
|
||||
var keys, vbavailable, vbrules, mutable, template, attempt;
|
||||
var attempt = yield this._getAttempt(false);
|
||||
var block = attempt.block;
|
||||
var i, j, tx, deps, input, dep, output, raw, rwhash;
|
||||
var keys, vbavailable, vbrules, mutable, template;
|
||||
var id, deployment, state;
|
||||
|
||||
try {
|
||||
attempt = yield this._getAttempt(false);
|
||||
} catch (e) {
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
|
||||
block = attempt.block;
|
||||
|
||||
for (i = 1; i < block.txs.length; i++) {
|
||||
tx = block.txs[i];
|
||||
txIndex[tx.hash('hex')] = i;
|
||||
}
|
||||
|
||||
for (i = 1; i < block.txs.length; i++) {
|
||||
tx = block.txs[i];
|
||||
deps = [];
|
||||
|
||||
for (j = 0; j < tx.inputs.length; j++) {
|
||||
input = tx.inputs[j];
|
||||
dep = txIndex[input.prevout.hash];
|
||||
if (dep != null)
|
||||
if (dep != null && deps.indexOf(dep) === -1) {
|
||||
assert(dep < i);
|
||||
deps.push(dep);
|
||||
}
|
||||
}
|
||||
|
||||
txs.push({
|
||||
@ -1600,10 +1611,8 @@ RPC.prototype._tmpl = co(function* _tmpl(version, coinbase, rules) {
|
||||
case constants.thresholdStates.ACTIVE:
|
||||
vbrules.push(id);
|
||||
if (rules) {
|
||||
if (rules.indexOf(id) === -1 && !deployment.force) {
|
||||
unlock();
|
||||
if (rules.indexOf(id) === -1 && !deployment.force)
|
||||
throw new RPCError('Client must support ' + id + '.');
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -1675,7 +1684,6 @@ RPC.prototype._tmpl = co(function* _tmpl(version, coinbase, rules) {
|
||||
template.default_witness_commitment = output.script.toJSON();
|
||||
}
|
||||
|
||||
unlock();
|
||||
return template;
|
||||
});
|
||||
|
||||
@ -1918,24 +1926,30 @@ RPC.prototype.setgenerate = co(function* setgenerate(args) {
|
||||
|
||||
RPC.prototype.generate = co(function* generate(args) {
|
||||
var unlock = yield this.locker.lock();
|
||||
try {
|
||||
return yield this._generate(args);
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
});
|
||||
|
||||
RPC.prototype._generate = co(function* generate(args) {
|
||||
var numblocks;
|
||||
|
||||
if (args.help || args.length < 1 || args.length > 2) {
|
||||
unlock();
|
||||
if (args.help || args.length < 1 || args.length > 2)
|
||||
throw new RPCError('generate numblocks ( maxtries )');
|
||||
}
|
||||
|
||||
numblocks = toNumber(args[0], 1);
|
||||
|
||||
return yield this._generate(numblocks);
|
||||
return yield this._generateBlocks(numblocks);
|
||||
});
|
||||
|
||||
RPC.prototype._generate = co(function* _generate(numblocks) {
|
||||
RPC.prototype._generateBlocks = co(function* _generateBlocks(blocks, address) {
|
||||
var hashes = [];
|
||||
var i, block;
|
||||
|
||||
for (i = 0; i < numblocks; i++) {
|
||||
block = yield this.miner.mineBlock();
|
||||
for (i = 0; i < blocks; i++) {
|
||||
block = yield this.miner.mineBlock(null, address);
|
||||
hashes.push(block.rhash);
|
||||
yield this.chain.add(block);
|
||||
}
|
||||
@ -1945,24 +1959,23 @@ RPC.prototype._generate = co(function* _generate(numblocks) {
|
||||
|
||||
RPC.prototype.generatetoaddress = co(function* generatetoaddress(args) {
|
||||
var unlock = yield this.locker.lock();
|
||||
var numblocks, address, hashes;
|
||||
|
||||
if (args.help || args.length < 2 || args.length > 3) {
|
||||
try {
|
||||
return yield this._generatetoaddress(args);
|
||||
} finally {
|
||||
unlock();
|
||||
throw new RPCError('generatetoaddress numblocks address ( maxtries )');
|
||||
}
|
||||
});
|
||||
|
||||
RPC.prototype._generatetoaddress = co(function* generatetoaddress(args) {
|
||||
var numblocks, address;
|
||||
|
||||
if (args.help || args.length < 2 || args.length > 3)
|
||||
throw new RPCError('generatetoaddress numblocks address ( maxtries )');
|
||||
|
||||
numblocks = toNumber(args[0], 1);
|
||||
address = this.miner.address;
|
||||
address = Address.fromBase58(toString(args[1]));
|
||||
|
||||
this.miner.address = Address.fromBase58(toString(args[1]));
|
||||
|
||||
hashes = yield this._generate(numblocks);
|
||||
|
||||
this.miner.address = address;
|
||||
|
||||
unlock();
|
||||
return hashes;
|
||||
return yield this._generateBlocks(numblocks, address);
|
||||
});
|
||||
|
||||
/*
|
||||
|
||||
@ -46,10 +46,6 @@ var MempoolEntry = require('./mempoolentry');
|
||||
* @property {Number} freeCount
|
||||
* @property {Number} lastTime
|
||||
* @property {Number} maxSize
|
||||
* @property {Boolean} blockSinceBump
|
||||
* @property {Number} lastFeeUpdate
|
||||
* @property {Rate} minRate
|
||||
* @property {Rate} minReasonable
|
||||
* @property {Rate} minRelayFee
|
||||
* @emits Mempool#open
|
||||
* @emits Mempool#error
|
||||
@ -99,7 +95,6 @@ function Mempool(options) {
|
||||
this.limitFree = this.options.limitFree !== false;
|
||||
this.limitFreeRelay = this.options.limitFreeRelay || 15;
|
||||
this.relayPriority = this.options.relayPriority !== false;
|
||||
this.rejectFee = this.options.rejectFee === true;
|
||||
this.requireStandard = this.options.requireStandard != null
|
||||
? this.options.requireStandard
|
||||
: this.network.requireStandard;
|
||||
@ -110,10 +105,6 @@ function Mempool(options) {
|
||||
|
||||
this.maxSize = options.maxSize || constants.mempool.MAX_MEMPOOL_SIZE;
|
||||
this.expiryTime = options.expiryTime || constants.mempool.MEMPOOL_EXPIRY;
|
||||
this.blockSinceBump = false;
|
||||
this.lastFeeUpdate = utils.now();
|
||||
this.minRate = 0;
|
||||
this.minReasonable = this.network.minRelay;
|
||||
this.minRelay = this.network.minRelay;
|
||||
}
|
||||
|
||||
@ -190,9 +181,6 @@ Mempool.prototype._addBlock = function addBlock(block) {
|
||||
entries.push(entry);
|
||||
}
|
||||
|
||||
this.blockSinceBump = true;
|
||||
this.lastFeeUpdate = utils.now();
|
||||
|
||||
if (this.fees)
|
||||
this.fees.processBlock(block.height, entries, this.chain.isFull());
|
||||
|
||||
@ -388,7 +376,7 @@ Mempool.prototype.isSpent = function isSpent(hash, index) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Get an output's spender transaction.
|
||||
* Get an output's spender entry.
|
||||
* @param {Hash} hash
|
||||
* @param {Number} index
|
||||
* @returns {MempoolEntry}
|
||||
@ -398,6 +386,22 @@ Mempool.prototype.getSpent = function getSpent(hash, index) {
|
||||
return this.spents[hash + index];
|
||||
};
|
||||
|
||||
/**
|
||||
* Get an output's spender transaction.
|
||||
* @param {Hash} hash
|
||||
* @param {Number} index
|
||||
* @returns {MempoolEntry}
|
||||
*/
|
||||
|
||||
Mempool.prototype.getSpentTX = function getSpentTX(hash, index) {
|
||||
var entry = this.spents[hash + index];
|
||||
|
||||
if (!entry)
|
||||
return;
|
||||
|
||||
return entry.tx;
|
||||
};
|
||||
|
||||
/**
|
||||
* Find all coins pertaining to a certain address.
|
||||
* @param {Address[]} addresses
|
||||
@ -788,7 +792,7 @@ Mempool.prototype._addUnchecked = co(function* addUnchecked(entry) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.logger.spam('Resolved orphan %s in mempool.', orphan.tx.rhash);
|
||||
this.logger.spam('Resolved orphan %s in mempool.', tx.rhash);
|
||||
}
|
||||
});
|
||||
|
||||
@ -800,9 +804,10 @@ Mempool.prototype._addUnchecked = co(function* addUnchecked(entry) {
|
||||
*/
|
||||
|
||||
Mempool.prototype.removeUnchecked = function removeUnchecked(entry, limit) {
|
||||
var rate, hash;
|
||||
var tx = entry.tx;
|
||||
var hash;
|
||||
|
||||
this.removeOrphan(entry.tx);
|
||||
this.removeOrphan(tx);
|
||||
|
||||
// We do not remove spenders if this is
|
||||
// being removed for a block. The spenders
|
||||
@ -810,66 +815,21 @@ Mempool.prototype.removeUnchecked = function removeUnchecked(entry, limit) {
|
||||
// now exist on the blockchain).
|
||||
if (limit) {
|
||||
this.removeSpenders(entry);
|
||||
this.logger.debug('Evicting %s from the mempool.', entry.tx.rhash);
|
||||
this.logger.debug('Evicting %s from the mempool.', tx.rhash);
|
||||
} else {
|
||||
this.logger.spam('Removing block tx %s from mempool.', tx.rhash);
|
||||
}
|
||||
|
||||
this.untrackEntry(entry);
|
||||
|
||||
if (this.fees) {
|
||||
hash = entry.tx.hash('hex');
|
||||
hash = tx.hash('hex');
|
||||
this.fees.removeTX(hash);
|
||||
}
|
||||
|
||||
if (limit) {
|
||||
this.logger.spam('Removed tx %s from mempool.', entry.tx.rhash);
|
||||
rate = TX.getRate(entry.sizes, entry.fees);
|
||||
rate += this.minReasonable;
|
||||
if (rate > this.minRate) {
|
||||
this.minRate = rate;
|
||||
this.blockSinceBump = false;
|
||||
}
|
||||
} else {
|
||||
this.logger.spam('Removed block tx %s from mempool.', entry.tx.rhash);
|
||||
}
|
||||
|
||||
this.emit('remove entry', entry);
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate and update the minimum rolling fee rate.
|
||||
* @returns {Rate} Rate.
|
||||
*/
|
||||
|
||||
Mempool.prototype.getMinRate = function getMinRate() {
|
||||
var now, halflife, size;
|
||||
|
||||
if (!this.blockSinceBump || this.minRate === 0)
|
||||
return this.minRate;
|
||||
|
||||
now = utils.now();
|
||||
|
||||
if (now > this.lastFeeUpdate + 10) {
|
||||
halflife = constants.mempool.FEE_HALFLIFE;
|
||||
size = this.getSize();
|
||||
|
||||
if (size < this.maxSize / 4)
|
||||
halflife >>>= 2;
|
||||
else if (size < this.maxSize / 2)
|
||||
halflife >>>= 1;
|
||||
|
||||
this.minRate /= Math.pow(2.0, (now - this.lastFeeUpdate) / halflife | 0);
|
||||
this.minRate |= 0;
|
||||
this.lastFeeUpdate = now;
|
||||
|
||||
if (this.minRate < this.minReasonable / 2) {
|
||||
this.minRate = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return Math.max(this.minRate, this.minReasonable);
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify a transaction with mempool standards.
|
||||
* @param {TX} tx
|
||||
@ -877,16 +837,15 @@ Mempool.prototype.getMinRate = function getMinRate() {
|
||||
*/
|
||||
|
||||
Mempool.prototype.verify = co(function* verify(entry) {
|
||||
var tx = entry.tx;
|
||||
var height = this.chain.height + 1;
|
||||
var lockFlags = flags.STANDARD_LOCKTIME_FLAGS;
|
||||
var flags1 = flags.STANDARD_VERIFY_FLAGS;
|
||||
var flags2 = flags1 & ~(flags.VERIFY_WITNESS | flags.VERIFY_CLEANSTACK);
|
||||
var flags3 = flags1 & ~flags.VERIFY_CLEANSTACK;
|
||||
var mandatory = flags.MANDATORY_VERIFY_FLAGS;
|
||||
var tx = entry.tx;
|
||||
var ret = new VerifyResult();
|
||||
var fee, modFee, now, size, minRate;
|
||||
var rejectFee, minRelayFee, count, result;
|
||||
var now, minFee, count, result;
|
||||
|
||||
result = yield this.checkLocks(tx, lockFlags);
|
||||
|
||||
@ -923,24 +882,9 @@ Mempool.prototype.verify = co(function* verify(entry) {
|
||||
0);
|
||||
}
|
||||
|
||||
fee = tx.getFee();
|
||||
modFee = entry.fees;
|
||||
size = entry.size;
|
||||
minFee = tx.getMinFee(entry.size, this.minRelay);
|
||||
|
||||
if (this.rejectFee) {
|
||||
minRate = this.getMinRate();
|
||||
rejectFee = tx.getMinFee(size, minRate);
|
||||
if (rejectFee > 0 && modFee < rejectFee) {
|
||||
throw new VerifyError(tx,
|
||||
'insufficientfee',
|
||||
'mempool min fee not met',
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
minRelayFee = tx.getMinFee(size, this.minRelay);
|
||||
|
||||
if (this.relayPriority && modFee < minRelayFee) {
|
||||
if (this.relayPriority && entry.fee < minFee) {
|
||||
if (!entry.isFree(height)) {
|
||||
throw new VerifyError(tx,
|
||||
'insufficientfee',
|
||||
@ -954,7 +898,7 @@ Mempool.prototype.verify = co(function* verify(entry) {
|
||||
// sending thousands of free transactions just to be
|
||||
// annoying or make others' transactions take longer
|
||||
// to confirm.
|
||||
if (this.limitFree && modFee < minRelayFee) {
|
||||
if (this.limitFree && entry.fee < minFee) {
|
||||
now = utils.now();
|
||||
|
||||
// Use an exponentially decaying ~10-minute window:
|
||||
@ -970,10 +914,10 @@ Mempool.prototype.verify = co(function* verify(entry) {
|
||||
0);
|
||||
}
|
||||
|
||||
this.freeCount += size;
|
||||
this.freeCount += entry.size;
|
||||
}
|
||||
|
||||
if (this.rejectAbsurdFees && fee > minRelayFee * 10000)
|
||||
if (this.rejectAbsurdFees && entry.fee > minFee * 10000)
|
||||
throw new VerifyError(tx, 'highfee', 'absurdly-high-fee', 0);
|
||||
|
||||
count = this.countAncestors(tx);
|
||||
@ -1087,18 +1031,19 @@ Mempool.prototype.checkInputs = co(function* checkInputs(tx, flags) {
|
||||
*/
|
||||
|
||||
Mempool.prototype.countAncestors = function countAncestors(tx) {
|
||||
return this._countAncestors(tx, {}, 0);
|
||||
return this._countAncestors(tx, 0, {});
|
||||
};
|
||||
|
||||
/**
|
||||
* Traverse ancestors and count.
|
||||
* @private
|
||||
* @param {TX} tx
|
||||
* @param {Object} set
|
||||
* @param {Number} count
|
||||
* @param {Object} set
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
Mempool.prototype._countAncestors = function countAncestors(tx, set, count) {
|
||||
Mempool.prototype._countAncestors = function countAncestors(tx, count, set) {
|
||||
var i, input, hash, prev;
|
||||
|
||||
for (i = 0; i < tx.inputs.length; i++) {
|
||||
@ -1118,7 +1063,7 @@ Mempool.prototype._countAncestors = function countAncestors(tx, set, count) {
|
||||
if (count > constants.mempool.ANCESTOR_LIMIT)
|
||||
break;
|
||||
|
||||
count = this._countAncestors(prev, set, count);
|
||||
count = this._countAncestors(prev, count, set);
|
||||
|
||||
if (count > constants.mempool.ANCESTOR_LIMIT)
|
||||
break;
|
||||
@ -1135,7 +1080,7 @@ Mempool.prototype._countAncestors = function countAncestors(tx, set, count) {
|
||||
*/
|
||||
|
||||
Mempool.prototype.countDescendants = function countDescendants(tx) {
|
||||
return this._countDescendants(tx, {}, 0);
|
||||
return this._countDescendants(tx, 0, {});
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1143,20 +1088,21 @@ Mempool.prototype.countDescendants = function countDescendants(tx) {
|
||||
* descendants a transaction may have.
|
||||
* @private
|
||||
* @param {TX} tx
|
||||
* @param {Number} count
|
||||
* @param {Object} set
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
Mempool.prototype._countDescendants = function countDescendants(tx, set, count) {
|
||||
Mempool.prototype._countDescendants = function countDescendants(tx, count, set) {
|
||||
var hash = tx.hash('hex');
|
||||
var i, entry, next, nhash;
|
||||
var i, next, nhash;
|
||||
|
||||
for (i = 0; i < tx.outputs.length; i++) {
|
||||
entry = this.getSpent(hash, i);
|
||||
next = this.getSpentTX(hash, i);
|
||||
|
||||
if (!entry)
|
||||
if (!next)
|
||||
continue;
|
||||
|
||||
next = entry.tx;
|
||||
nhash = next.hash('hex');
|
||||
|
||||
if (set[nhash])
|
||||
@ -1165,7 +1111,7 @@ Mempool.prototype._countDescendants = function countDescendants(tx, set, count)
|
||||
set[nhash] = true;
|
||||
count += 1;
|
||||
|
||||
count = this._countDescendants(next, set, count);
|
||||
count = this._countDescendants(next, count, set);
|
||||
}
|
||||
|
||||
return count;
|
||||
@ -1185,6 +1131,8 @@ Mempool.prototype.getAncestors = function getAncestors(tx) {
|
||||
* Get all transaction ancestors.
|
||||
* @private
|
||||
* @param {TX} tx
|
||||
* @param {MempoolEntry[]} entries
|
||||
* @param {Object} set
|
||||
* @returns {MempoolEntry[]}
|
||||
*/
|
||||
|
||||
@ -1224,20 +1172,21 @@ Mempool.prototype.getDescendants = function getDescendants(tx) {
|
||||
/**
|
||||
* Get all a transaction descendants.
|
||||
* @param {TX} tx
|
||||
* @param {MempoolEntry[]} entries
|
||||
* @param {Object} set
|
||||
* @returns {MempoolEntry[]}
|
||||
*/
|
||||
|
||||
Mempool.prototype._getDescendants = function getDescendants(tx, entries, set) {
|
||||
var hash = tx.hash('hex');
|
||||
var i, entry, next, nhash;
|
||||
var i, next, nhash;
|
||||
|
||||
for (i = 0; i < tx.outputs.length; i++) {
|
||||
entry = this.getSpent(hash, i);
|
||||
next = this.getSpentTX(hash, i);
|
||||
|
||||
if (!entry)
|
||||
if (!next)
|
||||
continue;
|
||||
|
||||
next = entry.tx;
|
||||
nhash = next.hash('hex');
|
||||
|
||||
if (set[nhash])
|
||||
|
||||
@ -19,18 +19,12 @@ var TX = require('../primitives/tx');
|
||||
* @param {Number} options.height - Entry height.
|
||||
* @param {Number} options.priority - Entry priority.
|
||||
* @param {Number} options.ts - Entry time.
|
||||
* @param {Amount} options.chainValue - Value of on-chain coins.
|
||||
* @param {Number} options.count - Number of descendants (includes tx).
|
||||
* @param {Number} options.size - TX and descendant modified size.
|
||||
* @param {Amount} options.fees - TX and descendant delta-applied fees.
|
||||
* @param {Amount} options.value - Value of on-chain coins.
|
||||
* @property {TX} tx
|
||||
* @property {Number} height
|
||||
* @property {Number} priority
|
||||
* @property {Number} ts
|
||||
* @property {Amount} chainValue
|
||||
* @property {Number} count
|
||||
* @property {Number} size
|
||||
* @property {Amount} fees
|
||||
* @property {Amount} value
|
||||
*/
|
||||
|
||||
function MempoolEntry(options) {
|
||||
@ -43,11 +37,7 @@ function MempoolEntry(options) {
|
||||
this.priority = 0;
|
||||
this.fee = 0;
|
||||
this.ts = 0;
|
||||
|
||||
this.chainValue = 0;
|
||||
this.count = 0;
|
||||
this.sizes = 0;
|
||||
this.fees = 0;
|
||||
this.value = 0;
|
||||
this.dependencies = false;
|
||||
|
||||
if (options)
|
||||
@ -67,13 +57,8 @@ MempoolEntry.prototype.fromOptions = function fromOptions(options) {
|
||||
this.priority = options.priority;
|
||||
this.fee = options.fee;
|
||||
this.ts = options.ts;
|
||||
|
||||
this.chainValue = options.chainValue;
|
||||
this.count = options.count;
|
||||
this.sizes = options.sizes;
|
||||
this.fees = options.fees;
|
||||
this.value = options.value;
|
||||
this.dependencies = options.dependencies;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
@ -100,10 +85,11 @@ MempoolEntry.prototype.fromTX = function fromTX(tx, height) {
|
||||
var dependencies = false;
|
||||
var size = tx.getVirtualSize();
|
||||
var fee = tx.getFee();
|
||||
var i;
|
||||
var i, input;
|
||||
|
||||
for (i = 0; i < tx.inputs.length; i++) {
|
||||
if (tx.inputs[i].coin.height === -1) {
|
||||
input = tx.inputs[i];
|
||||
if (input.coin.height === -1) {
|
||||
dependencies = true;
|
||||
break;
|
||||
}
|
||||
@ -114,11 +100,8 @@ MempoolEntry.prototype.fromTX = function fromTX(tx, height) {
|
||||
this.size = size;
|
||||
this.priority = priority;
|
||||
this.fee = fee;
|
||||
this.chainValue = value;
|
||||
this.ts = utils.now();
|
||||
this.count = 1;
|
||||
this.sizes = size;
|
||||
this.fees = fee;
|
||||
this.value = value;
|
||||
this.dependencies = dependencies;
|
||||
|
||||
return this;
|
||||
@ -146,7 +129,7 @@ MempoolEntry.fromTX = function fromTX(tx, height) {
|
||||
MempoolEntry.prototype.getPriority = function getPriority(height) {
|
||||
var heightDelta = height - this.height;
|
||||
var modSize = this.tx.getModifiedSize(this.size);
|
||||
var deltaPriority = (heightDelta * this.chainValue) / modSize;
|
||||
var deltaPriority = (heightDelta * this.value) / modSize;
|
||||
var result = this.priority + Math.floor(deltaPriority);
|
||||
if (result < 0)
|
||||
result = 0;
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
var utils = require('../utils/utils');
|
||||
var co = require('../utils/co');
|
||||
var assert = require('assert');
|
||||
var constants = require('../protocol/constants');
|
||||
var AsyncObject = require('../utils/async');
|
||||
var MinerBlock = require('./minerblock');
|
||||
var Address = require('../primitives/address');
|
||||
@ -48,10 +49,15 @@ function Miner(options) {
|
||||
this.since = 0;
|
||||
|
||||
this.version = -1;
|
||||
this.address = Address(options.address);
|
||||
this.addresses = [];
|
||||
this.coinbaseFlags = options.coinbaseFlags || 'mined by bcoin';
|
||||
|
||||
this._init();
|
||||
this.minWeight = 0;
|
||||
this.maxWeight = 750000 * 4;
|
||||
this.priorityWeight = 50000 * 4;
|
||||
this.minPriority = constants.tx.FREE_THRESHOLD;
|
||||
|
||||
this._init(options);
|
||||
}
|
||||
|
||||
utils.inherits(Miner, AsyncObject);
|
||||
@ -61,14 +67,23 @@ utils.inherits(Miner, AsyncObject);
|
||||
* @private
|
||||
*/
|
||||
|
||||
Miner.prototype._init = function _init() {
|
||||
Miner.prototype._init = function _init(options) {
|
||||
var self = this;
|
||||
var i;
|
||||
|
||||
if (options.address)
|
||||
this.addAddress(options.address);
|
||||
|
||||
if (options.addresses) {
|
||||
for (i = 0; i < options.addresses.length; i++)
|
||||
this.addAddress(options.addresses[i]);
|
||||
}
|
||||
|
||||
this.chain.on('tip', function(tip) {
|
||||
if (!self.attempt)
|
||||
return;
|
||||
|
||||
if (self.attempt.block.prevBlock !== tip.hash)
|
||||
if (self.attempt.block.prevBlock === tip.prevBlock)
|
||||
self.attempt.destroy();
|
||||
});
|
||||
|
||||
@ -239,9 +254,9 @@ Miner.prototype._onStop = function _onStop() {
|
||||
* @returns {Promise} - Returns {@link MinerBlock}.
|
||||
*/
|
||||
|
||||
Miner.prototype.createBlock = co(function* createBlock(tip) {
|
||||
Miner.prototype.createBlock = co(function* createBlock(tip, address) {
|
||||
var version = this.version;
|
||||
var ts, attempt, target, entries;
|
||||
var ts, attempt, target, locktime;
|
||||
|
||||
if (!tip)
|
||||
tip = this.chain.tip;
|
||||
@ -249,26 +264,32 @@ Miner.prototype.createBlock = co(function* createBlock(tip) {
|
||||
assert(tip);
|
||||
|
||||
ts = Math.max(time.now(), tip.ts + 1);
|
||||
locktime = ts;
|
||||
|
||||
target = yield this.chain.getTargetAsync(ts, tip);
|
||||
|
||||
if (version === -1)
|
||||
version = yield this.chain.computeBlockVersion(tip);
|
||||
|
||||
if (this.chain.state.hasMTP())
|
||||
locktime = yield tip.getMedianTimeAsync();
|
||||
|
||||
if (!address)
|
||||
address = this.getAddress();
|
||||
|
||||
attempt = new MinerBlock({
|
||||
tip: tip,
|
||||
version: version,
|
||||
bits: target,
|
||||
locktime: locktime,
|
||||
flags: this.chain.state.flags,
|
||||
address: this.address,
|
||||
address: address,
|
||||
coinbaseFlags: this.coinbaseFlags,
|
||||
witness: this.chain.state.hasWitness(),
|
||||
network: this.network
|
||||
});
|
||||
|
||||
entries = this.getSorted();
|
||||
|
||||
attempt.build(entries);
|
||||
this.fill(attempt);
|
||||
|
||||
return attempt;
|
||||
});
|
||||
@ -279,26 +300,11 @@ Miner.prototype.createBlock = co(function* createBlock(tip) {
|
||||
* @returns {Promise} - Returns [{@link Block}].
|
||||
*/
|
||||
|
||||
Miner.prototype.mineBlock = co(function* mineBlock(tip) {
|
||||
var attempt = yield this.createBlock(tip);
|
||||
Miner.prototype.mineBlock = co(function* mineBlock(tip, address) {
|
||||
var attempt = yield this.createBlock(tip, address);
|
||||
return yield attempt.mineAsync();
|
||||
});
|
||||
|
||||
/**
|
||||
* Add a transaction to the current block.
|
||||
* @param {TX} tx
|
||||
*/
|
||||
|
||||
Miner.prototype.addTX = function addTX(tx) {
|
||||
if (!this.running)
|
||||
return;
|
||||
|
||||
if (!this.attempt)
|
||||
return;
|
||||
|
||||
this.attempt.addTX(tx);
|
||||
};
|
||||
|
||||
/**
|
||||
* Notify the miner that a new tx has entered the mempool.
|
||||
* @param {MempoolEntry} entry
|
||||
@ -317,18 +323,37 @@ Miner.prototype.notifyEntry = function notifyEntry() {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Add an address to the address list.
|
||||
* @param {Address} address
|
||||
*/
|
||||
|
||||
Miner.prototype.addAddress = function addAddress(address) {
|
||||
this.addresses.push(Address(address));
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a random address from the address list.
|
||||
* @returns {Address}
|
||||
*/
|
||||
|
||||
Miner.prototype.getAddress = function getAddress() {
|
||||
assert(this.addresses.length !== 0, 'No address passed in for miner.');
|
||||
return this.addresses[Math.random() * this.addresses.length | 0];
|
||||
};
|
||||
|
||||
/**
|
||||
* Get mempool entries, sort by dependency order.
|
||||
* @returns {MempoolEntry[]}
|
||||
*/
|
||||
|
||||
Miner.prototype.getSorted = function getSorted() {
|
||||
Miner.prototype.fill = function fill(attempt) {
|
||||
var depMap = {};
|
||||
var count = {};
|
||||
var result = [];
|
||||
var top = [];
|
||||
var i, j, entry, tx, hash, input;
|
||||
var prev, hasDeps, deps, hashes;
|
||||
var block = attempt.block;
|
||||
var queue = new Queue(cmpPriority);
|
||||
var priority = true;
|
||||
var i, j, entry, item, tx, hash, input;
|
||||
var prev, deps, hashes, weight, sigops;
|
||||
|
||||
if (!this.mempool)
|
||||
return [];
|
||||
@ -338,10 +363,17 @@ Miner.prototype.getSorted = function getSorted() {
|
||||
for (i = 0; i < hashes.length; i++) {
|
||||
hash = hashes[i];
|
||||
entry = this.mempool.getEntry(hash);
|
||||
tx = entry.tx;
|
||||
item = new QueueItem(entry, attempt);
|
||||
tx = item.tx;
|
||||
|
||||
count[hash] = 0;
|
||||
hasDeps = false;
|
||||
if (tx.isCoinbase())
|
||||
throw new Error('Cannot add coinbase to block.');
|
||||
|
||||
if (!tx.hasCoins())
|
||||
throw new Error('Cannot add empty tx to block.');
|
||||
|
||||
if (!tx.isFinal(attempt.height, attempt.locktime))
|
||||
continue;
|
||||
|
||||
for (j = 0; j < tx.inputs.length; j++) {
|
||||
input = tx.inputs[j];
|
||||
@ -350,27 +382,57 @@ Miner.prototype.getSorted = function getSorted() {
|
||||
if (!this.mempool.hasTX(prev))
|
||||
continue;
|
||||
|
||||
hasDeps = true;
|
||||
item.depCount += 1;
|
||||
|
||||
if (!depMap[prev])
|
||||
depMap[prev] = [];
|
||||
|
||||
depMap[prev].push(entry);
|
||||
count[hash]++;
|
||||
depMap[prev].push(item);
|
||||
}
|
||||
|
||||
if (hasDeps)
|
||||
if (item.depCount > 0)
|
||||
continue;
|
||||
|
||||
top.push(entry);
|
||||
queue.push(item);
|
||||
}
|
||||
|
||||
for (i = 0; i < top.length; i++) {
|
||||
entry = top[i];
|
||||
tx = entry.tx;
|
||||
hash = tx.hash('hex');
|
||||
while (queue.size() > 0) {
|
||||
item = queue.pop();
|
||||
tx = item.tx;
|
||||
hash = item.hash;
|
||||
weight = attempt.weight;
|
||||
sigops = attempt.sigops;
|
||||
|
||||
result.push(entry);
|
||||
if (!attempt.witness && tx.hasWitness())
|
||||
continue;
|
||||
|
||||
weight += tx.getWeight();
|
||||
|
||||
if (weight > this.maxWeight)
|
||||
continue;
|
||||
|
||||
sigops += tx.getSigopsWeight(attempt.flags);
|
||||
|
||||
if (sigops > constants.block.MAX_SIGOPS_WEIGHT)
|
||||
continue;
|
||||
|
||||
if (priority) {
|
||||
if (weight > this.priorityWeight || item.priority < this.minPriority) {
|
||||
queue.cmp = cmpRate;
|
||||
priority = false;
|
||||
queue.push(item);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (item.free && weight >= this.minWeight)
|
||||
continue;
|
||||
}
|
||||
|
||||
attempt.weight = weight;
|
||||
attempt.sigops = sigops;
|
||||
attempt.fees += item.fee;
|
||||
|
||||
block.txs.push(tx.clone());
|
||||
|
||||
deps = depMap[hash];
|
||||
|
||||
@ -378,18 +440,67 @@ Miner.prototype.getSorted = function getSorted() {
|
||||
continue;
|
||||
|
||||
for (j = 0; j < deps.length; j++) {
|
||||
entry = deps[j];
|
||||
tx = entry.tx;
|
||||
hash = tx.hash('hex');
|
||||
|
||||
if (--count[hash] === 0)
|
||||
top.push(entry);
|
||||
item = deps[j];
|
||||
if (--item.depCount === 0)
|
||||
queue.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
attempt.updateCoinbase();
|
||||
attempt.updateMerkle();
|
||||
|
||||
assert(block.getWeight() <= attempt.weight);
|
||||
};
|
||||
|
||||
/**
|
||||
* QueueItem
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
function QueueItem(entry, attempt) {
|
||||
this.tx = entry.tx;
|
||||
this.hash = entry.tx.hash('hex');
|
||||
this.fee = entry.getFee();
|
||||
this.rate = entry.getRate();
|
||||
this.priority = entry.getPriority(attempt.height);
|
||||
this.free = entry.isFree(attempt.height);
|
||||
this.depCount = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
function Queue(cmp) {
|
||||
this.cmp = cmp;
|
||||
this.items = [];
|
||||
}
|
||||
|
||||
Queue.prototype.size = function size() {
|
||||
return this.items.length;
|
||||
};
|
||||
|
||||
Queue.prototype.push = function push(item) {
|
||||
utils.binaryInsert(this.items, item, this.cmp);
|
||||
};
|
||||
|
||||
Queue.prototype.pop = function pop() {
|
||||
return this.items.pop();
|
||||
};
|
||||
|
||||
/*
|
||||
* Helpers
|
||||
*/
|
||||
|
||||
function cmpPriority(a, b) {
|
||||
return a.priority - b.priority;
|
||||
}
|
||||
|
||||
function cmpRate(a, b) {
|
||||
return a.rate - b.rate;
|
||||
}
|
||||
|
||||
/*
|
||||
* Expose
|
||||
*/
|
||||
|
||||
@ -53,6 +53,7 @@ function MinerBlock(options) {
|
||||
this.height = options.tip.height + 1;
|
||||
this.bits = options.bits;
|
||||
this.target = utils.fromCompact(this.bits).toArrayLike(Buffer, 'le', 32);
|
||||
this.locktime = options.locktime;
|
||||
this.flags = options.flags;
|
||||
this.extraNonce = new BN(0);
|
||||
this.iterations = 0;
|
||||
@ -61,9 +62,11 @@ function MinerBlock(options) {
|
||||
this.address = options.address;
|
||||
this.network = Network.get(options.network);
|
||||
this.destroyed = false;
|
||||
this.reward = Block.reward(this.height, this.network);
|
||||
|
||||
this.sigops = 0;
|
||||
this.weight = 0;
|
||||
this.fees = 0;
|
||||
|
||||
this.coinbase = new TX();
|
||||
this.coinbase.mutable = true;
|
||||
@ -98,6 +101,7 @@ MinerBlock.prototype.__defineGetter__('rate', function() {
|
||||
*/
|
||||
|
||||
MinerBlock.prototype._init = function _init() {
|
||||
var scale = constants.WITNESS_SCALE_FACTOR;
|
||||
var block = this.block;
|
||||
var cb = this.coinbase;
|
||||
var input, output, hash, witnessNonce;
|
||||
@ -158,13 +162,25 @@ MinerBlock.prototype._init = function _init() {
|
||||
block.nonce = 0;
|
||||
block.height = this.height;
|
||||
|
||||
block.addTX(cb);
|
||||
block.txs.push(cb);
|
||||
|
||||
// Update coinbase since our coinbase was added.
|
||||
this.updateCoinbase();
|
||||
|
||||
// Create our merkle root.
|
||||
this.updateMerkle();
|
||||
|
||||
// Initialize weight.
|
||||
this.weight = this.block.getWeight();
|
||||
|
||||
// 4 extra bytes for varint tx count.
|
||||
this.weight += 4 * scale;
|
||||
|
||||
// 8 extra bytes for extra nonce.
|
||||
this.weight += 8 * scale;
|
||||
|
||||
// Initialize sigops weight.
|
||||
this.sigops = cb.getSigopsWeight(this.flags);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -197,7 +213,7 @@ MinerBlock.prototype.updateCoinbase = function updateCoinbase() {
|
||||
input.script.compile();
|
||||
|
||||
// Update reward.
|
||||
output.value = this.block.getReward(this.network);
|
||||
output.value = this.reward + this.fees;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -255,6 +271,9 @@ MinerBlock.prototype.addTX = function addTX(tx) {
|
||||
weight = tx.getWeight();
|
||||
sigops = tx.getSigopsWeight(this.flags);
|
||||
|
||||
if (!tx.isFinal(this.height, this.locktime))
|
||||
return false;
|
||||
|
||||
if (this.weight + weight > constants.block.MAX_WEIGHT)
|
||||
return false;
|
||||
|
||||
@ -264,6 +283,10 @@ MinerBlock.prototype.addTX = function addTX(tx) {
|
||||
if (!this.witness && tx.hasWitness())
|
||||
return false;
|
||||
|
||||
this.weight += weight;
|
||||
this.sigops += sigops;
|
||||
this.fees += tx.getFee();
|
||||
|
||||
// Add the tx to our block
|
||||
this.block.addTX(tx.clone());
|
||||
|
||||
@ -276,47 +299,6 @@ MinerBlock.prototype.addTX = function addTX(tx) {
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Fill the block with sorted mempool entries.
|
||||
* @param {MempoolEntry[]} entries
|
||||
*/
|
||||
|
||||
MinerBlock.prototype.build = function build(entries) {
|
||||
var len = Math.min(entries.length, constants.block.MAX_SIZE);
|
||||
var i, entry, tx, weight, sigops;
|
||||
|
||||
this.weight = this.block.getWeight() + 28;
|
||||
this.sigops = this.coinbase.getSigopsWeight(this.flags);
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
entry = entries[i];
|
||||
tx = entry.tx;
|
||||
|
||||
weight = tx.getWeight();
|
||||
sigops = tx.getSigopsWeight(this.flags);
|
||||
|
||||
if (this.weight + weight > constants.block.MAX_WEIGHT)
|
||||
break;
|
||||
|
||||
if (this.sigops + sigops > constants.block.MAX_SIGOPS_WEIGHT)
|
||||
break;
|
||||
|
||||
this.weight += weight;
|
||||
this.sigops += sigops;
|
||||
|
||||
// Add the tx to our block
|
||||
this.block.addTX(tx.clone());
|
||||
}
|
||||
|
||||
// Update coinbase value
|
||||
this.updateCoinbase();
|
||||
|
||||
// Update merkle root for new coinbase and new tx
|
||||
this.updateMerkle();
|
||||
|
||||
assert(this.block.getWeight() <= this.weight);
|
||||
};
|
||||
|
||||
/**
|
||||
* Hash until the nonce overflows.
|
||||
* @returns {Boolean} Whether the nonce was found.
|
||||
|
||||
@ -252,12 +252,8 @@ Node.prototype.openWallet = co(function* openWallet() {
|
||||
'Loaded wallet with id=%s wid=%d address=%s',
|
||||
wallet.id, wallet.wid, wallet.getAddress());
|
||||
|
||||
// Set the miner payout address if the
|
||||
// programmer didn't pass one in.
|
||||
if (this.miner) {
|
||||
if (!this.options.payoutAddress)
|
||||
this.miner.address = wallet.getAddress();
|
||||
}
|
||||
if (this.miner)
|
||||
this.miner.addAddress(wallet.getAddress());
|
||||
|
||||
this.wallet = wallet;
|
||||
});
|
||||
|
||||
@ -2081,6 +2081,82 @@ TX.getRate = function getRate(size, fee) {
|
||||
return Math.floor(fee * 1000 / size);
|
||||
};
|
||||
|
||||
/**
|
||||
* Sort an array of transactions in dependency order.
|
||||
* @param {TX[]} txs
|
||||
* @returns {TX[]}
|
||||
*/
|
||||
|
||||
TX.sort = function sort(txs) {
|
||||
var depMap = {};
|
||||
var count = {};
|
||||
var result = [];
|
||||
var top = [];
|
||||
var map = txs;
|
||||
var i, j, tx, hash, input;
|
||||
var prev, hasDeps, deps;
|
||||
|
||||
if (Array.isArray(txs)) {
|
||||
map = {};
|
||||
for (i = 0; i < txs.length; i++) {
|
||||
tx = txs[i];
|
||||
hash = tx.hash('hex');
|
||||
map[hash] = tx;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < txs.length; i++) {
|
||||
tx = txs[i];
|
||||
hash = tx.hash('hex');
|
||||
hasDeps = false;
|
||||
|
||||
count[hash] = 0;
|
||||
|
||||
for (j = 0; j < tx.inputs.length; j++) {
|
||||
input = tx.inputs[j];
|
||||
prev = input.prevout.hash;
|
||||
|
||||
if (!map[prev])
|
||||
continue;
|
||||
|
||||
count[hash] += 1;
|
||||
hasDeps = true;
|
||||
|
||||
if (!depMap[prev])
|
||||
depMap[prev] = [];
|
||||
|
||||
depMap[prev].push(tx);
|
||||
}
|
||||
|
||||
if (hasDeps)
|
||||
continue;
|
||||
|
||||
top.push(tx);
|
||||
}
|
||||
|
||||
for (i = 0; i < top.length; i++) {
|
||||
tx = top[i];
|
||||
hash = tx.hash('hex');
|
||||
|
||||
result.push(tx);
|
||||
|
||||
deps = depMap[hash];
|
||||
|
||||
if (!deps)
|
||||
continue;
|
||||
|
||||
for (j = 0; j < deps.length; j++) {
|
||||
tx = deps[j];
|
||||
hash = tx.hash('hex');
|
||||
|
||||
if (--count[hash] === 0)
|
||||
top.push(tx);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Inspect the transaction and return a more
|
||||
* user-friendly representation of the data.
|
||||
|
||||
@ -20,6 +20,7 @@ var BufferWriter = require('../utils/writer');
|
||||
var TXDB = require('./txdb');
|
||||
var Path = require('./path');
|
||||
var Address = require('../primitives/address');
|
||||
var TX = require('../primitives/tx');
|
||||
var MTX = require('../primitives/mtx');
|
||||
var WalletKey = require('./walletkey');
|
||||
var HD = require('../hd/hd');
|
||||
@ -1650,6 +1651,8 @@ Wallet.prototype.resend = co(function* resend() {
|
||||
if (txs.length > 0)
|
||||
this.logger.info('Rebroadcasting %d transactions.', txs.length);
|
||||
|
||||
txs = TX.sort(txs);
|
||||
|
||||
for (i = 0; i < txs.length; i++)
|
||||
yield this.db.send(txs[i]);
|
||||
|
||||
|
||||
@ -1403,6 +1403,7 @@ WalletDB.prototype.getPendingTX = co(function* getPendingTX() {
|
||||
|
||||
WalletDB.prototype.resend = co(function* resend() {
|
||||
var keys = yield this.getPendingTX();
|
||||
var txs = [];
|
||||
var i, key, data, tx;
|
||||
|
||||
if (keys.length > 0)
|
||||
@ -1420,8 +1421,13 @@ WalletDB.prototype.resend = co(function* resend() {
|
||||
if (tx.isCoinbase())
|
||||
continue;
|
||||
|
||||
yield this.send(tx);
|
||||
txs.push(tx);
|
||||
}
|
||||
|
||||
txs = TX.sort(txs);
|
||||
|
||||
for (i = 0; i < txs.length; i++)
|
||||
yield this.send(txs[i]);
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@ -79,7 +79,8 @@ describe('Chain', function() {
|
||||
|
||||
it('should open walletdb', cob(function* () {
|
||||
wallet = yield walletdb.create();
|
||||
miner.address = wallet.getAddress();
|
||||
miner.addresses.length = 0;
|
||||
miner.addAddress(wallet.getAddress());
|
||||
}));
|
||||
|
||||
it('should mine a block', cob(function* () {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user