mempool work. accurate reject messages.
This commit is contained in:
parent
37c488c802
commit
6194c66d8f
@ -377,7 +377,8 @@ Chain.prototype._verify = function _verify(block, prev, peer, callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!block.verify(ret)) {
|
if (!block.verify(ret)) {
|
||||||
self.emit('verify-error', block, ret.reason, ret.score, peer);
|
self.emit('verify-error',
|
||||||
|
block, 'invalid', ret.reason, ret.score, peer);
|
||||||
return done(null, false);
|
return done(null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -401,13 +402,15 @@ Chain.prototype._verify = function _verify(block, prev, peer, callback) {
|
|||||||
// Ensure the timestamp is correct
|
// Ensure the timestamp is correct
|
||||||
if (block.ts <= medianTime) {
|
if (block.ts <= medianTime) {
|
||||||
utils.debug('Block time is lower than median: %s', block.rhash);
|
utils.debug('Block time is lower than median: %s', block.rhash);
|
||||||
self.emit('verify-error', block, 'time-too-old', 0, peer);
|
self.emit('verify-error',
|
||||||
|
block, 'invalid', 'time-too-old', 0, peer);
|
||||||
return done(null, false);
|
return done(null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (block.bits !== self.getTarget(prev, block)) {
|
if (block.bits !== self.getTarget(prev, block)) {
|
||||||
utils.debug('Block is using wrong target: %s', block.rhash);
|
utils.debug('Block is using wrong target: %s', block.rhash);
|
||||||
self.emit('verify-error', block, 'bad-diffbits', 100, peer);
|
self.emit('verify-error',
|
||||||
|
block, 'invalid', 'bad-diffbits', 100, peer);
|
||||||
return done(null, false);
|
return done(null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -426,7 +429,8 @@ Chain.prototype._verify = function _verify(block, prev, peer, callback) {
|
|||||||
// once the majority of blocks are using it.
|
// once the majority of blocks are using it.
|
||||||
if (block.version < 2 && prev.isOutdated(2)) {
|
if (block.version < 2 && prev.isOutdated(2)) {
|
||||||
utils.debug('Block is outdated (v2): %s', block.rhash);
|
utils.debug('Block is outdated (v2): %s', block.rhash);
|
||||||
self.emit('verify-error', block, 'bad-version', 0, peer);
|
self.emit('verify-error',
|
||||||
|
block, 'obsolete', 'bad-version', 0, peer);
|
||||||
return done(null, false);
|
return done(null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -434,7 +438,8 @@ Chain.prototype._verify = function _verify(block, prev, peer, callback) {
|
|||||||
// once the majority of blocks are using it.
|
// once the majority of blocks are using it.
|
||||||
if (block.version < 3 && prev.isOutdated(3)) {
|
if (block.version < 3 && prev.isOutdated(3)) {
|
||||||
utils.debug('Block is outdated (v3): %s', block.rhash);
|
utils.debug('Block is outdated (v3): %s', block.rhash);
|
||||||
self.emit('verify-error', block, 'bad-version', 0, peer);
|
self.emit('verify-error',
|
||||||
|
block, 'obsolete', 'bad-version', 0, peer);
|
||||||
return done(null, false);
|
return done(null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -442,7 +447,8 @@ Chain.prototype._verify = function _verify(block, prev, peer, callback) {
|
|||||||
// once the majority of blocks are using it.
|
// once the majority of blocks are using it.
|
||||||
if (block.version < 4 && prev.isOutdated(4)) {
|
if (block.version < 4 && prev.isOutdated(4)) {
|
||||||
utils.debug('Block is outdated (v4): %s', block.rhash);
|
utils.debug('Block is outdated (v4): %s', block.rhash);
|
||||||
self.emit('verify-error', block, 'bad-version', 0, peer);
|
self.emit('verify-error',
|
||||||
|
block, 'obsolete', 'bad-version', 0, peer);
|
||||||
return done(null, false);
|
return done(null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -451,7 +457,8 @@ Chain.prototype._verify = function _verify(block, prev, peer, callback) {
|
|||||||
if (network.segwitHeight !== -1 && height >= network.segwitHeight) {
|
if (network.segwitHeight !== -1 && height >= network.segwitHeight) {
|
||||||
if (block.version < 5 && prev.isOutdated(5)) {
|
if (block.version < 5 && prev.isOutdated(5)) {
|
||||||
utils.debug('Block is outdated (v5): %s', block.rhash);
|
utils.debug('Block is outdated (v5): %s', block.rhash);
|
||||||
self.emit('verify-error', block, 'bad-version', 0, peer);
|
self.emit('verify-error',
|
||||||
|
block, 'obsolete', 'bad-version', 0, peer);
|
||||||
return done(null, false);
|
return done(null, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -493,7 +500,8 @@ Chain.prototype._verify = function _verify(block, prev, peer, callback) {
|
|||||||
if (coinbaseHeight) {
|
if (coinbaseHeight) {
|
||||||
if (block.getCoinbaseHeight() !== height) {
|
if (block.getCoinbaseHeight() !== height) {
|
||||||
utils.debug('Block has bad coinbase height: %s', block.rhash);
|
utils.debug('Block has bad coinbase height: %s', block.rhash);
|
||||||
self.emit('verify-error', block, 'bad-cb-height', 100, peer);
|
self.emit('verify-error',
|
||||||
|
block, 'invalid', 'bad-cb-height', 100, peer);
|
||||||
return done(null, false);
|
return done(null, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -501,13 +509,15 @@ Chain.prototype._verify = function _verify(block, prev, peer, callback) {
|
|||||||
if (block.version >= 5 && segwit) {
|
if (block.version >= 5 && segwit) {
|
||||||
if (block.commitmentHash !== block.getCommitmentHash()) {
|
if (block.commitmentHash !== block.getCommitmentHash()) {
|
||||||
utils.debug('Block failed witnessroot test: %s', block.rhash);
|
utils.debug('Block failed witnessroot test: %s', block.rhash);
|
||||||
self.emit('verify-error', block, 'bad-blk-wit-length', 100, peer);
|
self.emit('verify-error',
|
||||||
|
block, 'invalid', 'bad-blk-wit-length', 100, peer);
|
||||||
return done(null, false);
|
return done(null, false);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (block.hasWitness()) {
|
if (block.hasWitness()) {
|
||||||
utils.debug('Unexpected witness data found: %s', block.rhash);
|
utils.debug('Unexpected witness data found: %s', block.rhash);
|
||||||
self.emit('verify-error', block, 'unexpected-witness', 100, peer);
|
self.emit('verify-error',
|
||||||
|
block, 'invalid', 'unexpected-witness', 100, peer);
|
||||||
return done(null, false);
|
return done(null, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -523,7 +533,8 @@ Chain.prototype._verify = function _verify(block, prev, peer, callback) {
|
|||||||
// regards to nSequence and nLockTime.
|
// regards to nSequence and nLockTime.
|
||||||
if (!tx.isFinal(height, ts)) {
|
if (!tx.isFinal(height, ts)) {
|
||||||
utils.debug('TX is not final: %s (%s)', block.rhash, i);
|
utils.debug('TX is not final: %s (%s)', block.rhash, i);
|
||||||
self.emit('verify-error', block, 'bad-txns-nonfinal', 10, peer);
|
self.emit('verify-error',
|
||||||
|
block, 'invalid', 'bad-txns-nonfinal', 10, peer);
|
||||||
return done(null, false);
|
return done(null, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -557,7 +568,8 @@ Chain.prototype._checkDuplicates = function _checkDuplicates(block, prev, peer,
|
|||||||
if (result) {
|
if (result) {
|
||||||
utils.debug('Block is overwriting txids: %s', block.rhash);
|
utils.debug('Block is overwriting txids: %s', block.rhash);
|
||||||
if (!(network.type === 'main' && (height === 91842 || height === 91880))) {
|
if (!(network.type === 'main' && (height === 91842 || height === 91880))) {
|
||||||
self.emit('verify-error', block, 'bad-txns-BIP30', 100, peer);
|
self.emit('verify-error',
|
||||||
|
block, 'invalid', 'bad-txns-BIP30', 100, peer);
|
||||||
return next(null, false);
|
return next(null, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -606,7 +618,8 @@ Chain.prototype._checkInputs = function _checkInputs(block, prev, flags, peer, c
|
|||||||
|
|
||||||
if (sigops > constants.script.maxBlockSigops) {
|
if (sigops > constants.script.maxBlockSigops) {
|
||||||
utils.debug('Block has too many sigops: %s', block.rhash);
|
utils.debug('Block has too many sigops: %s', block.rhash);
|
||||||
self.emit('verify-error', block, 'bad-blk-sigops', 100, peer);
|
self.emit('verify-error',
|
||||||
|
block, 'invalid', 'bad-blk-sigops', 100, peer);
|
||||||
return callback(null, false);
|
return callback(null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -629,7 +642,8 @@ Chain.prototype._checkInputs = function _checkInputs(block, prev, flags, peer, c
|
|||||||
utils.revHex(input.prevout.hash) + '/' + input.prevout.index);
|
utils.revHex(input.prevout.hash) + '/' + input.prevout.index);
|
||||||
if (height < network.checkpoints.lastHeight)
|
if (height < network.checkpoints.lastHeight)
|
||||||
throw new Error('BUG: Spent inputs in historical data!');
|
throw new Error('BUG: Spent inputs in historical data!');
|
||||||
self.emit('verify-error', block, 'bad-txns-inputs-missingorspent', 100, peer);
|
self.emit('verify-error',
|
||||||
|
block, 'invalid', 'bad-txns-inputs-missingorspent', 100, peer);
|
||||||
return callback(null, false);
|
return callback(null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -682,7 +696,8 @@ Chain.prototype._checkReward = function _checkReward(block) {
|
|||||||
actual.iadd(block.txs[i].getFee());
|
actual.iadd(block.txs[i].getFee());
|
||||||
|
|
||||||
if (claimed.cmp(actual) > 0) {
|
if (claimed.cmp(actual) > 0) {
|
||||||
self.emit('verify-error', block, 'bad-cb-amount', 100, peer);
|
self.emit('verify-error',
|
||||||
|
block, 'invalid', 'bad-cb-amount', 100, peer);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -974,7 +989,8 @@ Chain.prototype.add = function add(initial, peer, callback, force) {
|
|||||||
chain: !!self.invalid[prevHash]
|
chain: !!self.invalid[prevHash]
|
||||||
}, peer);
|
}, peer);
|
||||||
self.invalid[hash] = true;
|
self.invalid[hash] = true;
|
||||||
self.emit('verify-error', block, 'duplicate', 0, peer);
|
self.emit('verify-error',
|
||||||
|
block, 'duplicate', 'duplicate', 0, peer);
|
||||||
return done();
|
return done();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -990,7 +1006,8 @@ Chain.prototype.add = function add(initial, peer, callback, force) {
|
|||||||
seen: false,
|
seen: false,
|
||||||
chain: false
|
chain: false
|
||||||
}, peer);
|
}, peer);
|
||||||
self.emit('verify-error', block, ret.reason, ret.score, peer);
|
self.emit('verify-error',
|
||||||
|
block, 'invalid', ret.reason, ret.score, peer);
|
||||||
return done();
|
return done();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1024,7 +1041,8 @@ Chain.prototype.add = function add(initial, peer, callback, force) {
|
|||||||
seen: true
|
seen: true
|
||||||
}, peer);
|
}, peer);
|
||||||
|
|
||||||
self.emit('verify-error', block, 'bad-prevblk', 0, peer);
|
self.emit('verify-error',
|
||||||
|
block, 'invalid', 'bad-prevblk', 0, peer);
|
||||||
|
|
||||||
return done();
|
return done();
|
||||||
}
|
}
|
||||||
@ -1048,7 +1066,8 @@ Chain.prototype.add = function add(initial, peer, callback, force) {
|
|||||||
hash: hash,
|
hash: hash,
|
||||||
seen: false
|
seen: false
|
||||||
}, peer);
|
}, peer);
|
||||||
self.emit('verify-error', block, 'bad-prevblk', 0, peer);
|
self.emit('verify-error',
|
||||||
|
block, 'invalid', 'bad-prevblk', 0, peer);
|
||||||
return done();
|
return done();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1078,7 +1097,8 @@ Chain.prototype.add = function add(initial, peer, callback, force) {
|
|||||||
checkpoint: true
|
checkpoint: true
|
||||||
}, peer);
|
}, peer);
|
||||||
|
|
||||||
self.emit('verify-error', block, 'checkpoint mismatch', 100, peer);
|
self.emit('verify-error',
|
||||||
|
block, 'checkpoint', 'checkpoint mismatch', 100, peer);
|
||||||
|
|
||||||
return done();
|
return done();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,34 +10,17 @@ var utils = bcoin.utils;
|
|||||||
var network = bcoin.protocol.network;
|
var network = bcoin.protocol.network;
|
||||||
var db = {};
|
var db = {};
|
||||||
|
|
||||||
module.exports = function ldb(name, options) {
|
/**
|
||||||
var file = bcoin.prefix + '/' + name + '-' + network.type + '.db';
|
* LDB
|
||||||
var backend = typeof options.db === 'string'
|
*/
|
||||||
? options.db
|
|
||||||
: process.env.BCOIN_DB;
|
function ldb(name, options) {
|
||||||
|
var file = getLocation(name);
|
||||||
|
|
||||||
if (!db[file]) {
|
if (!db[file]) {
|
||||||
if (!options)
|
if (!options)
|
||||||
options = {};
|
options = {};
|
||||||
|
|
||||||
if (!backend || backend === 'leveldb')
|
|
||||||
backend = 'leveldown';
|
|
||||||
else if (backend === 'rocksdb')
|
|
||||||
backend = 'rocksdown';
|
|
||||||
else if (backend === 'lmdb')
|
|
||||||
backend = 'lmdb';
|
|
||||||
else if (backend === 'memory')
|
|
||||||
backend = 'memdown';
|
|
||||||
|
|
||||||
if (bcoin.isBrowser && backend !== 'memdown') {
|
|
||||||
backend = require('level-js');
|
|
||||||
} else {
|
|
||||||
if (backend !== 'memdown')
|
|
||||||
bcoin.ensurePrefix();
|
|
||||||
|
|
||||||
backend = require(backend);
|
|
||||||
}
|
|
||||||
|
|
||||||
db[file] = new LowlevelUp(file, {
|
db[file] = new LowlevelUp(file, {
|
||||||
keyEncoding: 'ascii',
|
keyEncoding: 'ascii',
|
||||||
valueEncoding: 'binary',
|
valueEncoding: 'binary',
|
||||||
@ -59,12 +42,57 @@ module.exports = function ldb(name, options) {
|
|||||||
// optimizeCompaction: 'level',
|
// optimizeCompaction: 'level',
|
||||||
// memtableBudget: 512 << 20,
|
// memtableBudget: 512 << 20,
|
||||||
|
|
||||||
db: backend
|
db: getBackend(options.db)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return db[file];
|
return db[file];
|
||||||
};
|
}
|
||||||
|
|
||||||
|
function getLocation(name) {
|
||||||
|
return bcoin.prefix + '/' + name + '-' + network.type + '.db';
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBackend(backend) {
|
||||||
|
if (typeof backend !== 'string')
|
||||||
|
backend = process.env.BCOIN_DB;
|
||||||
|
|
||||||
|
if (!backend || backend === 'leveldb')
|
||||||
|
backend = 'leveldown';
|
||||||
|
else if (backend === 'rocksdb')
|
||||||
|
backend = 'rocksdown';
|
||||||
|
else if (backend === 'lmdb')
|
||||||
|
backend = 'lmdb';
|
||||||
|
else if (backend === 'memory')
|
||||||
|
backend = 'memdown';
|
||||||
|
|
||||||
|
// Require directly for browserify
|
||||||
|
if (backend === 'memdown')
|
||||||
|
return require('memdown');
|
||||||
|
|
||||||
|
if (bcoin.isBrowser)
|
||||||
|
return require('level-js');
|
||||||
|
|
||||||
|
bcoin.ensurePrefix();
|
||||||
|
|
||||||
|
return require(backend);
|
||||||
|
}
|
||||||
|
|
||||||
|
function destroy(name, backend, callback) {
|
||||||
|
var file = getLocation(name);
|
||||||
|
|
||||||
|
if (!callback) {
|
||||||
|
callback = backend;
|
||||||
|
backend = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
backend = getBackend(backend);
|
||||||
|
|
||||||
|
if (!backend.destroy)
|
||||||
|
return utils.nextTick(callback);
|
||||||
|
|
||||||
|
backend.destroy(file, callback);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* LowlevelUp
|
* LowlevelUp
|
||||||
@ -168,3 +196,13 @@ LowlevelUp.prototype.getProperty = function getProperty(name) {
|
|||||||
LowlevelUp.prototype.approximateSize = function approximateSize(start, end, callback) {
|
LowlevelUp.prototype.approximateSize = function approximateSize(start, end, callback) {
|
||||||
return this.binding.approximateSize(start, end, callback);
|
return this.binding.approximateSize(start, end, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expose
|
||||||
|
*/
|
||||||
|
|
||||||
|
exports = ldb;
|
||||||
|
exports.LowlevelUp = LowlevelUp;
|
||||||
|
exports.destroy = destroy;
|
||||||
|
|
||||||
|
module.exports = exports;
|
||||||
|
|||||||
@ -9,6 +9,7 @@ var EventEmitter = require('events').EventEmitter;
|
|||||||
var bcoin = require('../bcoin');
|
var bcoin = require('../bcoin');
|
||||||
var bn = require('bn.js');
|
var bn = require('bn.js');
|
||||||
var constants = bcoin.protocol.constants;
|
var constants = bcoin.protocol.constants;
|
||||||
|
var network = bcoin.protocol.network;
|
||||||
var utils = require('./utils');
|
var utils = require('./utils');
|
||||||
var assert = utils.assert;
|
var assert = utils.assert;
|
||||||
var BufferWriter = require('./writer');
|
var BufferWriter = require('./writer');
|
||||||
@ -32,34 +33,20 @@ function Mempool(node, options) {
|
|||||||
this.node = node;
|
this.node = node;
|
||||||
this.chain = node.chain;
|
this.chain = node.chain;
|
||||||
|
|
||||||
this.db = bcoin.ldb('mempool', {
|
|
||||||
db: 'memdown'
|
|
||||||
});
|
|
||||||
|
|
||||||
this.tx = new bcoin.txdb('m', this.db, {
|
|
||||||
indexSpent: true,
|
|
||||||
indexExtra: false,
|
|
||||||
indexAddress: false,
|
|
||||||
mapAddress: false,
|
|
||||||
verify: false
|
|
||||||
});
|
|
||||||
|
|
||||||
this.loaded = false;
|
this.loaded = false;
|
||||||
|
|
||||||
this.jobs = [];
|
this.locker = new bcoin.locker(this, this.add, 20 << 20);
|
||||||
this.busy = false;
|
|
||||||
|
|
||||||
this.pending = [];
|
this.db = null;
|
||||||
this.pendingTX = {};
|
this.tx = null;
|
||||||
this.pendingSize = 0;
|
this.size = 0;
|
||||||
this.pendingLimit = 20 << 20;
|
|
||||||
this.locker = new bcoin.locker(this, this.add, this.pendingLimit);
|
|
||||||
|
|
||||||
this.freeCount = 0;
|
this.freeCount = 0;
|
||||||
this.lastTime = 0;
|
this.lastTime = 0;
|
||||||
|
|
||||||
this.limitFree = this.options.limitFree !== false;
|
this.limitFree = this.options.limitFree !== false;
|
||||||
this.limitFreeRelay = this.options.limitFreeRelay || 15;
|
this.limitFreeRelay = this.options.limitFreeRelay || 15;
|
||||||
|
this.relayPriority = this.options.relayPriority !== false;
|
||||||
this.requireStandard = this.options.requireStandard !== false;
|
this.requireStandard = this.options.requireStandard !== false;
|
||||||
this.rejectInsaneFees = this.options.rejectInsaneFees !== false;
|
this.rejectInsaneFees = this.options.rejectInsaneFees !== false;
|
||||||
|
|
||||||
@ -73,6 +60,10 @@ utils.inherits(Mempool, EventEmitter);
|
|||||||
Mempool.flags = constants.flags.STANDARD_VERIFY_FLAGS;
|
Mempool.flags = constants.flags.STANDARD_VERIFY_FLAGS;
|
||||||
Mempool.mandatory = constants.flags.MANDATORY_VERIFY_FLAGS;
|
Mempool.mandatory = constants.flags.MANDATORY_VERIFY_FLAGS;
|
||||||
|
|
||||||
|
Mempool.ANCESTOR_LIMIT = 25;
|
||||||
|
Mempool.MAX_MEMPOOL_SIZE = 300 << 20;
|
||||||
|
Mempool.MEMPOOL_EXPIRY = 72 * 60 * 60;
|
||||||
|
|
||||||
Mempool.prototype._lock = function _lock(func, args, force) {
|
Mempool.prototype._lock = function _lock(func, args, force) {
|
||||||
return this.locker.lock(func, args, force);
|
return this.locker.lock(func, args, force);
|
||||||
};
|
};
|
||||||
@ -83,20 +74,53 @@ Mempool.prototype.purgePending = function purgePending() {
|
|||||||
|
|
||||||
Mempool.prototype._init = function _init() {
|
Mempool.prototype._init = function _init() {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
var unlock = this._lock(utils.nop, []);
|
||||||
|
|
||||||
if (this.db.loaded) {
|
bcoin.ldb.destroy('mempool', 'memdown', function(err) {
|
||||||
this.loaded = true;
|
if (err) {
|
||||||
return;
|
unlock();
|
||||||
}
|
return self.emit('error', err);
|
||||||
|
}
|
||||||
|
|
||||||
this.db.once('open', function() {
|
self.db = bcoin.ldb('mempool', {
|
||||||
self.loaded = true;
|
db: 'memdown'
|
||||||
self.emit('open');
|
});
|
||||||
|
|
||||||
|
self.tx = new bcoin.txdb('m', self.db, {
|
||||||
|
indexExtra: false,
|
||||||
|
indexAddress: false,
|
||||||
|
mapAddress: false,
|
||||||
|
verify: false
|
||||||
|
});
|
||||||
|
|
||||||
|
self.db.open(function(err) {
|
||||||
|
if (err) {
|
||||||
|
unlock();
|
||||||
|
return self.emit('error', err);
|
||||||
|
}
|
||||||
|
self.dynamicMemoryUsage(function(err, size) {
|
||||||
|
if (err)
|
||||||
|
self.emit('error', err);
|
||||||
|
else
|
||||||
|
self.size = size;
|
||||||
|
|
||||||
|
unlock();
|
||||||
|
self.loaded = true;
|
||||||
|
self.emit('open');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Mempool.prototype.dynamicMemoryUsage = function dynamicMemoryUsage(callback) {
|
||||||
|
return this.db.approximateSize('m', 'm~', callback);
|
||||||
|
};
|
||||||
|
|
||||||
Mempool.prototype.open = function open(callback) {
|
Mempool.prototype.open = function open(callback) {
|
||||||
return this.db.open(callback);
|
if (this.loaded)
|
||||||
|
return utils.nextTick(callback);
|
||||||
|
|
||||||
|
return this.once('open', callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
Mempool.prototype.addBlock = function addBlock(block, callback, force) {
|
Mempool.prototype.addBlock = function addBlock(block, callback, force) {
|
||||||
@ -107,14 +131,9 @@ Mempool.prototype.addBlock = function addBlock(block, callback, force) {
|
|||||||
|
|
||||||
callback = utils.wrap(callback, unlock);
|
callback = utils.wrap(callback, unlock);
|
||||||
|
|
||||||
this.open(function(err) {
|
utils.forEachSerial(block.txs, function(tx, next) {
|
||||||
if (err)
|
self.removeUnchecked(tx, next);
|
||||||
return callback(err);
|
}, callback);
|
||||||
|
|
||||||
utils.forEachSerial(block.txs, function(tx, next) {
|
|
||||||
self.tx.removeUnchecked(tx, next);
|
|
||||||
}, callback);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Mempool.prototype.removeBlock = function removeBlock(block, callback, force) {
|
Mempool.prototype.removeBlock = function removeBlock(block, callback, force) {
|
||||||
@ -126,10 +145,91 @@ Mempool.prototype.removeBlock = function removeBlock(block, callback, force) {
|
|||||||
callback = utils.wrap(callback, unlock);
|
callback = utils.wrap(callback, unlock);
|
||||||
|
|
||||||
utils.forEachSerial(block.txs.slice().reverse(), function(tx, next) {
|
utils.forEachSerial(block.txs.slice().reverse(), function(tx, next) {
|
||||||
self.tx.addUnchecked(tx, next);
|
self.addUnchecked(tx, next);
|
||||||
}, callback);
|
}, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Mempool.prototype.limitMempoolSize = function limitMempoolSize(callback) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
if (this.size <= Mempool.MAX_MEMPOOL_SIZE)
|
||||||
|
return callback(null, true);
|
||||||
|
|
||||||
|
this.db.getRange({
|
||||||
|
start: 0,
|
||||||
|
end: utils.now() - Mempool.MEMPOOL_EXPIRY
|
||||||
|
}, function(err, txs) {
|
||||||
|
if (err)
|
||||||
|
return callback(err);
|
||||||
|
|
||||||
|
utils.forEachSerial(function(tx, next) {
|
||||||
|
self.removeUnchecked(tx, next);
|
||||||
|
}, function(err) {
|
||||||
|
if (err)
|
||||||
|
return callback(err);
|
||||||
|
|
||||||
|
self.purgeOrphans(function(err) {
|
||||||
|
if (err)
|
||||||
|
return callback(err);
|
||||||
|
|
||||||
|
return callback(self.size <= Mempool.MAX_MEMPOOL_SIZE);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Mempool.prototype.purgeOrphans = function purgeOrphans(callback) {
|
||||||
|
var self = this;
|
||||||
|
var batch = this.db.batch();
|
||||||
|
|
||||||
|
callback = utils.ensure(callback);
|
||||||
|
|
||||||
|
utils.forEachSerial(['m/D', 'm/d'], function(type, callback) {
|
||||||
|
var iter = self.db.iterator({
|
||||||
|
gte: type,
|
||||||
|
lte: type + '~',
|
||||||
|
keys: true,
|
||||||
|
values: false,
|
||||||
|
fillCache: false,
|
||||||
|
keyAsBuffer: false
|
||||||
|
});
|
||||||
|
|
||||||
|
(function next() {
|
||||||
|
iter.next(function(err, key, value) {
|
||||||
|
if (err) {
|
||||||
|
return iter.end(function() {
|
||||||
|
callback(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === undefined)
|
||||||
|
return iter.end(callback);
|
||||||
|
|
||||||
|
batch.del(key);
|
||||||
|
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
}, function(err) {
|
||||||
|
if (err)
|
||||||
|
return callback(err);
|
||||||
|
|
||||||
|
batch.write(function(err) {
|
||||||
|
if (err)
|
||||||
|
return callback(err);
|
||||||
|
|
||||||
|
self.dynamicMemoryUsage(function(err, size) {
|
||||||
|
if (err)
|
||||||
|
return callback(err);
|
||||||
|
|
||||||
|
self.size = size;
|
||||||
|
|
||||||
|
return callback();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
Mempool.prototype.get =
|
Mempool.prototype.get =
|
||||||
Mempool.prototype.getTX = function getTX(hash, callback) {
|
Mempool.prototype.getTX = function getTX(hash, callback) {
|
||||||
if (hash instanceof bcoin.tx)
|
if (hash instanceof bcoin.tx)
|
||||||
@ -197,18 +297,18 @@ Mempool.prototype.addTX = function addTX(tx, peer, callback, force) {
|
|||||||
callback = utils.asyncify(callback);
|
callback = utils.asyncify(callback);
|
||||||
|
|
||||||
if (!this.checkTX(tx, peer))
|
if (!this.checkTX(tx, peer))
|
||||||
return callback(new VerifyError('CheckTransaction failed', -1));
|
return callback(new VerifyError('invalid', 'CheckTransaction failed', -1));
|
||||||
|
|
||||||
if (tx.isCoinbase()) {
|
if (tx.isCoinbase()) {
|
||||||
peer.sendReject(tx, 'coinbase', 100);
|
peer.sendReject(tx, 'invalid', 'coinbase', 100);
|
||||||
return callback(new VerifyError('coinbase as individual tx', 100));
|
return callback(new VerifyError('invalid', 'coinbase', 100));
|
||||||
}
|
}
|
||||||
|
|
||||||
ts = utils.now();
|
ts = utils.now();
|
||||||
height = this.chain.height + 1;
|
height = this.chain.height + 1;
|
||||||
|
|
||||||
if (this.requireStandard && !tx.isStandard(Mempool.flags, ts, height, ret)) {
|
if (this.requireStandard && !tx.isStandard(Mempool.flags, ts, height, ret)) {
|
||||||
peer.sendReject(tx, ret.reason, 0);
|
peer.sendReject(tx, 'nonstandard', ret.reason, 0);
|
||||||
return callback(new VerifyError(ret.reason, 0));
|
return callback(new VerifyError(ret.reason, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,33 +316,57 @@ Mempool.prototype.addTX = function addTX(tx, peer, callback, force) {
|
|||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
|
|
||||||
if (exists)
|
if (exists) {
|
||||||
|
peer.sendReject(tx, 'alreadyknown', 'txn-already-in-mempool', 0);
|
||||||
return callback();
|
return callback();
|
||||||
|
}
|
||||||
|
|
||||||
self.tx.isDoubleSpend(tx, function(err, doubleSpend) {
|
self.tx.isDoubleSpend(tx, function(err, doubleSpend) {
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
|
|
||||||
if (doubleSpend) {
|
if (doubleSpend) {
|
||||||
peer.sendReject(tx, 'bad-txns-inputs-spent', 0);
|
peer.sendReject(tx, 'duplicate', 'bad-txns-inputs-spent', 0);
|
||||||
return callback(new VerifyError('bad-txns-inputs-spent', 0));
|
return callback(new VerifyError(
|
||||||
|
'duplicate',
|
||||||
|
'bad-txns-inputs-spent',
|
||||||
|
0));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.node.fillCoin(tx, function(err) {
|
self.node.fillCoin(tx, function(err) {
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
|
|
||||||
if (!tx.hasPrevout())
|
if (!tx.hasPrevout()) {
|
||||||
|
if (self.size > Mempool.MAX_MEMPOOL_SIZE) {
|
||||||
|
return callback(
|
||||||
|
new VerifyError('insufficientfee',
|
||||||
|
'mempool full',
|
||||||
|
0));
|
||||||
|
}
|
||||||
return self.storeOrphan(tx, callback);
|
return self.storeOrphan(tx, callback);
|
||||||
|
}
|
||||||
|
|
||||||
self.verify(tx, function(err) {
|
self.verify(tx, function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
if (err.type === 'VerifyError' && err.score >= 0)
|
if (err.type === 'VerifyError' && err.score >= 0)
|
||||||
peer.sendReject(tx, err.reason, err.score);
|
peer.sendReject(tx, err.code, err.reason, err.score);
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.addUnchecked(tx, peer, callback);
|
self.limitMempoolSize(function(err, result) {
|
||||||
|
if (err)
|
||||||
|
return callback(err);
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
return callback(
|
||||||
|
new VerifyError('insufficientfee',
|
||||||
|
'mempool full',
|
||||||
|
0));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.addUnchecked(tx, peer, callback);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -255,7 +379,9 @@ Mempool.prototype.addUnchecked = function addUnchecked(tx, peer, callback) {
|
|||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
|
|
||||||
|
self.size += tx.getSize();
|
||||||
self.emit('tx', tx);
|
self.emit('tx', tx);
|
||||||
|
self.emit('add tx', tx);
|
||||||
|
|
||||||
utils.debug('Added tx %s to the mempool.', tx.rhash);
|
utils.debug('Added tx %s to the mempool.', tx.rhash);
|
||||||
|
|
||||||
@ -274,15 +400,40 @@ Mempool.prototype.addUnchecked = function addUnchecked(tx, peer, callback) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Mempool.prototype.removeUnchecked = function removeUnchecked(tx, callback) {
|
||||||
|
var self = this;
|
||||||
|
this.tx.removeUnchecked(tx, function(err) {
|
||||||
|
if (err)
|
||||||
|
return callback(err);
|
||||||
|
self.size -= tx.getSize();
|
||||||
|
self.emit('remove tx', tx);
|
||||||
|
return callback();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
Mempool.prototype.verify = function verify(tx, callback) {
|
Mempool.prototype.verify = function verify(tx, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var total, input, coin, i, fee, now;
|
var height = this.chain.height + 1;
|
||||||
|
var total, input, coin, i, fee, now, free, minFee;
|
||||||
|
|
||||||
if (this.requireStandard && !tx.isStandardInputs(Mempool.flags))
|
if (network.type !== 'segwit') {
|
||||||
return callback(new VerifyError('TX inputs are not standard.', -1));
|
if (tx.hasWitness())
|
||||||
|
return callback(new VerifyError('nonstandard', 'no-witness-yet', 0));
|
||||||
|
}
|
||||||
|
|
||||||
if (tx.getSigops(true) > constants.script.maxSigops)
|
if (this.requireStandard && !tx.isStandardInputs(Mempool.flags)) {
|
||||||
return callback(new VerifyError('bad-txns-too-many-sigops', 0));
|
return callback(new VerifyError(
|
||||||
|
'nonstandard',
|
||||||
|
'bad-txns-nonstandard-inputs',
|
||||||
|
0));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tx.getSigops(true) > constants.script.maxSigops) {
|
||||||
|
return callback(new VerifyError(
|
||||||
|
'nonstandard',
|
||||||
|
'bad-txns-too-many-sigops',
|
||||||
|
0));
|
||||||
|
}
|
||||||
|
|
||||||
total = new bn(0);
|
total = new bn(0);
|
||||||
for (i = 0; i < tx.inputs.length; i++) {
|
for (i = 0; i < tx.inputs.length; i++) {
|
||||||
@ -290,34 +441,61 @@ Mempool.prototype.verify = function verify(tx, callback) {
|
|||||||
coin = input.output;
|
coin = input.output;
|
||||||
|
|
||||||
if (coin.coinbase) {
|
if (coin.coinbase) {
|
||||||
if (this.chain.height - coin.height < constants.tx.coinbaseMaturity)
|
if (this.chain.height - coin.height < constants.tx.coinbaseMaturity) {
|
||||||
return callback(new VerifyError('bad-txns-premature-spend-of-coinbase', 0));
|
return callback(new VerifyError(
|
||||||
|
'invalid',
|
||||||
|
'bad-txns-premature-spend-of-coinbase',
|
||||||
|
0));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (coin.value.cmpn(0) < 0 || coin.value.cmp(constants.maxMoney) > 0)
|
if (coin.value.cmpn(0) < 0 || coin.value.cmp(constants.maxMoney) > 0) {
|
||||||
return callback(new VerifyError('bad-txns-inputvalues-outofrange', 100));
|
return callback(new VerifyError(
|
||||||
|
'invalid',
|
||||||
|
'bad-txns-inputvalues-outofrange',
|
||||||
|
100));
|
||||||
|
}
|
||||||
|
|
||||||
total.iadd(coin.value);
|
total.iadd(coin.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (total.cmpn(0) < 0 || total.cmp(constants.maxMoney) > 0)
|
if (total.cmpn(0) < 0 || total.cmp(constants.maxMoney) > 0) {
|
||||||
return callback(new VerifyError('bad-txns-inputvalues-outofrange', 100));
|
return callback(new VerifyError(
|
||||||
|
'invalid',
|
||||||
|
'bad-txns-inputvalues-outofrange',
|
||||||
|
100));
|
||||||
|
}
|
||||||
|
|
||||||
if (tx.getOutputValue().cmp(total) > 0)
|
if (tx.getOutputValue().cmp(total) > 0)
|
||||||
return callback(new VerifyError('bad-txns-in-belowout', 100));
|
return callback(new VerifyError('invalid', 'bad-txns-in-belowout', 100));
|
||||||
|
|
||||||
fee = total.sub(tx.getOutputValue());
|
fee = total.sub(tx.getOutputValue());
|
||||||
|
|
||||||
if (fee.cmpn(0) < 0)
|
if (fee.cmpn(0) < 0)
|
||||||
return callback(new VerifyError('bad-txns-fee-negative', 100));
|
return callback(new VerifyError('invalid', 'bad-txns-fee-negative', 100));
|
||||||
|
|
||||||
if (fee.cmp(constants.maxMoney) > 0)
|
if (fee.cmp(constants.maxMoney) > 0)
|
||||||
return callback(new VerifyError('bad-txns-fee-outofrange', 100));
|
return callback(new VerifyError('invalid', 'bad-txns-fee-outofrange', 100));
|
||||||
|
|
||||||
if (this.limitFree && fee.cmp(tx.getMinFee(true)) < 0)
|
minFee = tx.getMinFee();
|
||||||
return callback(new VerifyError('insufficient fee', 0));
|
if (fee.cmp(minFee) < 0) {
|
||||||
|
if (this.relayPriority && fee.cmpn(0) === 0) {
|
||||||
|
free = tx.isFree(height);
|
||||||
|
if (!free) {
|
||||||
|
return callback(new VerifyError(
|
||||||
|
'insufficientfee',
|
||||||
|
'insufficient priority',
|
||||||
|
0));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return callback(new VerifyError(
|
||||||
|
'insufficientfee',
|
||||||
|
'insufficient fee',
|
||||||
|
0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (this.limitFree && fee.cmpn(tx.getMinFee()) < 0) {
|
if (this.limitFree && free) {
|
||||||
now = utils.now();
|
now = utils.now();
|
||||||
|
|
||||||
if (!this.lastTime)
|
if (!this.lastTime)
|
||||||
@ -326,33 +504,85 @@ Mempool.prototype.verify = function verify(tx, callback) {
|
|||||||
this.freeCount *= Math.pow(1 - 1 / 600, now - this.lastTime);
|
this.freeCount *= Math.pow(1 - 1 / 600, now - this.lastTime);
|
||||||
this.lastTime = now;
|
this.lastTime = now;
|
||||||
|
|
||||||
if (this.freeCount > this.limitFreeRelay * 10 * 1000)
|
if (this.freeCount > this.limitFreeRelay * 10 * 1000) {
|
||||||
return callback(new VerifyError('insufficient priority', 0));
|
return callback(new VerifyError(
|
||||||
|
'insufficientfee',
|
||||||
|
'rate limited free transaction',
|
||||||
|
0));
|
||||||
|
}
|
||||||
|
|
||||||
this.freeCount += tx.getVirtualSize();
|
this.freeCount += tx.getVirtualSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.rejectInsaneFees && fee.cmpn(tx.getMinFee().muln(10000)) > 0)
|
if (this.rejectInsaneFees && fee.cmp(minFee.muln(10000)) > 0)
|
||||||
return callback(new VerifyError('TX has an insane fee.', -1));
|
return callback(new VerifyError('highfee', 'absurdly-high-fee', 0));
|
||||||
|
|
||||||
// Do this in the worker pool.
|
this.countAncestors(tx, function(err, count) {
|
||||||
tx.verifyAsync(null, true, Mempool.flags, function(err, result) {
|
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
|
|
||||||
if (!result) {
|
if (count > Mempool.ANCESTOR_LIMIT) {
|
||||||
return tx.verifyAsync(null, true, Mempool.mandatory, function(err, result) {
|
return callback(new VerifyError(
|
||||||
if (err)
|
'nonstandard',
|
||||||
return callback(err);
|
'too-long-mempool-chain',
|
||||||
|
0));
|
||||||
if (!result)
|
|
||||||
return callback(new VerifyError('mandatory-script-verify-flag', 0));
|
|
||||||
|
|
||||||
return callback(new VerifyError('non-mandatory-script-verify-flag', 0));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return callback();
|
// Do this in the worker pool.
|
||||||
|
tx.verifyAsync(null, true, Mempool.flags, function(err, result) {
|
||||||
|
if (err)
|
||||||
|
return callback(err);
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
return tx.verifyAsync(null, true, Mempool.mandatory, function(err, result) {
|
||||||
|
if (err)
|
||||||
|
return callback(err);
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
return callback(new VerifyError(
|
||||||
|
'nonstandard',
|
||||||
|
'mandatory-script-verify-flag',
|
||||||
|
0));
|
||||||
|
}
|
||||||
|
|
||||||
|
return callback(
|
||||||
|
new VerifyError('nonstandard',
|
||||||
|
'non-mandatory-script-verify-flag',
|
||||||
|
0));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return callback();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Mempool.prototype.countAncestors = function countAncestors(tx, callback) {
|
||||||
|
var self = this;
|
||||||
|
var inputs = new Array(tx.inputs.length);
|
||||||
|
utils.forEachSerial(tx.inputs, function(input, next, i) {
|
||||||
|
inputs[i] = 0;
|
||||||
|
self.getTX(input.prevout.hash, function(err, tx) {
|
||||||
|
if (err)
|
||||||
|
return next(err);
|
||||||
|
|
||||||
|
if (!tx)
|
||||||
|
return next();
|
||||||
|
|
||||||
|
self.countAncestors(tx, function(err, max) {
|
||||||
|
if (err)
|
||||||
|
return next(err);
|
||||||
|
|
||||||
|
inputs[i] += max;
|
||||||
|
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, function(err) {
|
||||||
|
if (err)
|
||||||
|
return callback(err);
|
||||||
|
|
||||||
|
return callback(null, inputs.sort().pop());
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -507,7 +737,7 @@ Mempool.prototype.resolveOrphans = function resolveOrphans(tx, callback, force)
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Mempool.prototype.getInv = function getInv(callback) {
|
Mempool.prototype.getSnapshot = function getSnapshot(callback) {
|
||||||
return this.tx.getAllHashes(callback);
|
return this.tx.getAllHashes(callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -516,60 +746,73 @@ Mempool.prototype.checkTX = function checkTX(tx, peer) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Mempool.checkTX = function checkTX(tx, peer) {
|
Mempool.checkTX = function checkTX(tx, peer) {
|
||||||
var i, input, output, size;
|
|
||||||
var total = new bn(0);
|
|
||||||
var uniq = {};
|
var uniq = {};
|
||||||
|
var total = new bn(0);
|
||||||
|
var i, input, output, size;
|
||||||
|
|
||||||
if (!peer)
|
if (!peer)
|
||||||
peer = DUMMY_PEER;
|
peer = DUMMY_PEER;
|
||||||
|
|
||||||
if (tx.inputs.length === 0)
|
if (tx.inputs.length === 0)
|
||||||
return peer.sendReject(tx, 'bad-txns-vin-empty', 100);
|
return peer.sendReject(tx, 'invalid', 'bad-txns-vin-empty', 100);
|
||||||
|
|
||||||
if (tx.outputs.length === 0)
|
if (tx.outputs.length === 0)
|
||||||
return peer.sendReject(tx, 'bad-txns-vout-empty', 100);
|
return peer.sendReject(tx, 'invalid', 'bad-txns-vout-empty', 100);
|
||||||
|
|
||||||
if (tx.getVirtualSize() > constants.block.maxSize)
|
if (tx.getVirtualSize() > constants.block.maxSize)
|
||||||
return peer.sendReject(tx, 'bad-txns-oversize', 100);
|
return peer.sendReject(tx, 'invalid', 'bad-txns-oversize', 100);
|
||||||
|
|
||||||
for (i = 0; i < tx.outputs.length; i++) {
|
for (i = 0; i < tx.outputs.length; i++) {
|
||||||
output = tx.outputs[i];
|
output = tx.outputs[i];
|
||||||
|
|
||||||
if (output.value.cmpn(0) < 0)
|
if (output.value.cmpn(0) < 0)
|
||||||
return peer.sendReject(tx, 'bad-txns-vout-negative', 100);
|
return peer.sendReject(tx, 'invalid', 'bad-txns-vout-negative', 100);
|
||||||
|
|
||||||
if (output.value.cmp(constants.maxMoney) > 0)
|
if (output.value.cmp(constants.maxMoney) > 0)
|
||||||
return peer.sendReject(tx, 'bad-txns-vout-toolarge', 100);
|
return peer.sendReject(tx, 'invalid', 'bad-txns-vout-toolarge', 100);
|
||||||
|
|
||||||
total.iadd(output.value);
|
total.iadd(output.value);
|
||||||
if (total.cmpn(0) < 0 || total.cmp(constants.maxMoney) > 0)
|
|
||||||
return peer.sendReject(tx, 'bad-txns-txouttotal-toolarge', 100);
|
if (total.cmpn(0) < 0 || total.cmp(constants.maxMoney) > 0) {
|
||||||
|
return peer.sendReject(tx,
|
||||||
|
'invalid',
|
||||||
|
'bad-txns-txouttotal-toolarge',
|
||||||
|
100);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < tx.inputs.length; i++) {
|
for (i = 0; i < tx.inputs.length; i++) {
|
||||||
input = tx.inputs[i];
|
input = tx.inputs[i];
|
||||||
if (uniq[input.prevout.hash])
|
if (uniq[input.prevout.hash])
|
||||||
return peer.sendReject(tx, 'bad-txns-inputs-duplicate', 100);
|
return peer.sendReject(tx, 'invalid', 'bad-txns-inputs-duplicate', 100);
|
||||||
uniq[input.prevout.hash] = true;
|
uniq[input.prevout.hash] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tx.isCoinbase()) {
|
if (tx.isCoinbase()) {
|
||||||
size = tx.inputs[0].script.getSize();
|
size = tx.inputs[0].script.getSize();
|
||||||
if (size < 2 || size > 100)
|
if (size < 2 || size > 100)
|
||||||
return peer.sendReject(tx, 'bad-cb-length', 100);
|
return peer.sendReject(tx, 'invalid', 'bad-cb-length', 100);
|
||||||
} else {
|
} else {
|
||||||
for (i = 0; i < tx.inputs.length; i++) {
|
for (i = 0; i < tx.inputs.length; i++) {
|
||||||
input = tx.inputs[i];
|
input = tx.inputs[i];
|
||||||
if (+input.prevout.hash === 0)
|
if (+input.prevout.hash === 0)
|
||||||
return peer.sendReject(tx, 'bad-txns-prevout-null', 10);
|
return peer.sendReject(tx, 'invalid', 'bad-txns-prevout-null', 10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
function VerifyError(reason, score) {
|
/**
|
||||||
|
* VerifyError
|
||||||
|
*/
|
||||||
|
|
||||||
|
function VerifyError(code, reason, score) {
|
||||||
Error.call(this);
|
Error.call(this);
|
||||||
if (Error.captureStackTrace)
|
if (Error.captureStackTrace)
|
||||||
Error.captureStackTrace(this, VerifyError);
|
Error.captureStackTrace(this, VerifyError);
|
||||||
this.type = 'VerifyError';
|
this.type = 'VerifyError';
|
||||||
|
this.code = code;
|
||||||
this.message = reason;
|
this.message = reason;
|
||||||
this.reason = score === -1 ? null : reason;
|
this.reason = score === -1 ? null : reason;
|
||||||
this.score = score;
|
this.score = score;
|
||||||
|
|||||||
@ -882,7 +882,7 @@ MTX.prototype.selectCoins = function selectCoins(unspent, options) {
|
|||||||
// Calculate max possible size after signing.
|
// Calculate max possible size after signing.
|
||||||
size = tx.maxSize(options.m, options.n);
|
size = tx.maxSize(options.m, options.n);
|
||||||
|
|
||||||
// if (newkb == null && tx.isFree(size)) {
|
// if (newkb == null && tx.isFree(this.chain.height + 1, size)) {
|
||||||
// fee = new bn(0);
|
// fee = new bn(0);
|
||||||
// break;
|
// break;
|
||||||
// }
|
// }
|
||||||
|
|||||||
@ -679,8 +679,8 @@ Peer.prototype.setMisbehavior = function setMisbehavior(dos) {
|
|||||||
return this.pool.setMisbehavior(this, dos);
|
return this.pool.setMisbehavior(this, dos);
|
||||||
};
|
};
|
||||||
|
|
||||||
Peer.prototype.sendReject = function sendReject(obj, reason, dos) {
|
Peer.prototype.sendReject = function sendReject(obj, code, reason, dos) {
|
||||||
return this.pool.reject(this, obj, reason, dos);
|
return this.pool.reject(this, obj, code, reason, dos);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -191,8 +191,8 @@ Pool.prototype._init = function _init() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.chain.on('verify-error', function(block, reason, score, peer) {
|
this.chain.on('verify-error', function(block, code, reason, score, peer) {
|
||||||
peer.sendReject(block, reason, score);
|
peer.sendReject(block, code, reason, score);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.chain.on('fork', function(block, data, peer) {
|
this.chain.on('fork', function(block, data, peer) {
|
||||||
@ -1885,14 +1885,15 @@ Pool.prototype.isMisbehaving = function isMisbehaving(host) {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
Pool.prototype.reject = function reject(peer, obj, reason, dos) {
|
Pool.prototype.reject = function reject(peer, obj, code, reason, dos) {
|
||||||
if (dos != null)
|
if (dos != null)
|
||||||
peer.setMisbehavior(dos);
|
peer.setMisbehavior(dos);
|
||||||
|
|
||||||
utils.debug('Rejecting %s %s: reason=%s',
|
utils.debug('Rejecting %s %s: ccode=%s reason=%s',
|
||||||
obj.type, obj.hash('hex'), reason);
|
obj.type, obj.hash('hex'), code, reason);
|
||||||
|
|
||||||
peer.reject({
|
peer.reject({
|
||||||
|
ccode: code,
|
||||||
reason: reason,
|
reason: reason,
|
||||||
data: obj.hash()
|
data: obj.hash()
|
||||||
});
|
});
|
||||||
|
|||||||
@ -249,7 +249,12 @@ exports.reject = {
|
|||||||
nonstandard: 0x40,
|
nonstandard: 0x40,
|
||||||
dust: 0x41,
|
dust: 0x41,
|
||||||
insufficientfee: 0x42,
|
insufficientfee: 0x42,
|
||||||
checkpoint: 0x43
|
checkpoint: 0x43,
|
||||||
|
// Internal codes (NOT FOR USE ON NETWORK)
|
||||||
|
internal: 0x100,
|
||||||
|
highfee: 0x100,
|
||||||
|
alreadyknown: 0x101,
|
||||||
|
conflict: 0x102
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.rejectByVal = Object.keys(exports.reject).reduce(function(out, name) {
|
exports.rejectByVal = Object.keys(exports.reject).reduce(function(out, name) {
|
||||||
|
|||||||
@ -580,9 +580,13 @@ Framer.headers = function _headers(block, writer) {
|
|||||||
|
|
||||||
Framer.reject = function reject(details, writer) {
|
Framer.reject = function reject(details, writer) {
|
||||||
var p = new BufferWriter(writer);
|
var p = new BufferWriter(writer);
|
||||||
|
var ccode = details.ccode;
|
||||||
|
|
||||||
|
if (ccode >= constants.reject.internal)
|
||||||
|
ccode = constants.reject.invalid;
|
||||||
|
|
||||||
p.writeVarString(details.message || '', 'ascii');
|
p.writeVarString(details.message || '', 'ascii');
|
||||||
p.writeU8(constants.reject[details.ccode] || constants.reject.malformed);
|
p.writeU8(constants.reject[ccode] || constants.reject.invalid);
|
||||||
p.writeVarString(details.reason || '', 'ascii');
|
p.writeVarString(details.reason || '', 'ascii');
|
||||||
if (details.data)
|
if (details.data)
|
||||||
p.writeHash(details.data);
|
p.writeHash(details.data);
|
||||||
|
|||||||
@ -916,10 +916,11 @@ TX.prototype.maxSize = function maxSize() {
|
|||||||
return this.getVirtualSize();
|
return this.getVirtualSize();
|
||||||
};
|
};
|
||||||
|
|
||||||
TX.prototype.getPriority = function getPriority(size) {
|
TX.prototype.getPriority = function getPriority(height, size) {
|
||||||
var sum, i, input, age, height;
|
var sum, i, input, age;
|
||||||
|
|
||||||
height = this.height;
|
if (height == null)
|
||||||
|
height = this.height;
|
||||||
|
|
||||||
if (height === -1)
|
if (height === -1)
|
||||||
height = null;
|
height = null;
|
||||||
@ -927,7 +928,9 @@ TX.prototype.getPriority = function getPriority(size) {
|
|||||||
if (!this.hasPrevout())
|
if (!this.hasPrevout())
|
||||||
return new bn(0);
|
return new bn(0);
|
||||||
|
|
||||||
size = size || this.maxSize();
|
if (size == null)
|
||||||
|
size = this.maxSize();
|
||||||
|
|
||||||
sum = new bn(0);
|
sum = new bn(0);
|
||||||
|
|
||||||
for (i = 0; i < this.inputs.length; i++) {
|
for (i = 0; i < this.inputs.length; i++) {
|
||||||
@ -950,29 +953,31 @@ TX.prototype.getPriority = function getPriority(size) {
|
|||||||
return sum.divn(size);
|
return sum.divn(size);
|
||||||
};
|
};
|
||||||
|
|
||||||
TX.prototype.isFree = function isFree(size) {
|
TX.prototype.isFree = function isFree(height, size) {
|
||||||
var priority;
|
var priority;
|
||||||
|
|
||||||
if (!this.hasPrevout())
|
if (!this.hasPrevout())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
size = size || this.maxSize();
|
if (height == null)
|
||||||
|
height = this.height;
|
||||||
|
|
||||||
|
if (size == null)
|
||||||
|
size = this.maxSize();
|
||||||
|
|
||||||
if (size >= constants.tx.maxFreeSize)
|
if (size >= constants.tx.maxFreeSize)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
priority = this.getPriority();
|
priority = this.getPriority(height, size);
|
||||||
|
|
||||||
return priority.cmp(constants.tx.freeThreshold) > 0;
|
return priority.cmp(constants.tx.freeThreshold) > 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
TX.prototype.getMinFee = function getMinFee(allowFree, size) {
|
TX.prototype.getMinFee = function getMinFee(size) {
|
||||||
var fee;
|
var fee;
|
||||||
|
|
||||||
size = size || this.maxSize();
|
if (size == null)
|
||||||
|
size = this.maxSize();
|
||||||
if (allowFree && this.isFree(size))
|
|
||||||
return new bn(0);
|
|
||||||
|
|
||||||
fee = new bn(constants.tx.minFee).muln(size).divn(1000);
|
fee = new bn(constants.tx.minFee).muln(size).divn(1000);
|
||||||
|
|
||||||
|
|||||||
@ -1546,6 +1546,7 @@ TXPool.prototype.addUnchecked = function addUnchecked(tx, callback, force) {
|
|||||||
batch = this.db.batch();
|
batch = this.db.batch();
|
||||||
|
|
||||||
batch.put(prefix + 't/t/' + hash, tx.toExtended());
|
batch.put(prefix + 't/t/' + hash, tx.toExtended());
|
||||||
|
batch.put(prefix + 't/s/s/' + pad32(tx.ps) + '/' + hash, DUMMY);
|
||||||
|
|
||||||
tx.getAddresses().forEach(function(address) {
|
tx.getAddresses().forEach(function(address) {
|
||||||
batch.put(prefix + 't/a/' + address + '/' + hash, DUMMY);
|
batch.put(prefix + 't/a/' + address + '/' + hash, DUMMY);
|
||||||
@ -1586,68 +1587,77 @@ TXPool.prototype.addUnchecked = function addUnchecked(tx, callback, force) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
TXPool.prototype.removeUnchecked = function removeUnchecked(tx, callback, force) {
|
TXPool.prototype.removeUnchecked = function removeUnchecked(hash, callback, force) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var prefix = this.prefix + '/';
|
var prefix = this.prefix + '/';
|
||||||
var hash = tx.hash('hex');
|
|
||||||
var batch;
|
var batch;
|
||||||
|
|
||||||
var unlock = this._lock(removeUnchecked, [tx, callback], force);
|
var unlock = this._lock(removeUnchecked, [hash, callback], force);
|
||||||
if (!unlock)
|
if (!unlock)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
callback = utils.wrap(callback, unlock);
|
callback = utils.wrap(callback, unlock);
|
||||||
|
|
||||||
batch = this.db.batch();
|
if (hash.hash)
|
||||||
|
hash = hash.hash('hex');
|
||||||
|
|
||||||
batch.del(prefix + 't/t/' + hash);
|
this.getTX(hash, function(err, tx) {
|
||||||
batch.del(prefix + 'D/' + hash);
|
|
||||||
|
|
||||||
tx.getAddresses().forEach(function(address) {
|
|
||||||
batch.del(prefix + 't/a/' + address + '/' + hash);
|
|
||||||
});
|
|
||||||
|
|
||||||
tx.inputs.forEach(function(input) {
|
|
||||||
var key = input.prevout.hash + '/' + input.prevout.index;
|
|
||||||
var address;
|
|
||||||
|
|
||||||
if (tx.isCoinbase())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!input.output)
|
|
||||||
return;
|
|
||||||
|
|
||||||
address = input.getAddress();
|
|
||||||
|
|
||||||
batch.del(prefix + 'u/t/' + key);
|
|
||||||
batch.del(prefix + 's/t/' + key);
|
|
||||||
|
|
||||||
if (address)
|
|
||||||
batch.del(prefix + 'u/a/' + address + '/' + key);
|
|
||||||
});
|
|
||||||
|
|
||||||
tx.outputs.forEach(function(output, i) {
|
|
||||||
var key = hash + '/' + i;
|
|
||||||
var address = output.getAddress();
|
|
||||||
|
|
||||||
batch.del(prefix + 'u/t/' + key);
|
|
||||||
|
|
||||||
if (address)
|
|
||||||
batch.del(prefix + 'u/a/' + address + '/' + key);
|
|
||||||
});
|
|
||||||
|
|
||||||
batch.write(function(err) {
|
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
self.emit('remove tx', tx);
|
|
||||||
return callback();
|
if (!tx)
|
||||||
|
return callback();
|
||||||
|
|
||||||
|
batch = self.db.batch();
|
||||||
|
|
||||||
|
batch.del(prefix + 't/t/' + hash);
|
||||||
|
batch.del(prefix + 't/s/s/' + pad32(tx.ps) + '/' + hash);
|
||||||
|
batch.del(prefix + 'D/' + hash);
|
||||||
|
|
||||||
|
tx.getAddresses().forEach(function(address) {
|
||||||
|
batch.del(prefix + 't/a/' + address + '/' + hash);
|
||||||
|
});
|
||||||
|
|
||||||
|
tx.inputs.forEach(function(input) {
|
||||||
|
var key = input.prevout.hash + '/' + input.prevout.index;
|
||||||
|
var address;
|
||||||
|
|
||||||
|
if (tx.isCoinbase())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!input.output)
|
||||||
|
return;
|
||||||
|
|
||||||
|
address = input.getAddress();
|
||||||
|
|
||||||
|
batch.del(prefix + 'u/t/' + key);
|
||||||
|
batch.del(prefix + 's/t/' + key);
|
||||||
|
|
||||||
|
if (address)
|
||||||
|
batch.del(prefix + 'u/a/' + address + '/' + key);
|
||||||
|
});
|
||||||
|
|
||||||
|
tx.outputs.forEach(function(output, i) {
|
||||||
|
var key = hash + '/' + i;
|
||||||
|
var address = output.getAddress();
|
||||||
|
|
||||||
|
batch.del(prefix + 'u/t/' + key);
|
||||||
|
|
||||||
|
if (address)
|
||||||
|
batch.del(prefix + 'u/a/' + address + '/' + key);
|
||||||
|
});
|
||||||
|
|
||||||
|
batch.write(function(err) {
|
||||||
|
if (err)
|
||||||
|
return callback(err);
|
||||||
|
self.emit('remove tx', tx);
|
||||||
|
return callback();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
TXPool.prototype.zap = function zap(tip, callback, force) {
|
TXPool.prototype.zap = function zap(now, age, callback, force) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var now = tip.ts;
|
|
||||||
var age = 10 * 60 * 60;
|
|
||||||
|
|
||||||
var unlock = this._lock(zap, [tip, callback], force);
|
var unlock = this._lock(zap, [tip, callback], force);
|
||||||
if (!unlock)
|
if (!unlock)
|
||||||
@ -1655,23 +1665,13 @@ TXPool.prototype.zap = function zap(tip, callback, force) {
|
|||||||
|
|
||||||
callback = utils.wrap(callback, unlock);
|
callback = utils.wrap(callback, unlock);
|
||||||
|
|
||||||
// return this.getRange(null, {
|
return this.getRange(null, {
|
||||||
// start: 0,
|
start: 0,
|
||||||
// end: now - age
|
end: now - age
|
||||||
// }, function(err, txs) {
|
}, function(err, txs) {
|
||||||
// });
|
|
||||||
|
|
||||||
this.getPending(function(err, txs) {
|
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
|
|
||||||
txs = txs.filter(function(tx) {
|
|
||||||
return now > tx.ps + age;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (txs.length === 0)
|
|
||||||
return callback();
|
|
||||||
|
|
||||||
self.fillTX(txs, function(err) {
|
self.fillTX(txs, function(err) {
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
|
|||||||
@ -98,7 +98,6 @@ WalletDB.prototype._init = function _init() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.tx = new bcoin.txdb('w', this.db, {
|
this.tx = new bcoin.txdb('w', this.db, {
|
||||||
indexSpent: true,
|
|
||||||
indexExtra: true,
|
indexExtra: true,
|
||||||
indexAddress: true,
|
indexAddress: true,
|
||||||
mapAddress: true,
|
mapAddress: true,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user