From 5af8418fee23af1be2065382dda14a97034fdf56 Mon Sep 17 00:00:00 2001 From: CryptoManiac Date: Tue, 23 Feb 2016 05:39:08 +0300 Subject: [PATCH] Separate implementations of CHECKLOCKTIMEVERIFY and CHECKSEQUENCEVERIFY. --- src/script.cpp | 150 ++++++++++++++++++++++++++++++++++++++++---------------- src/script.h | 24 ++++++++-- 2 files changed, 127 insertions(+), 47 deletions(-) diff --git a/src/script.cpp b/src/script.cpp index c0da405..040c906 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -338,6 +338,83 @@ bool IsCanonicalSignature(const valtype &vchSig, unsigned int flags) { return IsDERSignature(vchSig, true, (flags & SCRIPT_VERIFY_LOW_S) != 0); } +bool CheckLockTime(const int64_t& nLockTime, const CTransaction &txTo, unsigned int nIn) +{ + // There are two kinds of nLockTime: lock-by-blockheight + // and lock-by-blocktime, distinguished by whether + // nLockTime < LOCKTIME_THRESHOLD. + // + // We want to compare apples to apples, so fail the script + // unless the type of nLockTime being tested is the same as + // the nLockTime in the transaction. + if (!( + (txTo.nLockTime < LOCKTIME_THRESHOLD && nLockTime < LOCKTIME_THRESHOLD) || + (txTo.nLockTime >= LOCKTIME_THRESHOLD && nLockTime >= LOCKTIME_THRESHOLD) + )) + return false; + + // Now that we know we're comparing apples-to-apples, the + // comparison is a simple numeric one. + if (nLockTime > (int64_t)txTo.nLockTime) + return false; + + // Finally the nLockTime feature can be disabled and thus + // CHECKLOCKTIMEVERIFY bypassed if every txin has been + // finalized by setting nSequence to maxint. The + // transaction would be allowed into the blockchain, making + // the opcode ineffective. + // + // Testing if this vin is not final is sufficient to + // prevent this condition. Alternatively we could test all + // inputs, but testing just this input minimizes the data + // required to prove correct CHECKLOCKTIMEVERIFY execution. + if (SEQUENCE_FINAL == txTo.vin[nIn].nSequence) + return false; + + return true; +} + +bool CheckSequence(const int64_t& nSequence, const CTransaction &txTo, unsigned int nIn) +{ + // Relative lock times are supported by comparing the passed + // in operand to the sequence number of the input. + const int64_t txToSequence = (int64_t)txTo.vin[nIn].nSequence; + + // Sequence numbers with their most significant bit set are not + // consensus constrained. Testing that the transaction's sequence + // number do not have this bit set prevents using this property + // to get around a CHECKSEQUENCEVERIFY check. + if (txToSequence & SEQUENCE_LOCKTIME_DISABLE_FLAG) + return false; + + // Mask off any bits that do not have consensus-enforced meaning + // before doing the integer comparisons + const uint32_t nLockTimeMask = SEQUENCE_LOCKTIME_TYPE_FLAG | SEQUENCE_LOCKTIME_MASK; + const int64_t txToSequenceMasked = txToSequence & nLockTimeMask; + const int64_t nSequenceMasked = nSequence & nLockTimeMask; + + // There are two kinds of nSequence: lock-by-blockheight + // and lock-by-blocktime, distinguished by whether + // nSequenceMasked < CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG. + // + // We want to compare apples to apples, so fail the script + // unless the type of nSequenceMasked being tested is the same as + // the nSequenceMasked in the transaction. + if (!( + (txToSequenceMasked < SEQUENCE_LOCKTIME_TYPE_FLAG && nSequenceMasked < SEQUENCE_LOCKTIME_TYPE_FLAG) || + (txToSequenceMasked >= SEQUENCE_LOCKTIME_TYPE_FLAG && nSequenceMasked >= SEQUENCE_LOCKTIME_TYPE_FLAG) + )) { + return false; + } + + // Now that we know we're comparing apples-to-apples, the + // comparison is a simple numeric one. + if (nSequenceMasked > txToSequenceMasked) + return false; + + return true; +} + bool EvalScript(vector >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType) { CAutoBN_CTX pctx; @@ -486,71 +563,58 @@ bool EvalScript(vector >& stack, const CScript& script, co // CHECKLOCKTIMEVERIFY // // (nLockTime -- nLockTime) - if (!(flags & SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY)) - break; // treat as a NOP is not enabled + if (!(flags & SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY)) { + // treat as a NOP2 if not enabled + break; + } + if (stack.size() < 1) return false; - const CBigNum nLockTime = CastToBigNum(stacktop(-1)); - if (nLockTime < 0) - return false; // Negative argument is senseless. - if (!( // We can have either lock-by-blockheight or lock-by-blocktime. - (txTo.nLockTime < LOCKTIME_THRESHOLD && nLockTime < LOCKTIME_THRESHOLD) || - (txTo.nLockTime >= LOCKTIME_THRESHOLD && nLockTime >= LOCKTIME_THRESHOLD) - )) - return false; + CBigNum nLockTime = CastToBigNum(stacktop(-1)); - // Now we can perform a simple numerical comparison - if (nLockTime > (int64_t)txTo.nLockTime) + // In the rare event that the argument may be < 0 due to + // some arithmetic being done first, you can always use + // 0 MAX CHECKLOCKTIMEVERIFY. + if (nLockTime < 0) return false; - // Finally the nLockTime feature can be disabled and thus - // CHECKLOCKTIMEVERIFY bypassed if every txin has been - // finalized by setting nSequence to maxint. The - // transaction would be allowed into the blockchain, making - // the opcode ineffective. - // - // Testing if this vin is not final is sufficient to - // prevent this condition. Alternatively we could test all - // inputs, but testing just this input minimizes the data - // required to prove correct CHECKLOCKTIMEVERIFY execution. - if (txTo.vin[nIn].IsFinal()) + // Actually compare the specified lock time with the transaction. + if (!CheckLockTime(nLockTime.getuint64(), txTo, nIn)) return false; + break; } case OP_CHECKSEQUENCEVERIFY: { - if (!(flags & SCRIPT_VERIFY_CHECKSEQUENCEVERIFY)) - // treat as a NOP if not enabled + if (!(flags & SCRIPT_VERIFY_CHECKSEQUENCEVERIFY)) { + // treat as a NOP3 not enabled break; + } + if (stack.size() < 1) return false; - const CBigNum nInvSequence = CastToBigNum(stacktop(-1)); + + // nSequence, like nLockTime, is a 32-bit unsigned integer + // field. See the comment in CHECKLOCKTIMEVERIFY regarding + // 5-byte numeric operands. + CBigNum nSequence = CastToBigNum(stacktop(-1)); // In the rare event that the argument may be < 0 due to // some arithmetic being done first, you can always use // 0 MAX CHECKSEQUENCEVERIFY. - if (nInvSequence < 0) - return false; // negative nSequence is senseless - - // Relative lock times are supported by comparing the passed - // in lock time to the sequence number of the input. All other - // logic is the same, all that differs is what we are comparing - // the lock time to. - int64_t txToLockTime = (int64_t)~txTo.vin[nIn].nSequence; - if (txToLockTime >= SEQUENCE_THRESHOLD) + if (nSequence < 0) return false; - if (!( - (txToLockTime < SEQUENCE_THRESHOLD && nInvSequence < SEQUENCE_THRESHOLD) || - (txToLockTime >= SEQUENCE_THRESHOLD && nInvSequence >= SEQUENCE_THRESHOLD) - )) - return false; + // To provide for future soft-fork extensibility, if the + // operand has the disabled lock-time flag set, + // CHECKSEQUENCEVERIFY behaves as a NOP. + if ((nSequence.getint32() & SEQUENCE_LOCKTIME_DISABLE_FLAG) != 0) + break; - // Now that we know we're comparing apples-to-apples, the - // comparison is a simple numeric one. - if (nInvSequence > txToLockTime) + // Compare the specified sequence number with the input. + if (!CheckSequence(nSequence.getuint64(), txTo, nIn)) return false; break; diff --git a/src/script.h b/src/script.h index 98d0965..2ff2325 100644 --- a/src/script.h +++ b/src/script.h @@ -19,10 +19,27 @@ class CTransaction; static const unsigned int MAX_SCRIPT_ELEMENT_SIZE = 520; // bytes -// Threshold for inverted nSequence: below this value it is interpreted -// as a relative lock-time, otherwise ignored. +/* Setting nSequence to this value for every input in a transaction + * disables nLockTime. */ +static const uint32_t SEQUENCE_FINAL = 0xffffffff; + +/* Threshold for inverted nSequence: below this value it is interpreted + * as a relative lock-time, otherwise ignored. */ static const uint32_t SEQUENCE_THRESHOLD = (1 << 31); +/* If this flag set, CTxIn::nSequence is NOT interpreted as a + * relative lock-time. */ +static const uint32_t SEQUENCE_LOCKTIME_DISABLE_FLAG = (1 << 31); + +/* If CTxIn::nSequence encodes a relative lock-time and this flag + * is set, the relative lock-time has units of 512 seconds, + * otherwise it specifies blocks with a granularity of 1. */ +static const uint32_t SEQUENCE_LOCKTIME_TYPE_FLAG = (1 << 22); + +/* If CTxIn::nSequence encodes a relative lock-time, this mask is + * applied to extract that lock-time from the sequence field. */ +static const uint32_t SEQUENCE_LOCKTIME_MASK = 0x0000ffff; + /** IsMine() return codes */ enum isminetype { @@ -53,8 +70,7 @@ enum SCRIPT_VERIFY_NOCACHE = (1U << 3), // do not store results in signature cache (but do query it) SCRIPT_VERIFY_NULLDUMMY = (1U << 4), // verify dummy stack item consumed by CHECKMULTISIG is of zero-length SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 9), - SCRIPT_VERIFY_CHECKSEQUENCEVERIFY = (1U << 9) - + SCRIPT_VERIFY_CHECKSEQUENCEVERIFY = (1U << 10) }; // Strict verification: -- 1.7.1