tx/script/chain: refactor locktime handling.
This commit is contained in:
parent
73b84cc1a7
commit
b87f557823
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user