From 6ec9b73a53a1f7480a5c251e639b754d95f25c27 Mon Sep 17 00:00:00 2001 From: Conner Fromknecht Date: Fri, 19 Apr 2019 03:05:55 -0700 Subject: [PATCH] txscript/scriptnum: add maxscriptnum and maxcltvlength --- txscript/scriptnum.go | 20 ++++-- txscript/scriptnum_test.go | 126 ++++++++++++++++++------------------- txscript/stack.go | 4 +- 3 files changed, 81 insertions(+), 69 deletions(-) diff --git a/txscript/scriptnum.go b/txscript/scriptnum.go index a89d5f39cc..81f2636121 100644 --- a/txscript/scriptnum.go +++ b/txscript/scriptnum.go @@ -12,9 +12,21 @@ const ( maxInt32 = 1<<31 - 1 minInt32 = -1 << 31 - // defaultScriptNumLen is the default number of bytes - // data being interpreted as an integer may be. - defaultScriptNumLen = 4 + // maxScriptNumLen is the maximum number of bytes data being interpreted + // as an integer may be for the majority of op codes. + maxScriptNumLen = 4 + + // cltvMaxScriptNumLen is the maximum number of bytes data being interpreted + // as an integer may be for by-time and by-height locks as interpreted by + // CHECKLOCKTIMEVERIFY. + // + // The value comes from the fact that 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 needed since it will support + // up to 2^39-1 which allows dates beyond the current locktime limit. + cltvMaxScriptNumLen = 5 ) // scriptNum represents a numeric value used in the scripting engine with @@ -178,7 +190,7 @@ func (n scriptNum) Int32() int32 { // before an ErrStackNumberTooBig is returned. This effectively limits the // range of allowed values. // WARNING: Great care should be taken if passing a value larger than -// defaultScriptNumLen, which could lead to addition and multiplication +// maxScriptNumLen, which could lead to addition and multiplication // overflows. // // See the Bytes function documentation for example encodings. diff --git a/txscript/scriptnum_test.go b/txscript/scriptnum_test.go index e32862b736..668f912f6f 100644 --- a/txscript/scriptnum_test.go +++ b/txscript/scriptnum_test.go @@ -104,35 +104,35 @@ func TestMakeScriptNum(t *testing.T) { err error }{ // Minimal encoding must reject negative 0. - {hexToBytes("80"), 0, defaultScriptNumLen, true, errMinimalData}, + {hexToBytes("80"), 0, maxScriptNumLen, true, errMinimalData}, // Minimally encoded valid values with minimal encoding flag. // Should not error and return expected integral number. - {nil, 0, defaultScriptNumLen, true, nil}, - {hexToBytes("01"), 1, defaultScriptNumLen, true, nil}, - {hexToBytes("81"), -1, defaultScriptNumLen, true, nil}, - {hexToBytes("7f"), 127, defaultScriptNumLen, true, nil}, - {hexToBytes("ff"), -127, defaultScriptNumLen, true, nil}, - {hexToBytes("8000"), 128, defaultScriptNumLen, true, nil}, - {hexToBytes("8080"), -128, defaultScriptNumLen, true, nil}, - {hexToBytes("8100"), 129, defaultScriptNumLen, true, nil}, - {hexToBytes("8180"), -129, defaultScriptNumLen, true, nil}, - {hexToBytes("0001"), 256, defaultScriptNumLen, true, nil}, - {hexToBytes("0081"), -256, defaultScriptNumLen, true, nil}, - {hexToBytes("ff7f"), 32767, defaultScriptNumLen, true, nil}, - {hexToBytes("ffff"), -32767, defaultScriptNumLen, true, nil}, - {hexToBytes("008000"), 32768, defaultScriptNumLen, true, nil}, - {hexToBytes("008080"), -32768, defaultScriptNumLen, true, nil}, - {hexToBytes("ffff00"), 65535, defaultScriptNumLen, true, nil}, - {hexToBytes("ffff80"), -65535, defaultScriptNumLen, true, nil}, - {hexToBytes("000008"), 524288, defaultScriptNumLen, true, nil}, - {hexToBytes("000088"), -524288, defaultScriptNumLen, true, nil}, - {hexToBytes("000070"), 7340032, defaultScriptNumLen, true, nil}, - {hexToBytes("0000f0"), -7340032, defaultScriptNumLen, true, nil}, - {hexToBytes("00008000"), 8388608, defaultScriptNumLen, true, nil}, - {hexToBytes("00008080"), -8388608, defaultScriptNumLen, true, nil}, - {hexToBytes("ffffff7f"), 2147483647, defaultScriptNumLen, true, nil}, - {hexToBytes("ffffffff"), -2147483647, defaultScriptNumLen, true, nil}, + {nil, 0, maxScriptNumLen, true, nil}, + {hexToBytes("01"), 1, maxScriptNumLen, true, nil}, + {hexToBytes("81"), -1, maxScriptNumLen, true, nil}, + {hexToBytes("7f"), 127, maxScriptNumLen, true, nil}, + {hexToBytes("ff"), -127, maxScriptNumLen, true, nil}, + {hexToBytes("8000"), 128, maxScriptNumLen, true, nil}, + {hexToBytes("8080"), -128, maxScriptNumLen, true, nil}, + {hexToBytes("8100"), 129, maxScriptNumLen, true, nil}, + {hexToBytes("8180"), -129, maxScriptNumLen, true, nil}, + {hexToBytes("0001"), 256, maxScriptNumLen, true, nil}, + {hexToBytes("0081"), -256, maxScriptNumLen, true, nil}, + {hexToBytes("ff7f"), 32767, maxScriptNumLen, true, nil}, + {hexToBytes("ffff"), -32767, maxScriptNumLen, true, nil}, + {hexToBytes("008000"), 32768, maxScriptNumLen, true, nil}, + {hexToBytes("008080"), -32768, maxScriptNumLen, true, nil}, + {hexToBytes("ffff00"), 65535, maxScriptNumLen, true, nil}, + {hexToBytes("ffff80"), -65535, maxScriptNumLen, true, nil}, + {hexToBytes("000008"), 524288, maxScriptNumLen, true, nil}, + {hexToBytes("000088"), -524288, maxScriptNumLen, true, nil}, + {hexToBytes("000070"), 7340032, maxScriptNumLen, true, nil}, + {hexToBytes("0000f0"), -7340032, maxScriptNumLen, true, nil}, + {hexToBytes("00008000"), 8388608, maxScriptNumLen, true, nil}, + {hexToBytes("00008080"), -8388608, maxScriptNumLen, true, nil}, + {hexToBytes("ffffff7f"), 2147483647, maxScriptNumLen, true, nil}, + {hexToBytes("ffffffff"), -2147483647, maxScriptNumLen, true, nil}, {hexToBytes("ffffffff7f"), 549755813887, 5, true, nil}, {hexToBytes("ffffffffff"), -549755813887, 5, true, nil}, {hexToBytes("ffffffffffffff7f"), 9223372036854775807, 8, true, nil}, @@ -145,50 +145,50 @@ func TestMakeScriptNum(t *testing.T) { // Minimally encoded values that are out of range for data that // is interpreted as script numbers with the minimal encoding // flag set. Should error and return 0. - {hexToBytes("0000008000"), 0, defaultScriptNumLen, true, errNumTooBig}, - {hexToBytes("0000008080"), 0, defaultScriptNumLen, true, errNumTooBig}, - {hexToBytes("0000009000"), 0, defaultScriptNumLen, true, errNumTooBig}, - {hexToBytes("0000009080"), 0, defaultScriptNumLen, true, errNumTooBig}, - {hexToBytes("ffffffff00"), 0, defaultScriptNumLen, true, errNumTooBig}, - {hexToBytes("ffffffff80"), 0, defaultScriptNumLen, true, errNumTooBig}, - {hexToBytes("0000000001"), 0, defaultScriptNumLen, true, errNumTooBig}, - {hexToBytes("0000000081"), 0, defaultScriptNumLen, true, errNumTooBig}, - {hexToBytes("ffffffffffff00"), 0, defaultScriptNumLen, true, errNumTooBig}, - {hexToBytes("ffffffffffff80"), 0, defaultScriptNumLen, true, errNumTooBig}, - {hexToBytes("ffffffffffffff00"), 0, defaultScriptNumLen, true, errNumTooBig}, - {hexToBytes("ffffffffffffff80"), 0, defaultScriptNumLen, true, errNumTooBig}, - {hexToBytes("ffffffffffffff7f"), 0, defaultScriptNumLen, true, errNumTooBig}, - {hexToBytes("ffffffffffffffff"), 0, defaultScriptNumLen, true, errNumTooBig}, + {hexToBytes("0000008000"), 0, maxScriptNumLen, true, errNumTooBig}, + {hexToBytes("0000008080"), 0, maxScriptNumLen, true, errNumTooBig}, + {hexToBytes("0000009000"), 0, maxScriptNumLen, true, errNumTooBig}, + {hexToBytes("0000009080"), 0, maxScriptNumLen, true, errNumTooBig}, + {hexToBytes("ffffffff00"), 0, maxScriptNumLen, true, errNumTooBig}, + {hexToBytes("ffffffff80"), 0, maxScriptNumLen, true, errNumTooBig}, + {hexToBytes("0000000001"), 0, maxScriptNumLen, true, errNumTooBig}, + {hexToBytes("0000000081"), 0, maxScriptNumLen, true, errNumTooBig}, + {hexToBytes("ffffffffffff00"), 0, maxScriptNumLen, true, errNumTooBig}, + {hexToBytes("ffffffffffff80"), 0, maxScriptNumLen, true, errNumTooBig}, + {hexToBytes("ffffffffffffff00"), 0, maxScriptNumLen, true, errNumTooBig}, + {hexToBytes("ffffffffffffff80"), 0, maxScriptNumLen, true, errNumTooBig}, + {hexToBytes("ffffffffffffff7f"), 0, maxScriptNumLen, true, errNumTooBig}, + {hexToBytes("ffffffffffffffff"), 0, maxScriptNumLen, true, errNumTooBig}, // Non-minimally encoded, but otherwise valid values with // minimal encoding flag. Should error and return 0. - {hexToBytes("00"), 0, defaultScriptNumLen, true, errMinimalData}, // 0 - {hexToBytes("0100"), 0, defaultScriptNumLen, true, errMinimalData}, // 1 - {hexToBytes("7f00"), 0, defaultScriptNumLen, true, errMinimalData}, // 127 - {hexToBytes("800000"), 0, defaultScriptNumLen, true, errMinimalData}, // 128 - {hexToBytes("810000"), 0, defaultScriptNumLen, true, errMinimalData}, // 129 - {hexToBytes("000100"), 0, defaultScriptNumLen, true, errMinimalData}, // 256 - {hexToBytes("ff7f00"), 0, defaultScriptNumLen, true, errMinimalData}, // 32767 - {hexToBytes("00800000"), 0, defaultScriptNumLen, true, errMinimalData}, // 32768 - {hexToBytes("ffff0000"), 0, defaultScriptNumLen, true, errMinimalData}, // 65535 - {hexToBytes("00000800"), 0, defaultScriptNumLen, true, errMinimalData}, // 524288 - {hexToBytes("00007000"), 0, defaultScriptNumLen, true, errMinimalData}, // 7340032 - {hexToBytes("0009000100"), 0, 5, true, errMinimalData}, // 16779520 + {hexToBytes("00"), 0, maxScriptNumLen, true, errMinimalData}, // 0 + {hexToBytes("0100"), 0, maxScriptNumLen, true, errMinimalData}, // 1 + {hexToBytes("7f00"), 0, maxScriptNumLen, true, errMinimalData}, // 127 + {hexToBytes("800000"), 0, maxScriptNumLen, true, errMinimalData}, // 128 + {hexToBytes("810000"), 0, maxScriptNumLen, true, errMinimalData}, // 129 + {hexToBytes("000100"), 0, maxScriptNumLen, true, errMinimalData}, // 256 + {hexToBytes("ff7f00"), 0, maxScriptNumLen, true, errMinimalData}, // 32767 + {hexToBytes("00800000"), 0, maxScriptNumLen, true, errMinimalData}, // 32768 + {hexToBytes("ffff0000"), 0, maxScriptNumLen, true, errMinimalData}, // 65535 + {hexToBytes("00000800"), 0, maxScriptNumLen, true, errMinimalData}, // 524288 + {hexToBytes("00007000"), 0, maxScriptNumLen, true, errMinimalData}, // 7340032 + {hexToBytes("0009000100"), 0, 5, true, errMinimalData}, // 16779520 // Non-minimally encoded, but otherwise valid values without // minimal encoding flag. Should not error and return expected // integral number. - {hexToBytes("00"), 0, defaultScriptNumLen, false, nil}, - {hexToBytes("0100"), 1, defaultScriptNumLen, false, nil}, - {hexToBytes("7f00"), 127, defaultScriptNumLen, false, nil}, - {hexToBytes("800000"), 128, defaultScriptNumLen, false, nil}, - {hexToBytes("810000"), 129, defaultScriptNumLen, false, nil}, - {hexToBytes("000100"), 256, defaultScriptNumLen, false, nil}, - {hexToBytes("ff7f00"), 32767, defaultScriptNumLen, false, nil}, - {hexToBytes("00800000"), 32768, defaultScriptNumLen, false, nil}, - {hexToBytes("ffff0000"), 65535, defaultScriptNumLen, false, nil}, - {hexToBytes("00000800"), 524288, defaultScriptNumLen, false, nil}, - {hexToBytes("00007000"), 7340032, defaultScriptNumLen, false, nil}, + {hexToBytes("00"), 0, maxScriptNumLen, false, nil}, + {hexToBytes("0100"), 1, maxScriptNumLen, false, nil}, + {hexToBytes("7f00"), 127, maxScriptNumLen, false, nil}, + {hexToBytes("800000"), 128, maxScriptNumLen, false, nil}, + {hexToBytes("810000"), 129, maxScriptNumLen, false, nil}, + {hexToBytes("000100"), 256, maxScriptNumLen, false, nil}, + {hexToBytes("ff7f00"), 32767, maxScriptNumLen, false, nil}, + {hexToBytes("00800000"), 32768, maxScriptNumLen, false, nil}, + {hexToBytes("ffff0000"), 65535, maxScriptNumLen, false, nil}, + {hexToBytes("00000800"), 524288, maxScriptNumLen, false, nil}, + {hexToBytes("00007000"), 7340032, maxScriptNumLen, false, nil}, {hexToBytes("0009000100"), 16779520, 5, false, nil}, } diff --git a/txscript/stack.go b/txscript/stack.go index eb1d8cfdfe..923047d93e 100644 --- a/txscript/stack.go +++ b/txscript/stack.go @@ -86,7 +86,7 @@ func (s *stack) PopInt() (scriptNum, error) { return 0, err } - return makeScriptNum(so, s.verifyMinimalData, defaultScriptNumLen) + return makeScriptNum(so, s.verifyMinimalData, maxScriptNumLen) } // PopBool pops the value off the top of the stack, converts it into a bool, and @@ -123,7 +123,7 @@ func (s *stack) PeekInt(idx int32) (scriptNum, error) { return 0, err } - return makeScriptNum(so, s.verifyMinimalData, defaultScriptNumLen) + return makeScriptNum(so, s.verifyMinimalData, maxScriptNumLen) } // PeekBool returns the Nth item on the stack as a bool without removing it.