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)) {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -401,13 +402,15 @@ Chain.prototype._verify = function _verify(block, prev, peer, callback) {
|
||||
// Ensure the timestamp is correct
|
||||
if (block.ts <= medianTime) {
|
||||
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);
|
||||
}
|
||||
|
||||
if (block.bits !== self.getTarget(prev, block)) {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -426,7 +429,8 @@ Chain.prototype._verify = function _verify(block, prev, peer, callback) {
|
||||
// once the majority of blocks are using it.
|
||||
if (block.version < 2 && prev.isOutdated(2)) {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -434,7 +438,8 @@ Chain.prototype._verify = function _verify(block, prev, peer, callback) {
|
||||
// once the majority of blocks are using it.
|
||||
if (block.version < 3 && prev.isOutdated(3)) {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -442,7 +447,8 @@ Chain.prototype._verify = function _verify(block, prev, peer, callback) {
|
||||
// once the majority of blocks are using it.
|
||||
if (block.version < 4 && prev.isOutdated(4)) {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -451,7 +457,8 @@ Chain.prototype._verify = function _verify(block, prev, peer, callback) {
|
||||
if (network.segwitHeight !== -1 && height >= network.segwitHeight) {
|
||||
if (block.version < 5 && prev.isOutdated(5)) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -493,7 +500,8 @@ Chain.prototype._verify = function _verify(block, prev, peer, callback) {
|
||||
if (coinbaseHeight) {
|
||||
if (block.getCoinbaseHeight() !== height) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -501,13 +509,15 @@ Chain.prototype._verify = function _verify(block, prev, peer, callback) {
|
||||
if (block.version >= 5 && segwit) {
|
||||
if (block.commitmentHash !== block.getCommitmentHash()) {
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
if (block.hasWitness()) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -523,7 +533,8 @@ Chain.prototype._verify = function _verify(block, prev, peer, callback) {
|
||||
// regards to nSequence and nLockTime.
|
||||
if (!tx.isFinal(height, ts)) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -557,7 +568,8 @@ Chain.prototype._checkDuplicates = function _checkDuplicates(block, prev, peer,
|
||||
if (result) {
|
||||
utils.debug('Block is overwriting txids: %s', block.rhash);
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -606,7 +618,8 @@ Chain.prototype._checkInputs = function _checkInputs(block, prev, flags, peer, c
|
||||
|
||||
if (sigops > constants.script.maxBlockSigops) {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -629,7 +642,8 @@ Chain.prototype._checkInputs = function _checkInputs(block, prev, flags, peer, c
|
||||
utils.revHex(input.prevout.hash) + '/' + input.prevout.index);
|
||||
if (height < network.checkpoints.lastHeight)
|
||||
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);
|
||||
}
|
||||
|
||||
@ -682,7 +696,8 @@ Chain.prototype._checkReward = function _checkReward(block) {
|
||||
actual.iadd(block.txs[i].getFee());
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -974,7 +989,8 @@ Chain.prototype.add = function add(initial, peer, callback, force) {
|
||||
chain: !!self.invalid[prevHash]
|
||||
}, peer);
|
||||
self.invalid[hash] = true;
|
||||
self.emit('verify-error', block, 'duplicate', 0, peer);
|
||||
self.emit('verify-error',
|
||||
block, 'duplicate', 'duplicate', 0, peer);
|
||||
return done();
|
||||
}
|
||||
|
||||
@ -990,7 +1006,8 @@ Chain.prototype.add = function add(initial, peer, callback, force) {
|
||||
seen: false,
|
||||
chain: false
|
||||
}, peer);
|
||||
self.emit('verify-error', block, ret.reason, ret.score, peer);
|
||||
self.emit('verify-error',
|
||||
block, 'invalid', ret.reason, ret.score, peer);
|
||||
return done();
|
||||
}
|
||||
|
||||
@ -1024,7 +1041,8 @@ Chain.prototype.add = function add(initial, peer, callback, force) {
|
||||
seen: true
|
||||
}, peer);
|
||||
|
||||
self.emit('verify-error', block, 'bad-prevblk', 0, peer);
|
||||
self.emit('verify-error',
|
||||
block, 'invalid', 'bad-prevblk', 0, peer);
|
||||
|
||||
return done();
|
||||
}
|
||||
@ -1048,7 +1066,8 @@ Chain.prototype.add = function add(initial, peer, callback, force) {
|
||||
hash: hash,
|
||||
seen: false
|
||||
}, peer);
|
||||
self.emit('verify-error', block, 'bad-prevblk', 0, peer);
|
||||
self.emit('verify-error',
|
||||
block, 'invalid', 'bad-prevblk', 0, peer);
|
||||
return done();
|
||||
}
|
||||
|
||||
@ -1078,7 +1097,8 @@ Chain.prototype.add = function add(initial, peer, callback, force) {
|
||||
checkpoint: true
|
||||
}, peer);
|
||||
|
||||
self.emit('verify-error', block, 'checkpoint mismatch', 100, peer);
|
||||
self.emit('verify-error',
|
||||
block, 'checkpoint', 'checkpoint mismatch', 100, peer);
|
||||
|
||||
return done();
|
||||
}
|
||||
|
||||
@ -10,34 +10,17 @@ var utils = bcoin.utils;
|
||||
var network = bcoin.protocol.network;
|
||||
var db = {};
|
||||
|
||||
module.exports = function ldb(name, options) {
|
||||
var file = bcoin.prefix + '/' + name + '-' + network.type + '.db';
|
||||
var backend = typeof options.db === 'string'
|
||||
? options.db
|
||||
: process.env.BCOIN_DB;
|
||||
/**
|
||||
* LDB
|
||||
*/
|
||||
|
||||
function ldb(name, options) {
|
||||
var file = getLocation(name);
|
||||
|
||||
if (!db[file]) {
|
||||
if (!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, {
|
||||
keyEncoding: 'ascii',
|
||||
valueEncoding: 'binary',
|
||||
@ -59,12 +42,57 @@ module.exports = function ldb(name, options) {
|
||||
// optimizeCompaction: 'level',
|
||||
// memtableBudget: 512 << 20,
|
||||
|
||||
db: backend
|
||||
db: getBackend(options.db)
|
||||
});
|
||||
}
|
||||
|
||||
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
|
||||
@ -168,3 +196,13 @@ LowlevelUp.prototype.getProperty = function getProperty(name) {
|
||||
LowlevelUp.prototype.approximateSize = function 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 bn = require('bn.js');
|
||||
var constants = bcoin.protocol.constants;
|
||||
var network = bcoin.protocol.network;
|
||||
var utils = require('./utils');
|
||||
var assert = utils.assert;
|
||||
var BufferWriter = require('./writer');
|
||||
@ -32,34 +33,20 @@ function Mempool(node, options) {
|
||||
this.node = node;
|
||||
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.jobs = [];
|
||||
this.busy = false;
|
||||
this.locker = new bcoin.locker(this, this.add, 20 << 20);
|
||||
|
||||
this.pending = [];
|
||||
this.pendingTX = {};
|
||||
this.pendingSize = 0;
|
||||
this.pendingLimit = 20 << 20;
|
||||
this.locker = new bcoin.locker(this, this.add, this.pendingLimit);
|
||||
this.db = null;
|
||||
this.tx = null;
|
||||
this.size = 0;
|
||||
|
||||
this.freeCount = 0;
|
||||
this.lastTime = 0;
|
||||
|
||||
this.limitFree = this.options.limitFree !== false;
|
||||
this.limitFreeRelay = this.options.limitFreeRelay || 15;
|
||||
this.relayPriority = this.options.relayPriority !== false;
|
||||
this.requireStandard = this.options.requireStandard !== false;
|
||||
this.rejectInsaneFees = this.options.rejectInsaneFees !== false;
|
||||
|
||||
@ -73,6 +60,10 @@ utils.inherits(Mempool, EventEmitter);
|
||||
Mempool.flags = constants.flags.STANDARD_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) {
|
||||
return this.locker.lock(func, args, force);
|
||||
};
|
||||
@ -83,20 +74,53 @@ Mempool.prototype.purgePending = function purgePending() {
|
||||
|
||||
Mempool.prototype._init = function _init() {
|
||||
var self = this;
|
||||
var unlock = this._lock(utils.nop, []);
|
||||
|
||||
if (this.db.loaded) {
|
||||
this.loaded = true;
|
||||
return;
|
||||
}
|
||||
bcoin.ldb.destroy('mempool', 'memdown', function(err) {
|
||||
if (err) {
|
||||
unlock();
|
||||
return self.emit('error', err);
|
||||
}
|
||||
|
||||
this.db.once('open', function() {
|
||||
self.loaded = true;
|
||||
self.emit('open');
|
||||
self.db = bcoin.ldb('mempool', {
|
||||
db: 'memdown'
|
||||
});
|
||||
|
||||
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) {
|
||||
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) {
|
||||
@ -107,14 +131,9 @@ Mempool.prototype.addBlock = function addBlock(block, callback, force) {
|
||||
|
||||
callback = utils.wrap(callback, unlock);
|
||||
|
||||
this.open(function(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
utils.forEachSerial(block.txs, function(tx, next) {
|
||||
self.tx.removeUnchecked(tx, next);
|
||||
}, callback);
|
||||
});
|
||||
utils.forEachSerial(block.txs, function(tx, next) {
|
||||
self.removeUnchecked(tx, next);
|
||||
}, callback);
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
utils.forEachSerial(block.txs.slice().reverse(), function(tx, next) {
|
||||
self.tx.addUnchecked(tx, next);
|
||||
self.addUnchecked(tx, next);
|
||||
}, 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.getTX = function getTX(hash, callback) {
|
||||
if (hash instanceof bcoin.tx)
|
||||
@ -197,18 +297,18 @@ Mempool.prototype.addTX = function addTX(tx, peer, callback, force) {
|
||||
callback = utils.asyncify(callback);
|
||||
|
||||
if (!this.checkTX(tx, peer))
|
||||
return callback(new VerifyError('CheckTransaction failed', -1));
|
||||
return callback(new VerifyError('invalid', 'CheckTransaction failed', -1));
|
||||
|
||||
if (tx.isCoinbase()) {
|
||||
peer.sendReject(tx, 'coinbase', 100);
|
||||
return callback(new VerifyError('coinbase as individual tx', 100));
|
||||
peer.sendReject(tx, 'invalid', 'coinbase', 100);
|
||||
return callback(new VerifyError('invalid', 'coinbase', 100));
|
||||
}
|
||||
|
||||
ts = utils.now();
|
||||
height = this.chain.height + 1;
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
@ -216,33 +316,57 @@ Mempool.prototype.addTX = function addTX(tx, peer, callback, force) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
if (exists)
|
||||
if (exists) {
|
||||
peer.sendReject(tx, 'alreadyknown', 'txn-already-in-mempool', 0);
|
||||
return callback();
|
||||
}
|
||||
|
||||
self.tx.isDoubleSpend(tx, function(err, doubleSpend) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
if (doubleSpend) {
|
||||
peer.sendReject(tx, 'bad-txns-inputs-spent', 0);
|
||||
return callback(new VerifyError('bad-txns-inputs-spent', 0));
|
||||
peer.sendReject(tx, 'duplicate', 'bad-txns-inputs-spent', 0);
|
||||
return callback(new VerifyError(
|
||||
'duplicate',
|
||||
'bad-txns-inputs-spent',
|
||||
0));
|
||||
}
|
||||
|
||||
self.node.fillCoin(tx, function(err) {
|
||||
if (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);
|
||||
}
|
||||
|
||||
self.verify(tx, function(err) {
|
||||
if (err) {
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
return callback(err);
|
||||
|
||||
self.size += tx.getSize();
|
||||
self.emit('tx', tx);
|
||||
self.emit('add tx', tx);
|
||||
|
||||
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) {
|
||||
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))
|
||||
return callback(new VerifyError('TX inputs are not standard.', -1));
|
||||
if (network.type !== 'segwit') {
|
||||
if (tx.hasWitness())
|
||||
return callback(new VerifyError('nonstandard', 'no-witness-yet', 0));
|
||||
}
|
||||
|
||||
if (tx.getSigops(true) > constants.script.maxSigops)
|
||||
return callback(new VerifyError('bad-txns-too-many-sigops', 0));
|
||||
if (this.requireStandard && !tx.isStandardInputs(Mempool.flags)) {
|
||||
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);
|
||||
for (i = 0; i < tx.inputs.length; i++) {
|
||||
@ -290,34 +441,61 @@ Mempool.prototype.verify = function verify(tx, callback) {
|
||||
coin = input.output;
|
||||
|
||||
if (coin.coinbase) {
|
||||
if (this.chain.height - coin.height < constants.tx.coinbaseMaturity)
|
||||
return callback(new VerifyError('bad-txns-premature-spend-of-coinbase', 0));
|
||||
if (this.chain.height - coin.height < constants.tx.coinbaseMaturity) {
|
||||
return callback(new VerifyError(
|
||||
'invalid',
|
||||
'bad-txns-premature-spend-of-coinbase',
|
||||
0));
|
||||
}
|
||||
}
|
||||
|
||||
if (coin.value.cmpn(0) < 0 || coin.value.cmp(constants.maxMoney) > 0)
|
||||
return callback(new VerifyError('bad-txns-inputvalues-outofrange', 100));
|
||||
if (coin.value.cmpn(0) < 0 || coin.value.cmp(constants.maxMoney) > 0) {
|
||||
return callback(new VerifyError(
|
||||
'invalid',
|
||||
'bad-txns-inputvalues-outofrange',
|
||||
100));
|
||||
}
|
||||
|
||||
total.iadd(coin.value);
|
||||
}
|
||||
|
||||
if (total.cmpn(0) < 0 || total.cmp(constants.maxMoney) > 0)
|
||||
return callback(new VerifyError('bad-txns-inputvalues-outofrange', 100));
|
||||
if (total.cmpn(0) < 0 || total.cmp(constants.maxMoney) > 0) {
|
||||
return callback(new VerifyError(
|
||||
'invalid',
|
||||
'bad-txns-inputvalues-outofrange',
|
||||
100));
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
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)
|
||||
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)
|
||||
return callback(new VerifyError('insufficient fee', 0));
|
||||
minFee = tx.getMinFee();
|
||||
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();
|
||||
|
||||
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.lastTime = now;
|
||||
|
||||
if (this.freeCount > this.limitFreeRelay * 10 * 1000)
|
||||
return callback(new VerifyError('insufficient priority', 0));
|
||||
if (this.freeCount > this.limitFreeRelay * 10 * 1000) {
|
||||
return callback(new VerifyError(
|
||||
'insufficientfee',
|
||||
'rate limited free transaction',
|
||||
0));
|
||||
}
|
||||
|
||||
this.freeCount += tx.getVirtualSize();
|
||||
}
|
||||
|
||||
if (this.rejectInsaneFees && fee.cmpn(tx.getMinFee().muln(10000)) > 0)
|
||||
return callback(new VerifyError('TX has an insane fee.', -1));
|
||||
if (this.rejectInsaneFees && fee.cmp(minFee.muln(10000)) > 0)
|
||||
return callback(new VerifyError('highfee', 'absurdly-high-fee', 0));
|
||||
|
||||
// Do this in the worker pool.
|
||||
tx.verifyAsync(null, true, Mempool.flags, function(err, result) {
|
||||
this.countAncestors(tx, function(err, count) {
|
||||
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('mandatory-script-verify-flag', 0));
|
||||
|
||||
return callback(new VerifyError('non-mandatory-script-verify-flag', 0));
|
||||
});
|
||||
if (count > Mempool.ANCESTOR_LIMIT) {
|
||||
return callback(new VerifyError(
|
||||
'nonstandard',
|
||||
'too-long-mempool-chain',
|
||||
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);
|
||||
};
|
||||
|
||||
@ -516,60 +746,73 @@ Mempool.prototype.checkTX = function checkTX(tx, peer) {
|
||||
};
|
||||
|
||||
Mempool.checkTX = function checkTX(tx, peer) {
|
||||
var i, input, output, size;
|
||||
var total = new bn(0);
|
||||
var uniq = {};
|
||||
var total = new bn(0);
|
||||
var i, input, output, size;
|
||||
|
||||
if (!peer)
|
||||
peer = DUMMY_PEER;
|
||||
|
||||
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)
|
||||
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)
|
||||
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++) {
|
||||
output = tx.outputs[i];
|
||||
|
||||
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)
|
||||
return peer.sendReject(tx, 'bad-txns-vout-toolarge', 100);
|
||||
return peer.sendReject(tx, 'invalid', 'bad-txns-vout-toolarge', 100);
|
||||
|
||||
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++) {
|
||||
input = tx.inputs[i];
|
||||
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;
|
||||
}
|
||||
|
||||
if (tx.isCoinbase()) {
|
||||
size = tx.inputs[0].script.getSize();
|
||||
if (size < 2 || size > 100)
|
||||
return peer.sendReject(tx, 'bad-cb-length', 100);
|
||||
return peer.sendReject(tx, 'invalid', 'bad-cb-length', 100);
|
||||
} else {
|
||||
for (i = 0; i < tx.inputs.length; i++) {
|
||||
input = tx.inputs[i];
|
||||
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;
|
||||
};
|
||||
|
||||
function VerifyError(reason, score) {
|
||||
/**
|
||||
* VerifyError
|
||||
*/
|
||||
|
||||
function VerifyError(code, reason, score) {
|
||||
Error.call(this);
|
||||
if (Error.captureStackTrace)
|
||||
Error.captureStackTrace(this, VerifyError);
|
||||
this.type = 'VerifyError';
|
||||
this.code = code;
|
||||
this.message = reason;
|
||||
this.reason = score === -1 ? null : reason;
|
||||
this.score = score;
|
||||
|
||||
@ -882,7 +882,7 @@ MTX.prototype.selectCoins = function selectCoins(unspent, options) {
|
||||
// Calculate max possible size after signing.
|
||||
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);
|
||||
// break;
|
||||
// }
|
||||
|
||||
@ -679,8 +679,8 @@ Peer.prototype.setMisbehavior = function setMisbehavior(dos) {
|
||||
return this.pool.setMisbehavior(this, dos);
|
||||
};
|
||||
|
||||
Peer.prototype.sendReject = function sendReject(obj, reason, dos) {
|
||||
return this.pool.reject(this, obj, reason, dos);
|
||||
Peer.prototype.sendReject = function sendReject(obj, code, 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) {
|
||||
peer.sendReject(block, reason, score);
|
||||
this.chain.on('verify-error', function(block, code, reason, score, peer) {
|
||||
peer.sendReject(block, code, reason, score);
|
||||
});
|
||||
|
||||
this.chain.on('fork', function(block, data, peer) {
|
||||
@ -1885,14 +1885,15 @@ Pool.prototype.isMisbehaving = function isMisbehaving(host) {
|
||||
return false;
|
||||
};
|
||||
|
||||
Pool.prototype.reject = function reject(peer, obj, reason, dos) {
|
||||
Pool.prototype.reject = function reject(peer, obj, code, reason, dos) {
|
||||
if (dos != null)
|
||||
peer.setMisbehavior(dos);
|
||||
|
||||
utils.debug('Rejecting %s %s: reason=%s',
|
||||
obj.type, obj.hash('hex'), reason);
|
||||
utils.debug('Rejecting %s %s: ccode=%s reason=%s',
|
||||
obj.type, obj.hash('hex'), code, reason);
|
||||
|
||||
peer.reject({
|
||||
ccode: code,
|
||||
reason: reason,
|
||||
data: obj.hash()
|
||||
});
|
||||
|
||||
@ -249,7 +249,12 @@ exports.reject = {
|
||||
nonstandard: 0x40,
|
||||
dust: 0x41,
|
||||
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) {
|
||||
|
||||
@ -580,9 +580,13 @@ Framer.headers = function _headers(block, writer) {
|
||||
|
||||
Framer.reject = function reject(details, 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.writeU8(constants.reject[details.ccode] || constants.reject.malformed);
|
||||
p.writeU8(constants.reject[ccode] || constants.reject.invalid);
|
||||
p.writeVarString(details.reason || '', 'ascii');
|
||||
if (details.data)
|
||||
p.writeHash(details.data);
|
||||
|
||||
@ -916,10 +916,11 @@ TX.prototype.maxSize = function maxSize() {
|
||||
return this.getVirtualSize();
|
||||
};
|
||||
|
||||
TX.prototype.getPriority = function getPriority(size) {
|
||||
var sum, i, input, age, height;
|
||||
TX.prototype.getPriority = function getPriority(height, size) {
|
||||
var sum, i, input, age;
|
||||
|
||||
height = this.height;
|
||||
if (height == null)
|
||||
height = this.height;
|
||||
|
||||
if (height === -1)
|
||||
height = null;
|
||||
@ -927,7 +928,9 @@ TX.prototype.getPriority = function getPriority(size) {
|
||||
if (!this.hasPrevout())
|
||||
return new bn(0);
|
||||
|
||||
size = size || this.maxSize();
|
||||
if (size == null)
|
||||
size = this.maxSize();
|
||||
|
||||
sum = new bn(0);
|
||||
|
||||
for (i = 0; i < this.inputs.length; i++) {
|
||||
@ -950,29 +953,31 @@ TX.prototype.getPriority = function getPriority(size) {
|
||||
return sum.divn(size);
|
||||
};
|
||||
|
||||
TX.prototype.isFree = function isFree(size) {
|
||||
TX.prototype.isFree = function isFree(height, size) {
|
||||
var priority;
|
||||
|
||||
if (!this.hasPrevout())
|
||||
return false;
|
||||
|
||||
size = size || this.maxSize();
|
||||
if (height == null)
|
||||
height = this.height;
|
||||
|
||||
if (size == null)
|
||||
size = this.maxSize();
|
||||
|
||||
if (size >= constants.tx.maxFreeSize)
|
||||
return false;
|
||||
|
||||
priority = this.getPriority();
|
||||
priority = this.getPriority(height, size);
|
||||
|
||||
return priority.cmp(constants.tx.freeThreshold) > 0;
|
||||
};
|
||||
|
||||
TX.prototype.getMinFee = function getMinFee(allowFree, size) {
|
||||
TX.prototype.getMinFee = function getMinFee(size) {
|
||||
var fee;
|
||||
|
||||
size = size || this.maxSize();
|
||||
|
||||
if (allowFree && this.isFree(size))
|
||||
return new bn(0);
|
||||
if (size == null)
|
||||
size = this.maxSize();
|
||||
|
||||
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.put(prefix + 't/t/' + hash, tx.toExtended());
|
||||
batch.put(prefix + 't/s/s/' + pad32(tx.ps) + '/' + hash, DUMMY);
|
||||
|
||||
tx.getAddresses().forEach(function(address) {
|
||||
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 prefix = this.prefix + '/';
|
||||
var hash = tx.hash('hex');
|
||||
var batch;
|
||||
|
||||
var unlock = this._lock(removeUnchecked, [tx, callback], force);
|
||||
var unlock = this._lock(removeUnchecked, [hash, callback], force);
|
||||
if (!unlock)
|
||||
return;
|
||||
|
||||
callback = utils.wrap(callback, unlock);
|
||||
|
||||
batch = this.db.batch();
|
||||
if (hash.hash)
|
||||
hash = hash.hash('hex');
|
||||
|
||||
batch.del(prefix + 't/t/' + 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) {
|
||||
this.getTX(hash, function(err, tx) {
|
||||
if (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 now = tip.ts;
|
||||
var age = 10 * 60 * 60;
|
||||
|
||||
var unlock = this._lock(zap, [tip, callback], force);
|
||||
if (!unlock)
|
||||
@ -1655,23 +1665,13 @@ TXPool.prototype.zap = function zap(tip, callback, force) {
|
||||
|
||||
callback = utils.wrap(callback, unlock);
|
||||
|
||||
// return this.getRange(null, {
|
||||
// start: 0,
|
||||
// end: now - age
|
||||
// }, function(err, txs) {
|
||||
// });
|
||||
|
||||
this.getPending(function(err, txs) {
|
||||
return this.getRange(null, {
|
||||
start: 0,
|
||||
end: now - age
|
||||
}, function(err, txs) {
|
||||
if (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) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
@ -98,7 +98,6 @@ WalletDB.prototype._init = function _init() {
|
||||
});
|
||||
|
||||
this.tx = new bcoin.txdb('w', this.db, {
|
||||
indexSpent: true,
|
||||
indexExtra: true,
|
||||
indexAddress: true,
|
||||
mapAddress: true,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user