Published: 2018-12-18

BIP112(CHECKSEQUENCEVERIFY)

1 BIP地址

BIP112

BIP: 112
Layer: Consensus (soft fork)
Title: CHECKSEQUENCEVERIFY
Author: BtcDrak <btcdrak@gmail.com>
        Mark Friedenbach <mark@friedenbach.org>
        Eric Lombrozo <elombrozo@gmail.com>
Comments-Summary: No comments yet.
Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0112
Status: Final
Type: Standards Track
Created: 2015-08-10
License: PD

2 原因和目的

BIP112 定义了新的比特币脚本操作符 CHECKSEQUENCEVERIFY ,和BIP68(nSequence)组合使用,提供将transaction的output锁定到一定期限后(时间期限或者block个数期限)才可以被花费使用的能力。

BIP68(nSequence)重新定义TxIn的sequence字段为relative lock time, 使得"non-final" transaction直到一定期限后才能被包含进一个block。 通过这一限制,新的操作符 CHECKSEQUENCEVERIFY 使得脚本拥有控制生成的transaction的outputs冻结一段时间才能被花费的能力。

CHECKSEQUENCEVERIFY 赋予了脚本能够访问TxIn的sequence字段的能力。这也使得像许多像"三方委托"(escrow)、"支付通道"(patment channels)、"bidirectional pegs"成为可能。

3 部署

采用bip9的bit 0部署,必须和BIP68,BIP113使用相同的部署机制。

mainnet上,开始时间是"midnight 1st May 2016 UTC (Epoch timestamp 1462060800)", 超时时间是"midnight 1st May 2017 UTC (Epoch timestamp 1493596800)."

4 相关实现

CHECKSEQUENCEVERIFY 的验证逻辑:

// opcodeCheckSequenceVerify compares the top item on the data stack to the
// LockTime field of the transaction containing the script signature
// validating if the transaction outputs are spendable yet.  If flag
// ScriptVerifyCheckSequenceVerify is not set, the code continues as if OP_NOP3
// were executed.
func opcodeCheckSequenceVerify(op *parsedOpcode, vm *Engine) error {
        // If the ScriptVerifyCheckSequenceVerify script flag is not set, treat
        // opcode as OP_NOP3 instead.
        if !vm.hasFlag(ScriptVerifyCheckSequenceVerify) {
                if vm.hasFlag(ScriptDiscourageUpgradableNops) {
                        return scriptError(ErrDiscourageUpgradableNOPs,
                                "OP_NOP3 reserved for soft-fork upgrades")
                }
                return nil
        }

        // The current transaction sequence is a uint32 resulting in a maximum
        // sequence of 2^32-1.  However, scriptNums are signed and therefore a
        // standard 4-byte scriptNum would only support up to a maximum of
        // 2^31-1.  Thus, a 5-byte scriptNum is used here since it will support
        // up to 2^39-1 which allows sequences beyond the current sequence
        // limit.
        //
        // PeekByteArray is used here instead of PeekInt because we do not want
        // to be limited to a 4-byte integer for reasons specified above.
        so, err := vm.dstack.PeekByteArray(0)
        if err != nil {
                return err
        }
        stackSequence, err := makeScriptNum(so, vm.dstack.verifyMinimalData, 5)
        if err != nil {
                return err
        }

        // In the rare event that the argument needs to be < 0 due to some
        // arithmetic being done first, you can always use
        // 0 OP_MAX OP_CHECKSEQUENCEVERIFY.
        if stackSequence < 0 {
                str := fmt.Sprintf("negative sequence: %d", stackSequence)
                return scriptError(ErrNegativeLockTime, str)
        }

        sequence := int64(stackSequence)

        // To provide for future soft-fork extensibility, if the
        // operand has the disabled lock-time flag set,
        // CHECKSEQUENCEVERIFY behaves as a NOP.
        if sequence&int64(wire.SequenceLockTimeDisabled) != 0 {
                return nil
        }

        // Transaction version numbers not high enough to trigger CSV rules must
        // fail.
        if vm.tx.Version < 2 {
                str := fmt.Sprintf("invalid transaction version: %d",
                        vm.tx.Version)
                return scriptError(ErrUnsatisfiedLockTime, str)
        }

        // Sequence numbers with their most significant bit set are not
        // consensus constrained. Testing that the transaction's sequence
        // number does not have this bit set prevents using this property
        // to get around a CHECKSEQUENCEVERIFY check.
        txSequence := int64(vm.tx.TxIn[vm.txIdx].Sequence)
        if txSequence&int64(wire.SequenceLockTimeDisabled) != 0 {
                str := fmt.Sprintf("transaction sequence has sequence "+
                        "locktime disabled bit set: 0x%x", txSequence)
                return scriptError(ErrUnsatisfiedLockTime, str)
        }

        // Mask off non-consensus bits before doing comparisons.
        lockTimeMask := int64(wire.SequenceLockTimeIsSeconds |
                wire.SequenceLockTimeMask)
        return verifyLockTime(txSequence&lockTimeMask,
                wire.SequenceLockTimeIsSeconds, sequence&lockTimeMask)
}


// verifyLockTime is a helper function used to validate locktimes.
func verifyLockTime(txLockTime, threshold, lockTime int64) error {
        // The lockTimes in both the script and transaction must be of the same
        // type.
        if !((txLockTime < threshold && lockTime < threshold) ||
                (txLockTime >= threshold && lockTime >= threshold)) {
                str := fmt.Sprintf("mismatched locktime types -- tx locktime "+
                        "%d, stack locktime %d", txLockTime, lockTime)
                return scriptError(ErrUnsatisfiedLockTime, str)
        }

        if lockTime > txLockTime {
                str := fmt.Sprintf("locktime requirement not satisfied -- "+
                        "locktime is greater than the transaction locktime: "+
                        "%d > %d", lockTime, txLockTime)
                return scriptError(ErrUnsatisfiedLockTime, str)
        }

        return nil
}

5 END

Author: Nisen

Email: imnisen@163.com