check sequence locks in mempool.

This commit is contained in:
Christopher Jeffrey 2016-03-28 03:10:36 -07:00
parent e89ed67843
commit aff7c0e360
4 changed files with 221 additions and 106 deletions

View File

@ -430,7 +430,18 @@ Mempool.prototype.verify = function verify(tx, callback) {
mandatory |= constants.flags.VERIFY_WITNESS; mandatory |= constants.flags.VERIFY_WITNESS;
} }
if (this.requireStandard && !tx.isStandardInputs(flags)) { this.checkMempoolLocks(tx, flags, function(err, result) {
if (err)
return callback(err);
if (!result) {
return callback(new VerifyError(
'nonstandard',
'non-BIP68-final',
0));
}
if (self.requireStandard && !tx.isStandardInputs(flags)) {
return callback(new VerifyError( return callback(new VerifyError(
'nonstandard', 'nonstandard',
'bad-txns-nonstandard-inputs', 'bad-txns-nonstandard-inputs',
@ -450,7 +461,7 @@ 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 (self.chain.height - coin.height < constants.tx.coinbaseMaturity) {
return callback(new VerifyError( return callback(new VerifyError(
'invalid', 'invalid',
'bad-txns-premature-spend-of-coinbase', 'bad-txns-premature-spend-of-coinbase',
@ -483,12 +494,16 @@ Mempool.prototype.verify = function verify(tx, callback) {
if (fee.cmpn(0) < 0) if (fee.cmpn(0) < 0)
return callback(new VerifyError('invalid', '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('invalid', 'bad-txns-fee-outofrange', 100)); return callback(new VerifyError(
'invalid',
'bad-txns-fee-outofrange',
100));
}
minFee = tx.getMinFee(); minFee = tx.getMinFee();
if (fee.cmp(minFee) < 0) { if (fee.cmp(minFee) < 0) {
if (this.relayPriority && fee.cmpn(0) === 0) { if (self.relayPriority && fee.cmpn(0) === 0) {
free = tx.isFree(height); free = tx.isFree(height);
if (!free) { if (!free) {
return callback(new VerifyError( return callback(new VerifyError(
@ -504,29 +519,29 @@ Mempool.prototype.verify = function verify(tx, callback) {
} }
} }
if (this.limitFree && free) { if (self.limitFree && free) {
now = utils.now(); now = utils.now();
if (!this.lastTime) if (!self.lastTime)
this.lastTime = now; self.lastTime = now;
this.freeCount *= Math.pow(1 - 1 / 600, now - this.lastTime); self.freeCount *= Math.pow(1 - 1 / 600, now - self.lastTime);
this.lastTime = now; self.lastTime = now;
if (this.freeCount > this.limitFreeRelay * 10 * 1000) { if (self.freeCount > self.limitFreeRelay * 10 * 1000) {
return callback(new VerifyError( return callback(new VerifyError(
'insufficientfee', 'insufficientfee',
'rate limited free transaction', 'rate limited free transaction',
0)); 0));
} }
this.freeCount += tx.getSize(); self.freeCount += tx.getSize();
} }
if (this.rejectInsaneFees && fee.cmp(minFee.muln(10000)) > 0) if (self.rejectInsaneFees && fee.cmp(minFee.muln(10000)) > 0)
return callback(new VerifyError('highfee', 'absurdly-high-fee', 0)); return callback(new VerifyError('highfee', 'absurdly-high-fee', 0));
this.countAncestors(tx, function(err, count) { self.countAncestors(tx, function(err, count) {
if (err) if (err)
return callback(err); return callback(err);
@ -564,6 +579,7 @@ Mempool.prototype.verify = function verify(tx, callback) {
return callback(); return callback();
}); });
}); });
});
}; };
Mempool.prototype.countAncestors = function countAncestors(tx, callback) { Mempool.prototype.countAncestors = function countAncestors(tx, callback) {
@ -814,6 +830,104 @@ Mempool.checkTX = function checkTX(tx, peer) {
return true; return true;
}; };
Mempool.prototype.getLocks = function getLocks(tx, flags, entry, callback) {
var self = this;
var mask = constants.sequenceLocktimeMask
var granularity = constants.sequenceLocktimeGranularity;
var disableFlag = constants.sequenceLocktimeDisableFlag;
var typeFlag = constants.sequenceLocktimeTypeFlag;
var hasFlag = flags & constants.flags.VERIFY_CHECKSEQUENCEVERIFY;
var minHeight = -1;
var minTime = -1;
var coinHeight;
if ((tx.version >>> 0) < 2 || !hasFlag)
return utils.asyncify(callback)(null, minHeight, minTime);
utils.forEachSerial(tx.inputs, function(input, next) {
if (input.sequence & disableFlag)
return next();
coinHeight = coin.height === -1
? self.chain.tip + 1
: coin.height;
if ((input.sequence & typeFlag) === 0) {
coinHeight += (input.sequence & mask) - 1;
minHeight = Math.max(minHeight, coinHeight);
return next();
}
entry.getAncestorByHeight(Math.max(coinHeight - 1, 0), function(err, entry) {
if (err)
return next(err);
assert(entry, 'Database is corrupt.');
entry.getMedianTimeAsync(function(err, coinTime) {
if (err)
return next(err);
coinTime += ((input.sequence & mask) << granularity) - 1;
minTime = Math.max(minTime, coinTime);
next();
});
});
}, function(err) {
if (err)
return callback(err);
return callback(null, minHeight, minTime);
});
};
Mempool.prototype.evalLocks = function evalLocks(entry, minHeight, minTime, callback) {
if (minHeight >= entry.height)
return utils.asyncify(callback)(null, false);
if (minTime === -1)
return utils.asyncify(callback)(null, true);
entry.getMedianTimeAsync(function(err, medianTime) {
if (err)
return callback(err);
if (minTime >= medianTime)
return callback(null, false);
return callback(null, true);
});
}
Mempool.prototype.checkLocks = function checkLocks(tx, flags, entry, callback) {
var self = this;
this.getLocks(tx, flags, entry, function(err, minHeight, minTime) {
if (err)
return callback(err);
self.evalLocks(entry, minHeight, minTime, callback);
});
}
Mempool.prototype.checkMempoolLocks = function checkMempoolLocks(tx, flags, callback) {
var self = this;
var tip = this.chain.tip;
var index = new bcoin.chainblock(this.chain, {
hash: utils.toHex(constants.zeroHash),
version: tip.version,
prevBlock: tip.hash,
merkleRoot: utils.toHex(constants.zeroHash),
ts: utils.now(),
bits: 0,
nonce: 0,
height: tip.height + 1,
chainwork: tip.chainwork
});
this.checkLocks(tx, flags, index, callback);
}
/** /**
* VerifyError * VerifyError
*/ */

View File

@ -273,8 +273,9 @@ exports.hd = {
exports.locktimeThreshold = 500000000; // Tue Nov 5 00:53:20 1985 UTC exports.locktimeThreshold = 500000000; // Tue Nov 5 00:53:20 1985 UTC
exports.sequenceLocktimeDisableFlag = 0x80000000; // (1 << 31) exports.sequenceLocktimeDisableFlag = (1 << 31) >>> 0;
exports.sequenceLocktimeTypeFlag = 1 << 22; exports.sequenceLocktimeTypeFlag = 1 << 22;
exports.sequenceLocktimeGranularity = 9;
exports.sequenceLocktimeMask = 0x0000ffff; exports.sequenceLocktimeMask = 0x0000ffff;
exports.oneHash = new Buffer( exports.oneHash = new Buffer(

View File

@ -1084,7 +1084,7 @@ Script.checkSequence = function checkSequence(sequence, tx, i) {
var txSequence = tx.inputs[i].sequence; var txSequence = tx.inputs[i].sequence;
var locktimeMask, txSequenceMasked, sequenceMasked; var locktimeMask, txSequenceMasked, sequenceMasked;
if (tx.version < 2) if ((tx.version >>> 0) < 2)
return false; return false;
if (txSequence & constants.sequenceLocktimeDisableFlag) if (txSequence & constants.sequenceLocktimeDisableFlag)

View File

@ -707,7 +707,7 @@ TX.prototype.isStandard = function isStandard(flags, ts, height, ret) {
if (flags & constants.flags.VERIFY_CHECKSEQUENCEVERIFY) if (flags & constants.flags.VERIFY_CHECKSEQUENCEVERIFY)
maxVersion = Math.max(maxVersion, 2); maxVersion = Math.max(maxVersion, 2);
if (this.version < 1 || this.version > maxVersion) { if ((this.version >>> 0) < 1 || (this.version >>> 0) > maxVersion) {
ret.reason = 'version'; ret.reason = 'version';
return false; return false;
} }