BIP112(CHECKSEQUENCEVERIFY)
1 BIP地址
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 }