diff --git a/lib/blockchain/chain.js b/lib/blockchain/chain.js index 13b02375..bfbdb59f 100644 --- a/lib/blockchain/chain.js +++ b/lib/blockchain/chain.js @@ -2302,19 +2302,22 @@ Chain.prototype.verifyFinal = async function verifyFinal(prev, tx, flags) { */ Chain.prototype.getLocks = async function getLocks(prev, tx, view, flags) { - const mask = consensus.SEQUENCE_MASK; - const granularity = consensus.SEQUENCE_GRANULARITY; - const disableFlag = consensus.SEQUENCE_DISABLE_FLAG; - const typeFlag = consensus.SEQUENCE_TYPE_FLAG; - const hasFlag = flags & common.lockFlags.VERIFY_SEQUENCE; + const GRANULARITY = consensus.SEQUENCE_GRANULARITY; + const DISABE_FLAG = consensus.SEQUENCE_DISABLE_FLAG; + const TYPE_FLAG = consensus.SEQUENCE_TYPE_FLAG; + const MASK = consensus.SEQUENCE_MASK; + + if (!(flags & common.lockFlags.VERIFY_SEQUENCE)) + return [-1, -1]; + + if (tx.isCoinbase() || tx.version < 2) + return [-1, -1]; + let minHeight = -1; let minTime = -1; - if (tx.isCoinbase() || tx.version < 2 || !hasFlag) - return [minHeight, minTime]; - for (const {prevout, sequence} of tx.inputs) { - if (sequence & disableFlag) + if (sequence & DISABE_FLAG) continue; let height = view.getHeight(prevout); @@ -2322,8 +2325,8 @@ Chain.prototype.getLocks = async function getLocks(prev, tx, view, flags) { if (height === -1) height = this.height + 1; - if ((sequence & typeFlag) === 0) { - height += (sequence & mask) - 1; + if (!(sequence & TYPE_FLAG)) { + height += (sequence & MASK) - 1; minHeight = Math.max(minHeight, height); continue; } @@ -2334,7 +2337,7 @@ Chain.prototype.getLocks = async function getLocks(prev, tx, view, flags) { assert(entry, 'Database is corrupt.'); let time = await entry.getMedianTime(); - time += ((sequence & mask) << granularity) - 1; + time += ((sequence & MASK) << GRANULARITY) - 1; minTime = Math.max(minTime, time); } diff --git a/lib/primitives/mtx.js b/lib/primitives/mtx.js index 470581ee..6ae12cf4 100644 --- a/lib/primitives/mtx.js +++ b/lib/primitives/mtx.js @@ -1414,9 +1414,10 @@ MTX.prototype.setSequence = function setSequence(index, locktime, seconds) { if (seconds) { locktime >>>= consensus.SEQUENCE_GRANULARITY; - locktime = consensus.SEQUENCE_TYPE_FLAG | locktime; + locktime &= consensus.SEQUENCE_MASK; + locktime |= consensus.SEQUENCE_TYPE_FLAG; } else { - locktime = consensus.SEQUENCE_MASK & locktime; + locktime &= consensus.SEQUENCE_MASK; } input.sequence = locktime; diff --git a/lib/primitives/tx.js b/lib/primitives/tx.js index 88628b2d..95ef010c 100644 --- a/lib/primitives/tx.js +++ b/lib/primitives/tx.js @@ -1225,23 +1225,22 @@ TX.prototype.isFinal = function isFinal(height, time) { * Verify the absolute locktime of a transaction. * Called by OP_CHECKLOCKTIMEVERIFY. * @param {Number} index - Index of input being verified. - * @param {Number} locktime - Locktime to verify against. + * @param {Number} predicate - Locktime to verify against. * @returns {Boolean} */ -TX.prototype.verifyLocktime = function verifyLocktime(index, locktime) { +TX.prototype.verifyLocktime = function verifyLocktime(index, predicate) { const THRESHOLD = consensus.LOCKTIME_THRESHOLD; const input = this.inputs[index]; 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) - || (this.locktime >= THRESHOLD && locktime >= THRESHOLD))) { + // Locktimes must be of the same type (blocks or seconds). + if ((this.locktime < THRESHOLD) !== (predicate < THRESHOLD)) return false; - } - if (locktime > this.locktime) + if (predicate > this.locktime) return false; if (input.sequence === 0xffffffff) @@ -1254,38 +1253,38 @@ TX.prototype.verifyLocktime = function verifyLocktime(index, locktime) { * Verify the relative locktime of an input. * Called by OP_CHECKSEQUENCEVERIFY. * @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} */ -TX.prototype.verifySequence = function verifySequence(index, locktime) { +TX.prototype.verifySequence = function verifySequence(index, predicate) { const DISABLE_FLAG = consensus.SEQUENCE_DISABLE_FLAG; const TYPE_FLAG = consensus.SEQUENCE_TYPE_FLAG; - const SEQUENCE_MASK = consensus.SEQUENCE_MASK; + const MASK = consensus.SEQUENCE_MASK; const input = this.inputs[index]; 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; + // Version must be >=2. if (this.version < 2) 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; - const mask = TYPE_FLAG | SEQUENCE_MASK; - const sequence = input.sequence & mask; - const predicate = locktime & mask; - - if (!((sequence < TYPE_FLAG && predicate < TYPE_FLAG) - || (sequence >= TYPE_FLAG && predicate >= TYPE_FLAG))) { + // Locktimes must be of the same type (blocks or seconds). + if ((input.sequence & TYPE_FLAG) !== (predicate & TYPE_FLAG)) return false; - } - if (predicate > sequence) + if ((predicate & MASK) > (input.sequence & MASK)) return false; return true; diff --git a/lib/script/script.js b/lib/script/script.js index c5a10d61..751ea81e 100644 --- a/lib/script/script.js +++ b/lib/script/script.js @@ -652,12 +652,12 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers if (stack.length === 0) 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); - locktime = locktime.toDouble(); + const locktime = num.toDouble(); if (!tx.verifyLocktime(index, locktime)) 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) 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); - locktime = locktime.toDouble(); + const locktime = num.toDouble(); if (!tx.verifySequence(index, locktime)) throw new ScriptError('UNSATISFIED_LOCKTIME', op, ip);