tx/script/chain: refactor locktime handling.

This commit is contained in:
Christopher Jeffrey 2017-08-27 19:07:57 -07:00
parent 73b84cc1a7
commit b87f557823
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
4 changed files with 44 additions and 41 deletions

View File

@ -2302,19 +2302,22 @@ Chain.prototype.verifyFinal = async function verifyFinal(prev, tx, flags) {
*/ */
Chain.prototype.getLocks = async function getLocks(prev, tx, view, flags) { Chain.prototype.getLocks = async function getLocks(prev, tx, view, flags) {
const mask = consensus.SEQUENCE_MASK; const GRANULARITY = consensus.SEQUENCE_GRANULARITY;
const granularity = consensus.SEQUENCE_GRANULARITY; const DISABE_FLAG = consensus.SEQUENCE_DISABLE_FLAG;
const disableFlag = consensus.SEQUENCE_DISABLE_FLAG; const TYPE_FLAG = consensus.SEQUENCE_TYPE_FLAG;
const typeFlag = consensus.SEQUENCE_TYPE_FLAG; const MASK = consensus.SEQUENCE_MASK;
const hasFlag = flags & common.lockFlags.VERIFY_SEQUENCE;
if (!(flags & common.lockFlags.VERIFY_SEQUENCE))
return [-1, -1];
if (tx.isCoinbase() || tx.version < 2)
return [-1, -1];
let minHeight = -1; let minHeight = -1;
let minTime = -1; let minTime = -1;
if (tx.isCoinbase() || tx.version < 2 || !hasFlag)
return [minHeight, minTime];
for (const {prevout, sequence} of tx.inputs) { for (const {prevout, sequence} of tx.inputs) {
if (sequence & disableFlag) if (sequence & DISABE_FLAG)
continue; continue;
let height = view.getHeight(prevout); let height = view.getHeight(prevout);
@ -2322,8 +2325,8 @@ Chain.prototype.getLocks = async function getLocks(prev, tx, view, flags) {
if (height === -1) if (height === -1)
height = this.height + 1; height = this.height + 1;
if ((sequence & typeFlag) === 0) { if (!(sequence & TYPE_FLAG)) {
height += (sequence & mask) - 1; height += (sequence & MASK) - 1;
minHeight = Math.max(minHeight, height); minHeight = Math.max(minHeight, height);
continue; continue;
} }
@ -2334,7 +2337,7 @@ Chain.prototype.getLocks = async function getLocks(prev, tx, view, flags) {
assert(entry, 'Database is corrupt.'); assert(entry, 'Database is corrupt.');
let time = await entry.getMedianTime(); let time = await entry.getMedianTime();
time += ((sequence & mask) << granularity) - 1; time += ((sequence & MASK) << GRANULARITY) - 1;
minTime = Math.max(minTime, time); minTime = Math.max(minTime, time);
} }

View File

@ -1414,9 +1414,10 @@ MTX.prototype.setSequence = function setSequence(index, locktime, seconds) {
if (seconds) { if (seconds) {
locktime >>>= consensus.SEQUENCE_GRANULARITY; locktime >>>= consensus.SEQUENCE_GRANULARITY;
locktime = consensus.SEQUENCE_TYPE_FLAG | locktime; locktime &= consensus.SEQUENCE_MASK;
locktime |= consensus.SEQUENCE_TYPE_FLAG;
} else { } else {
locktime = consensus.SEQUENCE_MASK & locktime; locktime &= consensus.SEQUENCE_MASK;
} }
input.sequence = locktime; input.sequence = locktime;

View File

@ -1225,23 +1225,22 @@ TX.prototype.isFinal = function isFinal(height, time) {
* Verify the absolute locktime of a transaction. * Verify the absolute locktime of a transaction.
* Called by OP_CHECKLOCKTIMEVERIFY. * Called by OP_CHECKLOCKTIMEVERIFY.
* @param {Number} index - Index of input being verified. * @param {Number} index - Index of input being verified.
* @param {Number} locktime - Locktime to verify against. * @param {Number} predicate - Locktime to verify against.
* @returns {Boolean} * @returns {Boolean}
*/ */
TX.prototype.verifyLocktime = function verifyLocktime(index, locktime) { TX.prototype.verifyLocktime = function verifyLocktime(index, predicate) {
const THRESHOLD = consensus.LOCKTIME_THRESHOLD; const THRESHOLD = consensus.LOCKTIME_THRESHOLD;
const input = this.inputs[index]; const input = this.inputs[index];
assert(input, 'Input does not exist.'); assert(input, 'Input does not exist.');
assert(locktime >= 0, 'Locktime must be non-negative.'); assert(predicate >= 0, 'Locktime must be non-negative.');
if (!((this.locktime < THRESHOLD && locktime < THRESHOLD) // Locktimes must be of the same type (blocks or seconds).
|| (this.locktime >= THRESHOLD && locktime >= THRESHOLD))) { if ((this.locktime < THRESHOLD) !== (predicate < THRESHOLD))
return false; return false;
}
if (locktime > this.locktime) if (predicate > this.locktime)
return false; return false;
if (input.sequence === 0xffffffff) if (input.sequence === 0xffffffff)
@ -1254,38 +1253,38 @@ TX.prototype.verifyLocktime = function verifyLocktime(index, locktime) {
* Verify the relative locktime of an input. * Verify the relative locktime of an input.
* Called by OP_CHECKSEQUENCEVERIFY. * Called by OP_CHECKSEQUENCEVERIFY.
* @param {Number} index - Index of input being verified. * @param {Number} index - Index of input being verified.
* @param {Number} locktime - Sequence locktime to verify against. * @param {Number} predicate - Relative locktime to verify against.
* @returns {Boolean} * @returns {Boolean}
*/ */
TX.prototype.verifySequence = function verifySequence(index, locktime) { TX.prototype.verifySequence = function verifySequence(index, predicate) {
const DISABLE_FLAG = consensus.SEQUENCE_DISABLE_FLAG; const DISABLE_FLAG = consensus.SEQUENCE_DISABLE_FLAG;
const TYPE_FLAG = consensus.SEQUENCE_TYPE_FLAG; const TYPE_FLAG = consensus.SEQUENCE_TYPE_FLAG;
const SEQUENCE_MASK = consensus.SEQUENCE_MASK; const MASK = consensus.SEQUENCE_MASK;
const input = this.inputs[index]; const input = this.inputs[index];
assert(input, 'Input does not exist.'); assert(input, 'Input does not exist.');
assert(locktime >= 0, 'Locktime must be non-negative.'); assert(predicate >= 0, 'Locktime must be non-negative.');
if ((locktime & DISABLE_FLAG) !== 0) // For future softfork capability.
if (predicate & DISABLE_FLAG)
return true; return true;
// Version must be >=2.
if (this.version < 2) if (this.version < 2)
return false; return false;
if ((input.sequence & DISABLE_FLAG) !== 0) // Cannot use the disable flag without
// the predicate also having the disable
// flag (for future softfork capability).
if (input.sequence & DISABLE_FLAG)
return false; return false;
const mask = TYPE_FLAG | SEQUENCE_MASK; // Locktimes must be of the same type (blocks or seconds).
const sequence = input.sequence & mask; if ((input.sequence & TYPE_FLAG) !== (predicate & TYPE_FLAG))
const predicate = locktime & mask;
if (!((sequence < TYPE_FLAG && predicate < TYPE_FLAG)
|| (sequence >= TYPE_FLAG && predicate >= TYPE_FLAG))) {
return false; return false;
}
if (predicate > sequence) if ((predicate & MASK) > (input.sequence & MASK))
return false; return false;
return true; return true;

View File

@ -652,12 +652,12 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
if (stack.length === 0) if (stack.length === 0)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip); throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
let locktime = stack.getNum(-1, minimal, 5); const num = stack.getNum(-1, minimal, 5);
if (locktime.isNeg()) if (num.isNeg())
throw new ScriptError('NEGATIVE_LOCKTIME', op, ip); throw new ScriptError('NEGATIVE_LOCKTIME', op, ip);
locktime = locktime.toDouble(); const locktime = num.toDouble();
if (!tx.verifyLocktime(index, locktime)) if (!tx.verifyLocktime(index, locktime))
throw new ScriptError('UNSATISFIED_LOCKTIME', op, ip); throw new ScriptError('UNSATISFIED_LOCKTIME', op, ip);
@ -678,12 +678,12 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers
if (stack.length === 0) if (stack.length === 0)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip); throw new ScriptError('INVALID_STACK_OPERATION', op, ip);
let locktime = stack.getNum(-1, minimal, 5); const num = stack.getNum(-1, minimal, 5);
if (locktime.isNeg()) if (num.isNeg())
throw new ScriptError('NEGATIVE_LOCKTIME', op, ip); throw new ScriptError('NEGATIVE_LOCKTIME', op, ip);
locktime = locktime.toDouble(); const locktime = num.toDouble();
if (!tx.verifySequence(index, locktime)) if (!tx.verifySequence(index, locktime))
throw new ScriptError('UNSATISFIED_LOCKTIME', op, ip); throw new ScriptError('UNSATISFIED_LOCKTIME', op, ip);