From a6bf1d9850f857f8971204b62c466c4b0f7be2ac Mon Sep 17 00:00:00 2001 From: David Hill Date: Fri, 23 Oct 2015 17:05:24 -0400 Subject: [PATCH] txscript: Implement CheckSequenceVerify (BIP0112) --- txscript/consensus.go | 7 +- txscript/data/tx_invalid.json | 54 +++++++++++++++ txscript/data/tx_valid.json | 84 +++++++++++++++++++++++ txscript/engine.go | 7 +- txscript/opcode.go | 125 +++++++++++++++++++++++++++++----- txscript/opcode_test.go | 22 ++++-- txscript/reference_test.go | 2 + txscript/standard.go | 3 +- wire/msgtx.go | 26 +++++-- 9 files changed, 293 insertions(+), 37 deletions(-) diff --git a/txscript/consensus.go b/txscript/consensus.go index 1aec5028..865b7277 100644 --- a/txscript/consensus.go +++ b/txscript/consensus.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015 The btcsuite developers +// Copyright (c) 2015-2016 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -8,7 +8,6 @@ const ( // LockTimeThreshold is the number below which a lock time is // interpreted to be a block number. Since an average of one block // is generated per 10 minutes, this allows blocks for about 9,512 - // years. However, if the field is interpreted as a timestamp, given - // the lock time is a uint32, the max is sometime around 2106. - LockTimeThreshold uint32 = 5e8 // Tue Nov 5 00:53:20 1985 UTC + // years. + LockTimeThreshold = 5e8 // Tue Nov 5 00:53:20 1985 UTC ) diff --git a/txscript/data/tx_invalid.json b/txscript/data/tx_invalid.json index 983c873a..3d0f18bd 100644 --- a/txscript/data/tx_invalid.json +++ b/txscript/data/tx_invalid.json @@ -191,5 +191,59 @@ [[["b1dbc81696c8a9c0fccd0693ab66d7c368dbc38c0def4e800685560ddd1b2132", 0, "DUP HASH160 0x14 0x4b3bd7eba3bc0284fd3007be7f3be275e94f5826 EQUALVERIFY CHECKSIG"]], "010000000132211bdd0d568506804eef0d8cc3db68c3d766ab9306cdfcc0a9c89616c8dbb1000000006c493045022100c7bb0faea0522e74ff220c20c022d2cb6033f8d167fb89e75a50e237a35fd6d202203064713491b1f8ad5f79e623d0219ad32510bfaa1009ab30cbee77b59317d6e30001210237af13eb2d84e4545af287b919c2282019c9691cc509e78e196a9d8274ed1be0ffffffff0100000000000000001976a914f1b3ed2eda9a2ebe5a9374f692877cdf87c0f95b88ac00000000", "P2SH,DERSIG"], +["CHECKSEQUENCEVERIFY tests"], + +["By-height locks, with argument just beyond txin.nSequence"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1 NOP3 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4259839 NOP3 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000feff40000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + +["By-time locks, with argument just beyond txin.nSequence (but within numerical boundries)"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4194305 NOP3 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4259839 NOP3 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000feff40000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + +["Argument missing"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "NOP3 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + +["Argument negative with by-blockheight txin.nSequence=0"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "-1 NOP3 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + +["Argument negative with by-blocktime txin.nSequence=CTxIn::SEQUENCE_UNITS_THRESHOD"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "-1 NOP3 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + +["Argument/tx height/time mismatch, both versions"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 NOP3 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "65535 NOP3 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4194304 NOP3 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4259839 NOP3 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + +["6 byte non-minimally-encoded arguments are invalid even if their contents are valid"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x06 0x000000000000 NOP3 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffff00000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + +["Failure due to failing CHECKSEQUENCEVERIFY in scriptSig"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"]], +"02000000010001000000000000000000000000000000000000000000000000000000000000000000000251b2000000000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + +["Failure due to failing CHECKSEQUENCEVERIFY in redeemScript"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x7c17aff532f22beb54069942f9bf567a66133eaf EQUAL"]], +"0200000001000100000000000000000000000000000000000000000000000000000000000000000000030251b2000000000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + +["Failure due to insufficient tx.nVersion (<2)"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 NOP3 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4194304 NOP3 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + ["Make diffs cleaner by leaving a comment here without comma at the end"] ] diff --git a/txscript/data/tx_valid.json b/txscript/data/tx_valid.json index 0dfef73a..dd4343a8 100644 --- a/txscript/data/tx_valid.json +++ b/txscript/data/tx_valid.json @@ -233,5 +233,89 @@ [[["b1dbc81696c8a9c0fccd0693ab66d7c368dbc38c0def4e800685560ddd1b2132", 0, "DUP HASH160 0x14 0x4b3bd7eba3bc0284fd3007be7f3be275e94f5826 EQUALVERIFY CHECKSIG"]], "010000000132211bdd0d568506804eef0d8cc3db68c3d766ab9306cdfcc0a9c89616c8dbb1000000006c493045022100c7bb0faea0522e74ff220c20c022d2cb6033f8d167fb89e75a50e237a35fd6d202203064713491b1f8ad5f79e623d0219ad32510bfaa1009ab30cbee77b59317d6e30001210237af13eb2d84e4545af287b919c2282019c9691cc509e78e196a9d8274ed1be0ffffffff0100000000000000001976a914f1b3ed2eda9a2ebe5a9374f692877cdf87c0f95b88ac00000000", "P2SH"], +["CHECKSEQUENCEVERIFY tests"], + +["By-height locks, with argument == 0 and == txin.nSequence"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 NOP3 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "65535 NOP3 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffff00000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "65535 NOP3 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffbf7f0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 NOP3 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffbf7f0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + +["By-time locks, with argument == 0 and == txin.nSequence"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4194304 NOP3 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4259839 NOP3 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffff40000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4259839 NOP3 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffff7f0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4194304 NOP3 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffff7f0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + +["Upper sequence with upper sequence is fine"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483648 NOP3 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000800100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 NOP3 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000800100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483648 NOP3 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000feffffff0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 NOP3 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000feffffff0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483648 NOP3 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 NOP3 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + +["Argument 2^31 with various nSequence"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483648 NOP3 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffbf7f0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483648 NOP3 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffff7f0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483648 NOP3 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + +["Argument 2^32-1 with various nSequence"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 NOP3 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffbf7f0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 NOP3 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffff7f0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 NOP3 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + +["Argument 3<<31 with various nSequence"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "6442450944 NOP3 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffbf7f0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "6442450944 NOP3 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffff7f0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "6442450944 NOP3 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + +["5 byte non-minimally-encoded operandss are valid"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x05 0x0000000000 NOP3 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + +["The argument can be calculated rather than created directly by a PUSHDATA"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4194303 1ADD NOP3 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4194304 1SUB NOP3 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffff00000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + +["An ADD producing a 5-byte result that sets CTxIn::SEQUENCE_LOCKTIME_DISABLED_FLAG"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483647 65536 NOP3 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483647 4259840 ADD NOP3 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + +["Valid CHECKSEQUENCEVERIFY in scriptSig"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"]], +"02000000010001000000000000000000000000000000000000000000000000000000000000000000000251b2010000000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + +["Valid CHECKSEQUENCEVERIFY in redeemScript"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x7c17aff532f22beb54069942f9bf567a66133eaf EQUAL"]], +"0200000001000100000000000000000000000000000000000000000000000000000000000000000000030251b2010000000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + ["Make diffs cleaner by leaving a comment here without comma at the end"] ] diff --git a/txscript/engine.go b/txscript/engine.go index 97111eb1..fc9a8269 100644 --- a/txscript/engine.go +++ b/txscript/engine.go @@ -1,4 +1,4 @@ -// Copyright (c) 2013-2015 The btcsuite developers +// Copyright (c) 2013-2016 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -38,6 +38,11 @@ const ( // This is BIP0065. ScriptVerifyCheckLockTimeVerify + // ScriptVerifyCheckSequenceVerify defines whether to allow execution + // pathways of a script to be restricted based on the age of the output + // being spent. This is BIP0112. + ScriptVerifyCheckSequenceVerify + // ScriptVerifyCleanStack defines that the stack must contain only // one stack element after evaluation and that the element must be // true if interpreted as a boolean. This is rule 6 of BIP0062. diff --git a/txscript/opcode.go b/txscript/opcode.go index f0da2cf1..3c147ae3 100644 --- a/txscript/opcode.go +++ b/txscript/opcode.go @@ -216,6 +216,7 @@ const ( OP_NOP2 = 0xb1 // 177 OP_CHECKLOCKTIMEVERIFY = 0xb1 // 177 - AKA OP_NOP2 OP_NOP3 = 0xb2 // 178 + OP_CHECKSEQUENCEVERIFY = 0xb2 // 178 - AKA OP_NOP3 OP_NOP4 = 0xb3 // 179 OP_NOP5 = 0xb4 // 180 OP_NOP6 = 0xb5 // 181 @@ -417,6 +418,7 @@ var opcodeArray = [256]opcode{ OP_VERIFY: {OP_VERIFY, "OP_VERIFY", 1, opcodeVerify}, OP_RETURN: {OP_RETURN, "OP_RETURN", 1, opcodeReturn}, OP_CHECKLOCKTIMEVERIFY: {OP_CHECKLOCKTIMEVERIFY, "OP_CHECKLOCKTIMEVERIFY", 1, opcodeCheckLockTimeVerify}, + OP_CHECKSEQUENCEVERIFY: {OP_CHECKSEQUENCEVERIFY, "OP_CHECKSEQUENCEVERIFY", 1, opcodeCheckSequenceVerify}, // Stack opcodes. OP_TOALTSTACK: {OP_TOALTSTACK, "OP_TOALTSTACK", 1, opcodeToAltStack}, @@ -499,7 +501,6 @@ var opcodeArray = [256]opcode{ // Reserved opcodes. OP_NOP1: {OP_NOP1, "OP_NOP1", 1, opcodeNop}, - OP_NOP3: {OP_NOP3, "OP_NOP3", 1, opcodeNop}, OP_NOP4: {OP_NOP4, "OP_NOP4", 1, opcodeNop}, OP_NOP5: {OP_NOP5, "OP_NOP5", 1, opcodeNop}, OP_NOP6: {OP_NOP6, "OP_NOP6", 1, opcodeNop}, @@ -876,7 +877,7 @@ func opcodeN(op *parsedOpcode, vm *Engine) error { // the flag to discourage use of NOPs is set for select opcodes. func opcodeNop(op *parsedOpcode, vm *Engine) error { switch op.opcode.value { - case OP_NOP1, OP_NOP3, OP_NOP4, OP_NOP5, + case OP_NOP1, OP_NOP4, OP_NOP5, OP_NOP6, OP_NOP7, OP_NOP8, OP_NOP9, OP_NOP10: if vm.hasFlag(ScriptDiscourageUpgradableNops) { return fmt.Errorf("OP_NOP%d reserved for soft-fork "+ @@ -1009,6 +1010,25 @@ func opcodeReturn(op *parsedOpcode, vm *Engine) error { return ErrStackEarlyReturn } +// 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)) { + return fmt.Errorf("mismatched locktime types -- tx locktime %d, stack "+ + "locktime %d", txLockTime, lockTime) + } + + if lockTime > txLockTime { + str := "locktime requirement not satisfied -- locktime is " + + "greater than the transaction locktime: %d > %d" + return fmt.Errorf(str, lockTime, txLockTime) + } + + return nil +} + // opcodeCheckLockTimeVerify 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 @@ -1043,8 +1063,9 @@ func opcodeCheckLockTimeVerify(op *parsedOpcode, vm *Engine) error { return err } - // In the rare event that the argument may be < 0 due to some arithmetic - // being done first, you can always use 0 OP_MAX OP_CHECKLOCKTIMEVERIFY. + // 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_CHECKLOCKTIMEVERIFY. if lockTime < 0 { return fmt.Errorf("negative locktime: %d", lockTime) } @@ -1053,19 +1074,10 @@ func opcodeCheckLockTimeVerify(op *parsedOpcode, vm *Engine) error { // which the transaction is finalized or a timestamp depending on if the // value is before the txscript.LockTimeThreshold. When it is under the // threshold it is a block height. - // - // The lockTimes in both the script and transaction must be of the same - // type. - if !((vm.tx.LockTime < LockTimeThreshold && int64(lockTime) < int64(LockTimeThreshold)) || - (vm.tx.LockTime >= LockTimeThreshold && int64(lockTime) >= int64(LockTimeThreshold))) { - return fmt.Errorf("mismatched locktime types -- tx locktime %d, stack "+ - "locktime %d", vm.tx.LockTime, lockTime) - } - - if int64(lockTime) > int64(vm.tx.LockTime) { - str := "locktime requirement not satisfied -- locktime is " + - "greater than the transaction locktime: %d > %d" - return fmt.Errorf(str, lockTime, vm.tx.LockTime) + err = verifyLockTime(int64(vm.tx.LockTime), LockTimeThreshold, + int64(lockTime)) + if err != nil { + return err } // The lock time feature can also be disabled, thereby bypassing @@ -1089,6 +1101,84 @@ func opcodeCheckLockTimeVerify(op *parsedOpcode, vm *Engine) error { return nil } +// 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 errors.New("OP_NOP3 reserved for soft-fork " + + "upgrades") + } + return nil + } + + if vm.tx.Version < 2 { + return fmt.Errorf("invalid transaction version: %d", + vm.tx.Version) + } + + // 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 { + return fmt.Errorf("negative sequence: %d", stackSequence) + } + + 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 + } + + // 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 { + return fmt.Errorf("transaction sequence has sequence "+ + "locktime disabled bit set: 0x%x", txSequence) + } + + // Mask off non-consensus bits before doing comparisons. + lockTimeMask := int64(wire.SequenceLockTimeIsSeconds | + wire.SequenceLockTimeMask) + err = verifyLockTime(txSequence&lockTimeMask, + wire.SequenceLockTimeIsSeconds, + sequence&lockTimeMask) + if err != nil { + return err + } + + return nil +} + // opcodeToAltStack removes the top item from the main data stack and pushes it // onto the alternate data stack. // @@ -2201,4 +2291,5 @@ func init() { OpcodeByName["OP_FALSE"] = OP_FALSE OpcodeByName["OP_TRUE"] = OP_TRUE OpcodeByName["OP_NOP2"] = OP_CHECKLOCKTIMEVERIFY + OpcodeByName["OP_NOP3"] = OP_CHECKSEQUENCEVERIFY } diff --git a/txscript/opcode_test.go b/txscript/opcode_test.go index bdd19e2f..c9bdfa5d 100644 --- a/txscript/opcode_test.go +++ b/txscript/opcode_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2013-2015 The btcsuite developers +// Copyright (c) 2013-2016 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -109,10 +109,14 @@ func TestOpcodeDisasm(t *testing.T) { // OP_NOP1 through OP_NOP10. case opcodeVal >= 0xb0 && opcodeVal <= 0xb9: - // OP_NOP2 is an alias of OP_CHECKLOCKTIMEVERIFY - if opcodeVal == 0xb1 { + switch opcodeVal { + case 0xb1: + // OP_NOP2 is an alias of OP_CHECKLOCKTIMEVERIFY expectedStr = "OP_CHECKLOCKTIMEVERIFY" - } else { + case 0xb2: + // OP_NOP3 is an alias of OP_CHECKSEQUENCEVERIFY + expectedStr = "OP_CHECKSEQUENCEVERIFY" + default: val := byte(opcodeVal - (0xb0 - 1)) expectedStr = "OP_NOP" + strconv.Itoa(int(val)) } @@ -171,10 +175,14 @@ func TestOpcodeDisasm(t *testing.T) { // OP_NOP1 through OP_NOP10. case opcodeVal >= 0xb0 && opcodeVal <= 0xb9: - // OP_NOP2 is an alias of OP_CHECKLOCKTIMEVERIFY - if opcodeVal == 0xb1 { + switch opcodeVal { + case 0xb1: + // OP_NOP2 is an alias of OP_CHECKLOCKTIMEVERIFY expectedStr = "OP_CHECKLOCKTIMEVERIFY" - } else { + case 0xb2: + // OP_NOP3 is an alias of OP_CHECKSEQUENCEVERIFY + expectedStr = "OP_CHECKSEQUENCEVERIFY" + default: val := byte(opcodeVal - (0xb0 - 1)) expectedStr = "OP_NOP" + strconv.Itoa(int(val)) } diff --git a/txscript/reference_test.go b/txscript/reference_test.go index aba9314e..5d546e85 100644 --- a/txscript/reference_test.go +++ b/txscript/reference_test.go @@ -128,6 +128,8 @@ func parseScriptFlags(flagStr string) (ScriptFlags, error) { // Nothing. case "CHECKLOCKTIMEVERIFY": flags |= ScriptVerifyCheckLockTimeVerify + case "CHECKSEQUENCEVERIFY": + flags |= ScriptVerifyCheckSequenceVerify case "CLEANSTACK": flags |= ScriptVerifyCleanStack case "DERSIG": diff --git a/txscript/standard.go b/txscript/standard.go index 66079461..921208f1 100644 --- a/txscript/standard.go +++ b/txscript/standard.go @@ -1,4 +1,4 @@ -// Copyright (c) 2013-2015 The btcsuite developers +// Copyright (c) 2013-2016 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -32,6 +32,7 @@ const ( ScriptDiscourageUpgradableNops | ScriptVerifyCleanStack | ScriptVerifyCheckLockTimeVerify | + ScriptVerifyCheckSequenceVerify | ScriptVerifyLowS ) diff --git a/wire/msgtx.go b/wire/msgtx.go index 85d234dd..3987f893 100644 --- a/wire/msgtx.go +++ b/wire/msgtx.go @@ -24,14 +24,26 @@ const ( // MaxPrevOutIndex is the maximum index the index field of a previous // outpoint can be. MaxPrevOutIndex uint32 = 0xffffffff -) -const ( - // defaultTxInOutAlloc is the default size used for the backing array - // for transaction inputs and outputs. The array will dynamically grow - // as needed, but this figure is intended to provide enough space for - // the number of inputs and outputs in a typical transaction without - // needing to grow the backing array multiple times. + // SequenceLockTimeDisabled is a flag that if set on a transaction + // input's sequence number, the sequence number will not be interpreted + // as a relative locktime. + SequenceLockTimeDisabled = 1 << 31 + + // SequenceLockTimeIsSeconds is a flag that if set on a transaction + // input's sequence number, the relative locktime has units of 512 + // seconds. + SequenceLockTimeIsSeconds = 1 << 22 + + // SequenceLockTimeMask is a mask that extracts the relative locktime + // when masked against the transaction input sequence number. + SequenceLockTimeMask = 0x0000ffff + + // defaultTxInOutAlloc is the default size used for the backing array for + // transaction inputs and outputs. The array will dynamically grow as needed, + // but this figure is intended to provide enough space for the number of + // inputs and outputs in a typical transaction without needing to grow the + // backing array multiple times. defaultTxInOutAlloc = 15 // minTxInPayload is the minimum payload size for a transaction input.