From 4c3ad4987b70335ca90e9c50065a69e5674202a4 Mon Sep 17 00:00:00 2001 From: David Hill Date: Mon, 5 Oct 2015 12:11:56 -0400 Subject: [PATCH] txscript: Implement CheckLockTimeVerify (BIP0065) See https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki for more information. This commit mimics Bitcoin Core commit bc60b2b4b401f0adff5b8b9678903ff8feb5867b and includes additional tests from Bitcoin Core commit cb54d17355864fa08826d6511a0d7692b21ef2c9 --- txscript/data/tx_invalid.json | 72 ++++++++++++++++++++++ txscript/data/tx_valid.json | 42 +++++++++++++ txscript/engine.go | 5 ++ txscript/opcode.go | 112 +++++++++++++++++++++++++++++----- txscript/opcode_test.go | 18 ++++-- txscript/reference_test.go | 2 + txscript/standard.go | 1 + 7 files changed, 234 insertions(+), 18 deletions(-) diff --git a/txscript/data/tx_invalid.json b/txscript/data/tx_invalid.json index fa133340..9da7fbaa 100644 --- a/txscript/data/tx_invalid.json +++ b/txscript/data/tx_invalid.json @@ -114,6 +114,78 @@ [[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "IF CODESEPARATOR ENDIF 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 CHECKSIGVERIFY CODESEPARATOR 1"]], "010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a483045022100fa4a74ba9fd59c59f46c3960cf90cbe0d2b743c471d24a3d5d6db6002af5eebb02204d70ec490fd0f7055a7c45f86514336e3a7f03503dacecabb247fc23f15c83510100ffffffff010000000000000000016a00000000", "P2SH"], +["CHECKLOCKTIMEVERIFY tests"], + +["By-height locks, with argument just beyond tx nLockTime"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1 NOP2 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "499999999 NOP2 1"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000fe64cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], + +["By-time locks, with argument just beyond tx nLockTime (but within numerical boundaries)"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000001 NOP2 1"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 NOP2 1"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000feffffff", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Argument missing"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "NOP2 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000001b1010000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Argument negative with by-blockheight nLockTime=0"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "-1 NOP2 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Argument negative with by-blocktime nLockTime=500,000,000"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "-1 NOP2 1"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000004005194b1010000000100000000000000000002000000", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Input locked"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 NOP2 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000251b1ffffffff0100000000000000000002000000", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Another input being unlocked isn't sufficient; the CHECKLOCKTIMEVERIFY-using input must be unlocked"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 NOP2 1"] , + ["0000000000000000000000000000000000000000000000000000000000000200", 1, "1"]], +"010000000200010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00020000000000000000000000000000000000000000000000000000000000000100000000000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Argument/tx height/time mismatch, both versions"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 NOP2 1"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000251b100000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "499999999 NOP2 1"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000000 NOP2 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000000 NOP2 1"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ff64cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Argument 2^32 with nLockTime=2^32-1"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967296 NOP2 1"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ffffffff", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Same, but with nLockTime=2^31-1"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483648 NOP2 1"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ffffff7f", "P2SH,CHECKLOCKTIMEVERIFY"], + +["6 byte non-minimally-encoded arguments are invalid even if their contents are valid"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x06 0x000000000000 NOP2 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Failure due to failing CHECKLOCKTIMEVERIFY in scriptSig"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000251b1000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Failure due to failing CHECKLOCKTIMEVERIFY in redeemScript"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0xc5b93064159b3b2d6ab506a41b1f50463771b988 EQUAL"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000030251b1000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], ["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 182b88ef..9744a3c8 100644 --- a/txscript/data/tx_valid.json +++ b/txscript/data/tx_valid.json @@ -187,5 +187,47 @@ "0100000002dbb33bdf185b17f758af243c5d3c6e164cc873f6bb9f40c0677d6e0f8ee5afce000000006b4830450221009627444320dc5ef8d7f68f35010b4c050a6ed0d96b67a84db99fda9c9de58b1e02203e4b4aaa019e012e65d69b487fdf8719df72f488fa91506a80c49a33929f1fd50121022b78b756e2258af13779c1a1f37ea6800259716ca4b7f0b87610e0bf3ab52a01ffffffffdbb33bdf185b17f758af243c5d3c6e164cc873f6bb9f40c0677d6e0f8ee5afce010000009300483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303ffffffff01a0860100000000001976a9149bc0bbdd3024da4d0c38ed1aecf5c68dd1d3fa1288ac00000000", "P2SH"], +["CHECKLOCKTIMEVERIFY tests"], + +["By-height locks, with argument == 0 and == tx nLockTime"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 NOP2 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "499999999 NOP2 1"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ff64cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 NOP2 1"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ff64cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], + +["By-time locks, with argument just beyond tx nLockTime (but within numerical boundaries)"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000000 NOP2 1"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 NOP2 1"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ffffffff", "P2SH,CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000000 NOP2 1"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ffffffff", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Any non-maxint nSequence is fine"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 NOP2 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000feffffff0100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], + +["The argument can be calculated rather than created directly by a PUSHDATA"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "499999999 1ADD NOP2 1"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Perhaps even by an ADD producing a 5-byte result that is out of bounds for other opcodes"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483647 2147483647 ADD NOP2 1"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000feffffff", "P2SH,CHECKLOCKTIMEVERIFY"], + +["5 byte non-minimally-encoded arguments are valid"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x05 0x0000000000 NOP2 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Valid CHECKLOCKTIMEVERIFY in scriptSig"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000251b1000000000100000000000000000001000000", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Valid CHECKLOCKTIMEVERIFY in redeemScript"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0xc5b93064159b3b2d6ab506a41b1f50463771b988 EQUAL"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000030251b1000000000100000000000000000001000000", "P2SH,CHECKLOCKTIMEVERIFY"], + ["Make diffs cleaner by leaving a comment here without comma at the end"] ] diff --git a/txscript/engine.go b/txscript/engine.go index a2def673..9a39aa8f 100644 --- a/txscript/engine.go +++ b/txscript/engine.go @@ -33,6 +33,11 @@ const ( // executed. ScriptDiscourageUpgradableNops + // ScriptVerifyCheckLockTimeVerify defines whether to verify that + // a transaction output is spendable based on the locktime. + // This is BIP0065. + ScriptVerifyCheckLockTimeVerify + // 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 631fab30..6c42bcd9 100644 --- a/txscript/opcode.go +++ b/txscript/opcode.go @@ -8,6 +8,7 @@ import ( "bytes" "crypto/sha1" "encoding/binary" + "errors" "fmt" "hash" @@ -212,6 +213,7 @@ const ( OP_CHECKMULTISIGVERIFY = 0xaf // 175 OP_NOP1 = 0xb0 // 176 OP_NOP2 = 0xb1 // 177 + OP_CHECKLOCKTIMEVERIFY = 0xb1 // 177 - AKA OP_NOP2 OP_NOP3 = 0xb2 // 178 OP_NOP4 = 0xb3 // 179 OP_NOP5 = 0xb4 // 180 @@ -403,16 +405,17 @@ var opcodeArray = [256]opcode{ OP_16: {OP_16, "OP_16", 1, opcodeN}, // Control opcodes. - OP_NOP: {OP_NOP, "OP_NOP", 1, opcodeNop}, - OP_VER: {OP_VER, "OP_VER", 1, opcodeReserved}, - OP_IF: {OP_IF, "OP_IF", 1, opcodeIf}, - OP_NOTIF: {OP_NOTIF, "OP_NOTIF", 1, opcodeNotIf}, - OP_VERIF: {OP_VERIF, "OP_VERIF", 1, opcodeReserved}, - OP_VERNOTIF: {OP_VERNOTIF, "OP_VERNOTIF", 1, opcodeReserved}, - OP_ELSE: {OP_ELSE, "OP_ELSE", 1, opcodeElse}, - OP_ENDIF: {OP_ENDIF, "OP_ENDIF", 1, opcodeEndif}, - OP_VERIFY: {OP_VERIFY, "OP_VERIFY", 1, opcodeVerify}, - OP_RETURN: {OP_RETURN, "OP_RETURN", 1, opcodeReturn}, + OP_NOP: {OP_NOP, "OP_NOP", 1, opcodeNop}, + OP_VER: {OP_VER, "OP_VER", 1, opcodeReserved}, + OP_IF: {OP_IF, "OP_IF", 1, opcodeIf}, + OP_NOTIF: {OP_NOTIF, "OP_NOTIF", 1, opcodeNotIf}, + OP_VERIF: {OP_VERIF, "OP_VERIF", 1, opcodeReserved}, + OP_VERNOTIF: {OP_VERNOTIF, "OP_VERNOTIF", 1, opcodeReserved}, + OP_ELSE: {OP_ELSE, "OP_ELSE", 1, opcodeElse}, + OP_ENDIF: {OP_ENDIF, "OP_ENDIF", 1, opcodeEndif}, + OP_VERIFY: {OP_VERIFY, "OP_VERIFY", 1, opcodeVerify}, + OP_RETURN: {OP_RETURN, "OP_RETURN", 1, opcodeReturn}, + OP_CHECKLOCKTIMEVERIFY: {OP_CHECKLOCKTIMEVERIFY, "OP_CHECKLOCKTIMEVERIFY", 1, opcodeCheckLockTimeVerify}, // Stack opcodes. OP_TOALTSTACK: {OP_TOALTSTACK, "OP_TOALTSTACK", 1, opcodeToAltStack}, @@ -495,7 +498,6 @@ var opcodeArray = [256]opcode{ // Reserved opcodes. OP_NOP1: {OP_NOP1, "OP_NOP1", 1, opcodeNop}, - OP_NOP2: {OP_NOP2, "OP_NOP2", 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}, @@ -873,7 +875,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_NOP2, OP_NOP3, OP_NOP4, OP_NOP5, + case OP_NOP1, OP_NOP3, 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 "+ @@ -1006,6 +1008,86 @@ func opcodeReturn(op *parsedOpcode, vm *Engine) error { return ErrStackEarlyReturn } +// 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 +// ScriptVerifyCheckLockTimeVerify is not set, the code continues as if OP_NOP2 +// were executed. +func opcodeCheckLockTimeVerify(op *parsedOpcode, vm *Engine) error { + // If the ScriptVerifyCheckLockTimeVerify script flag is not set, treat + // opcode as OP_NOP2 instead. + if !vm.hasFlag(ScriptVerifyCheckLockTimeVerify) { + if vm.hasFlag(ScriptDiscourageUpgradableNops) { + return errors.New("OP_NOP2 reserved for soft-fork " + + "upgrades") + } + return nil + } + + // The current transaction locktime is a uint32 resulting in a maximum + // locktime of 2^32-1 (the year 2106). However, scriptNums are signed + // and therefore a standard 4-byte scriptNum would only support up to a + // maximum of 2^31-1 (the year 2038). Thus, a 5-byte scriptNum is used + // here since it will support up to 2^39-1 which allows dates beyond the + // current locktime 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 + } + lockTime, err := makeScriptNum(so, vm.dstack.verifyMinimalData, 5) + if err != nil { + 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. + if lockTime < 0 { + return fmt.Errorf("negative locktime: %d", lockTime) + } + + // The lock time field of a transaction is either a block height at + // 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) + } + + // The lock time feature can also be disabled, thereby bypassing + // OP_CHECKLOCKTIMEVERIFY, if every transaction input has been finalized by + // setting its sequence to the maximum value (wire.MaxTxInSequenceNum). This + // condition would result in the transaction being allowed into the blockchain + // making the opcode ineffective. + // + // This condition is prevented by enforcing that the input being used by + // the opcode is unlocked (its sequence number is less than the max + // value). This is sufficient to prove correctness without having to + // check every input. + // + // NOTE: This implies that even if the transaction is not finalized due to + // another input being unlocked, the opcode execution will still fail when the + // input being used by the opcode is locked. + if vm.tx.TxIn[vm.txIdx].Sequence == wire.MaxTxInSequenceNum { + return errors.New("transaction input is finalized") + } + + return nil +} + // opcodeToAltStack removes the top item from the main data stack and pushes it // onto the alternate data stack. // @@ -2109,11 +2191,13 @@ var OpcodeByName = make(map[string]byte) func init() { // Initialize the opcode name to value map using the contents of the - // opcode array. Also add entries for "OP_FALSE" and "OP_TRUE" since - // they are aliases for "OP_0" and "OP_1", respectively. + // opcode array. Also add entries for "OP_FALSE", "OP_TRUE", and + // "OP_NOP2" since they are aliases for "OP_0", "OP_1", + // and "OP_CHECKLOCKTIMEVERIFY" respectively. for _, op := range opcodeArray { OpcodeByName[op.name] = op.value } OpcodeByName["OP_FALSE"] = OP_FALSE OpcodeByName["OP_TRUE"] = OP_TRUE + OpcodeByName["OP_NOP2"] = OP_CHECKLOCKTIMEVERIFY } diff --git a/txscript/opcode_test.go b/txscript/opcode_test.go index 0c31fc0b..bdd19e2f 100644 --- a/txscript/opcode_test.go +++ b/txscript/opcode_test.go @@ -109,8 +109,13 @@ func TestOpcodeDisasm(t *testing.T) { // OP_NOP1 through OP_NOP10. case opcodeVal >= 0xb0 && opcodeVal <= 0xb9: - val := byte(opcodeVal - (0xb0 - 1)) - expectedStr = "OP_NOP" + strconv.Itoa(int(val)) + // OP_NOP2 is an alias of OP_CHECKLOCKTIMEVERIFY + if opcodeVal == 0xb1 { + expectedStr = "OP_CHECKLOCKTIMEVERIFY" + } else { + val := byte(opcodeVal - (0xb0 - 1)) + expectedStr = "OP_NOP" + strconv.Itoa(int(val)) + } // OP_UNKNOWN#. case opcodeVal >= 0xba && opcodeVal <= 0xf8 || opcodeVal == 0xfc: @@ -166,8 +171,13 @@ func TestOpcodeDisasm(t *testing.T) { // OP_NOP1 through OP_NOP10. case opcodeVal >= 0xb0 && opcodeVal <= 0xb9: - val := byte(opcodeVal - (0xb0 - 1)) - expectedStr = "OP_NOP" + strconv.Itoa(int(val)) + // OP_NOP2 is an alias of OP_CHECKLOCKTIMEVERIFY + if opcodeVal == 0xb1 { + expectedStr = "OP_CHECKLOCKTIMEVERIFY" + } else { + val := byte(opcodeVal - (0xb0 - 1)) + expectedStr = "OP_NOP" + strconv.Itoa(int(val)) + } // OP_UNKNOWN#. case opcodeVal >= 0xba && opcodeVal <= 0xf8 || opcodeVal == 0xfc: diff --git a/txscript/reference_test.go b/txscript/reference_test.go index 4ef94e83..1e5c58a9 100644 --- a/txscript/reference_test.go +++ b/txscript/reference_test.go @@ -124,6 +124,8 @@ func parseScriptFlags(flagStr string) (ScriptFlags, error) { switch flag { case "": // Nothing. + case "CHECKLOCKTIMEVERIFY": + flags |= ScriptVerifyCheckLockTimeVerify case "CLEANSTACK": flags |= ScriptVerifyCleanStack case "DERSIG": diff --git a/txscript/standard.go b/txscript/standard.go index c869c8db..a032d211 100644 --- a/txscript/standard.go +++ b/txscript/standard.go @@ -31,6 +31,7 @@ const ( ScriptStrictMultiSig | ScriptDiscourageUpgradableNops | ScriptVerifyCleanStack | + ScriptVerifyCheckLockTimeVerify | ScriptVerifyLowS )