txscript: Change makeScriptNum to take a length argument
While current existing numeric opcodes are limited to 4 bytes, new opcodes may need different limits. This mimics Bitcoin Core commit 99088d60d8a7747c6d1a7fd5d8cd388be1b3e138
This commit is contained in:
parent
e4c053e504
commit
ce22159fb2
3 changed files with 107 additions and 84 deletions
|
@ -8,9 +8,9 @@ const (
|
||||||
maxInt32 = 1<<31 - 1
|
maxInt32 = 1<<31 - 1
|
||||||
minInt32 = -1 << 31
|
minInt32 = -1 << 31
|
||||||
|
|
||||||
// maxScriptNumLen is the maximum number of bytes data being interpreted
|
// defaultScriptNumLen is the default number of bytes
|
||||||
// as an integer may be.
|
// data being interpreted as an integer may be.
|
||||||
maxScriptNumLen = 4
|
defaultScriptNumLen = 4
|
||||||
)
|
)
|
||||||
|
|
||||||
// scriptNum represents a numeric value used in the scripting engine with
|
// scriptNum represents a numeric value used in the scripting engine with
|
||||||
|
@ -36,9 +36,9 @@ const (
|
||||||
//
|
//
|
||||||
// Then, whenever data is interpreted as an integer, it is converted to this
|
// Then, whenever data is interpreted as an integer, it is converted to this
|
||||||
// type by using the makeScriptNum function which will return an error if the
|
// type by using the makeScriptNum function which will return an error if the
|
||||||
// number is out of range (or not minimally encoded depending on a flag). Since
|
// number is out of range or not minimally encoded depending on parameters.
|
||||||
// all numeric opcodes involve pulling data from the stack and interpreting it
|
// Since all numeric opcodes involve pulling data from the stack and
|
||||||
// as an integer, it provides the required behavior.
|
// interpreting it as an integer, it provides the required behavior.
|
||||||
type scriptNum int64
|
type scriptNum int64
|
||||||
|
|
||||||
// checkMinimalDataEncoding returns whether or not the passed byte array adheres
|
// checkMinimalDataEncoding returns whether or not the passed byte array adheres
|
||||||
|
@ -132,11 +132,12 @@ func (n scriptNum) Bytes() []byte {
|
||||||
// and the consensus rules dictate numbers which are directly cast to ints
|
// and the consensus rules dictate numbers which are directly cast to ints
|
||||||
// provide this behavior.
|
// provide this behavior.
|
||||||
//
|
//
|
||||||
// In practice, the number should never really be out of range since it will
|
// In practice, for most opcodes, the number should never be out of range since
|
||||||
// have been created with makeScriptNum which rejects them, but in case
|
// it will have been created with makeScriptNum using the defaultScriptLen
|
||||||
// something in the future ends up calling this function against the result
|
// value, which rejects them. In case something in the future ends up calling
|
||||||
// of some arithmetic, which IS allowed to be out of range before being
|
// this function against the result of some arithmetic, which IS allowed to be
|
||||||
// reinterpreted as an integer, this will provide the correct behavior.
|
// out of range before being reinterpreted as an integer, this will provide the
|
||||||
|
// correct behavior.
|
||||||
func (n scriptNum) Int32() int32 {
|
func (n scriptNum) Int32() int32 {
|
||||||
if n > maxInt32 {
|
if n > maxInt32 {
|
||||||
return maxInt32
|
return maxInt32
|
||||||
|
@ -152,10 +153,13 @@ func (n scriptNum) Int32() int32 {
|
||||||
// makeScriptNum interprets the passed serialized bytes as an encoded integer
|
// makeScriptNum interprets the passed serialized bytes as an encoded integer
|
||||||
// and returns the result as a script number.
|
// and returns the result as a script number.
|
||||||
//
|
//
|
||||||
// Since the consensus rules dictate the serialized bytes interpreted as ints
|
// Since the consensus rules dictate that serialized bytes interpreted as ints
|
||||||
// are only allowed to be in the range [-2^31 + 1, 2^31 - 1], an error will be
|
// are only allowed to be in the range determined by a maximum number of bytes,
|
||||||
// returned when the provided bytes would result in a number outside of that
|
// on a per opcode basis, an error will be returned when the provided bytes
|
||||||
// range.
|
// would result in a number outside of that range. In particular, the range for
|
||||||
|
// the vast majority of opcodes dealing with numeric values are limited to 4
|
||||||
|
// bytes and therefore will pass that value to this function resulting in an
|
||||||
|
// allowed range of [-2^31 + 1, 2^31 - 1].
|
||||||
//
|
//
|
||||||
// The requireMinimal flag causes an error to be returned if additional checks
|
// The requireMinimal flag causes an error to be returned if additional checks
|
||||||
// on the encoding determine it is not represented with the smallest possible
|
// on the encoding determine it is not represented with the smallest possible
|
||||||
|
@ -164,11 +168,18 @@ func (n scriptNum) Int32() int32 {
|
||||||
// [0x7f 0x00 0x00 ...], etc. All forms except [0x7f] will return an error with
|
// [0x7f 0x00 0x00 ...], etc. All forms except [0x7f] will return an error with
|
||||||
// requireMinimal enabled.
|
// requireMinimal enabled.
|
||||||
//
|
//
|
||||||
|
// The scriptNumLen is the maximum number of bytes the encoded value can be
|
||||||
|
// 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
|
||||||
|
// overflows.
|
||||||
|
//
|
||||||
// See the Bytes function documentation for example encodings.
|
// See the Bytes function documentation for example encodings.
|
||||||
func makeScriptNum(v []byte, requireMinimal bool) (scriptNum, error) {
|
func makeScriptNum(v []byte, requireMinimal bool, scriptNumLen int) (scriptNum, error) {
|
||||||
// Interpreting data as an integer requires that it is not larger than
|
// Interpreting data requires that it is not larger than
|
||||||
// a 32-bit integer.
|
// the the passed scriptNumLen value.
|
||||||
if len(v) > maxScriptNumLen {
|
if len(v) > scriptNumLen {
|
||||||
return 0, ErrStackNumberTooBig
|
return 0, ErrStackNumberTooBig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -94,90 +94,102 @@ func TestMakeScriptNum(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
serialized []byte
|
serialized []byte
|
||||||
num scriptNum
|
num scriptNum
|
||||||
|
numLen int
|
||||||
minimalEncoding bool
|
minimalEncoding bool
|
||||||
err error
|
err error
|
||||||
}{
|
}{
|
||||||
// Minimal encoding must reject negative 0.
|
// Minimal encoding must reject negative 0.
|
||||||
{hexToBytes("80"), 0, true, ErrStackMinimalData},
|
{hexToBytes("80"), 0, defaultScriptNumLen, true, ErrStackMinimalData},
|
||||||
|
|
||||||
// Minimally encoded valid values with minimal encoding flag.
|
// Minimally encoded valid values with minimal encoding flag.
|
||||||
// Should not error and return expected integral number.
|
// Should not error and return expected integral number.
|
||||||
{nil, 0, true, nil},
|
{nil, 0, defaultScriptNumLen, true, nil},
|
||||||
{hexToBytes("01"), 1, true, nil},
|
{hexToBytes("01"), 1, defaultScriptNumLen, true, nil},
|
||||||
{hexToBytes("81"), -1, true, nil},
|
{hexToBytes("81"), -1, defaultScriptNumLen, true, nil},
|
||||||
{hexToBytes("7f"), 127, true, nil},
|
{hexToBytes("7f"), 127, defaultScriptNumLen, true, nil},
|
||||||
{hexToBytes("ff"), -127, true, nil},
|
{hexToBytes("ff"), -127, defaultScriptNumLen, true, nil},
|
||||||
{hexToBytes("8000"), 128, true, nil},
|
{hexToBytes("8000"), 128, defaultScriptNumLen, true, nil},
|
||||||
{hexToBytes("8080"), -128, true, nil},
|
{hexToBytes("8080"), -128, defaultScriptNumLen, true, nil},
|
||||||
{hexToBytes("8100"), 129, true, nil},
|
{hexToBytes("8100"), 129, defaultScriptNumLen, true, nil},
|
||||||
{hexToBytes("8180"), -129, true, nil},
|
{hexToBytes("8180"), -129, defaultScriptNumLen, true, nil},
|
||||||
{hexToBytes("0001"), 256, true, nil},
|
{hexToBytes("0001"), 256, defaultScriptNumLen, true, nil},
|
||||||
{hexToBytes("0081"), -256, true, nil},
|
{hexToBytes("0081"), -256, defaultScriptNumLen, true, nil},
|
||||||
{hexToBytes("ff7f"), 32767, true, nil},
|
{hexToBytes("ff7f"), 32767, defaultScriptNumLen, true, nil},
|
||||||
{hexToBytes("ffff"), -32767, true, nil},
|
{hexToBytes("ffff"), -32767, defaultScriptNumLen, true, nil},
|
||||||
{hexToBytes("008000"), 32768, true, nil},
|
{hexToBytes("008000"), 32768, defaultScriptNumLen, true, nil},
|
||||||
{hexToBytes("008080"), -32768, true, nil},
|
{hexToBytes("008080"), -32768, defaultScriptNumLen, true, nil},
|
||||||
{hexToBytes("ffff00"), 65535, true, nil},
|
{hexToBytes("ffff00"), 65535, defaultScriptNumLen, true, nil},
|
||||||
{hexToBytes("ffff80"), -65535, true, nil},
|
{hexToBytes("ffff80"), -65535, defaultScriptNumLen, true, nil},
|
||||||
{hexToBytes("000008"), 524288, true, nil},
|
{hexToBytes("000008"), 524288, defaultScriptNumLen, true, nil},
|
||||||
{hexToBytes("000088"), -524288, true, nil},
|
{hexToBytes("000088"), -524288, defaultScriptNumLen, true, nil},
|
||||||
{hexToBytes("000070"), 7340032, true, nil},
|
{hexToBytes("000070"), 7340032, defaultScriptNumLen, true, nil},
|
||||||
{hexToBytes("0000f0"), -7340032, true, nil},
|
{hexToBytes("0000f0"), -7340032, defaultScriptNumLen, true, nil},
|
||||||
{hexToBytes("00008000"), 8388608, true, nil},
|
{hexToBytes("00008000"), 8388608, defaultScriptNumLen, true, nil},
|
||||||
{hexToBytes("00008080"), -8388608, true, nil},
|
{hexToBytes("00008080"), -8388608, defaultScriptNumLen, true, nil},
|
||||||
{hexToBytes("ffffff7f"), 2147483647, true, nil},
|
{hexToBytes("ffffff7f"), 2147483647, defaultScriptNumLen, true, nil},
|
||||||
{hexToBytes("ffffffff"), -2147483647, true, nil},
|
{hexToBytes("ffffffff"), -2147483647, defaultScriptNumLen, true, nil},
|
||||||
|
{hexToBytes("ffffffff7f"), 549755813887, 5, true, nil},
|
||||||
|
{hexToBytes("ffffffffff"), -549755813887, 5, true, nil},
|
||||||
|
{hexToBytes("ffffffffffffff7f"), 9223372036854775807, 8, true, nil},
|
||||||
|
{hexToBytes("ffffffffffffffff"), -9223372036854775807, 8, true, nil},
|
||||||
|
{hexToBytes("ffffffffffffffff7f"), -1, 9, true, nil},
|
||||||
|
{hexToBytes("ffffffffffffffffff"), 1, 9, true, nil},
|
||||||
|
{hexToBytes("ffffffffffffffffff7f"), -1, 10, true, nil},
|
||||||
|
{hexToBytes("ffffffffffffffffffff"), 1, 10, true, nil},
|
||||||
|
|
||||||
// Minimally encoded values that are out of range for data that
|
// Minimally encoded values that are out of range for data that
|
||||||
// is interpreted as script numbers with the minimal encoding
|
// is interpreted as script numbers with the minimal encoding
|
||||||
// flag set. Should error and return 0.
|
// flag set. Should error and return 0.
|
||||||
{hexToBytes("0000008000"), 0, true, ErrStackNumberTooBig},
|
{hexToBytes("0000008000"), 0, defaultScriptNumLen, true, ErrStackNumberTooBig},
|
||||||
{hexToBytes("0000008080"), 0, true, ErrStackNumberTooBig},
|
{hexToBytes("0000008080"), 0, defaultScriptNumLen, true, ErrStackNumberTooBig},
|
||||||
{hexToBytes("0000009000"), 0, true, ErrStackNumberTooBig},
|
{hexToBytes("0000009000"), 0, defaultScriptNumLen, true, ErrStackNumberTooBig},
|
||||||
{hexToBytes("0000009080"), 0, true, ErrStackNumberTooBig},
|
{hexToBytes("0000009080"), 0, defaultScriptNumLen, true, ErrStackNumberTooBig},
|
||||||
{hexToBytes("ffffffff00"), 0, true, ErrStackNumberTooBig},
|
{hexToBytes("ffffffff00"), 0, defaultScriptNumLen, true, ErrStackNumberTooBig},
|
||||||
{hexToBytes("ffffffff80"), 0, true, ErrStackNumberTooBig},
|
{hexToBytes("ffffffff80"), 0, defaultScriptNumLen, true, ErrStackNumberTooBig},
|
||||||
{hexToBytes("0000000001"), 0, true, ErrStackNumberTooBig},
|
{hexToBytes("0000000001"), 0, defaultScriptNumLen, true, ErrStackNumberTooBig},
|
||||||
{hexToBytes("0000000081"), 0, true, ErrStackNumberTooBig},
|
{hexToBytes("0000000081"), 0, defaultScriptNumLen, true, ErrStackNumberTooBig},
|
||||||
{hexToBytes("ffffffffffff00"), 0, true, ErrStackNumberTooBig},
|
{hexToBytes("ffffffffffff00"), 0, defaultScriptNumLen, true, ErrStackNumberTooBig},
|
||||||
{hexToBytes("ffffffffffff80"), 0, true, ErrStackNumberTooBig},
|
{hexToBytes("ffffffffffff80"), 0, defaultScriptNumLen, true, ErrStackNumberTooBig},
|
||||||
{hexToBytes("ffffffffffffff00"), 0, true, ErrStackNumberTooBig},
|
{hexToBytes("ffffffffffffff00"), 0, defaultScriptNumLen, true, ErrStackNumberTooBig},
|
||||||
{hexToBytes("ffffffffffffff80"), 0, true, ErrStackNumberTooBig},
|
{hexToBytes("ffffffffffffff80"), 0, defaultScriptNumLen, true, ErrStackNumberTooBig},
|
||||||
{hexToBytes("ffffffffffffff7f"), 0, true, ErrStackNumberTooBig},
|
{hexToBytes("ffffffffffffff7f"), 0, defaultScriptNumLen, true, ErrStackNumberTooBig},
|
||||||
{hexToBytes("ffffffffffffffff"), 0, true, ErrStackNumberTooBig},
|
{hexToBytes("ffffffffffffffff"), 0, defaultScriptNumLen, true, ErrStackNumberTooBig},
|
||||||
|
|
||||||
// Non-minimally encoded, but otherwise valid values with
|
// Non-minimally encoded, but otherwise valid values with
|
||||||
// minimal encoding flag. Should error and return 0.
|
// minimal encoding flag. Should error and return 0.
|
||||||
{hexToBytes("00"), 0, true, ErrStackMinimalData}, // 0
|
{hexToBytes("00"), 0, defaultScriptNumLen, true, ErrStackMinimalData}, // 0
|
||||||
{hexToBytes("0100"), 0, true, ErrStackMinimalData}, // 1
|
{hexToBytes("0100"), 0, defaultScriptNumLen, true, ErrStackMinimalData}, // 1
|
||||||
{hexToBytes("7f00"), 0, true, ErrStackMinimalData}, // 127
|
{hexToBytes("7f00"), 0, defaultScriptNumLen, true, ErrStackMinimalData}, // 127
|
||||||
{hexToBytes("800000"), 0, true, ErrStackMinimalData}, // 128
|
{hexToBytes("800000"), 0, defaultScriptNumLen, true, ErrStackMinimalData}, // 128
|
||||||
{hexToBytes("810000"), 0, true, ErrStackMinimalData}, // 129
|
{hexToBytes("810000"), 0, defaultScriptNumLen, true, ErrStackMinimalData}, // 129
|
||||||
{hexToBytes("000100"), 0, true, ErrStackMinimalData}, // 256
|
{hexToBytes("000100"), 0, defaultScriptNumLen, true, ErrStackMinimalData}, // 256
|
||||||
{hexToBytes("ff7f00"), 0, true, ErrStackMinimalData}, // 32767
|
{hexToBytes("ff7f00"), 0, defaultScriptNumLen, true, ErrStackMinimalData}, // 32767
|
||||||
{hexToBytes("00800000"), 0, true, ErrStackMinimalData}, // 32768
|
{hexToBytes("00800000"), 0, defaultScriptNumLen, true, ErrStackMinimalData}, // 32768
|
||||||
{hexToBytes("ffff0000"), 0, true, ErrStackMinimalData}, // 65535
|
{hexToBytes("ffff0000"), 0, defaultScriptNumLen, true, ErrStackMinimalData}, // 65535
|
||||||
{hexToBytes("00000800"), 0, true, ErrStackMinimalData}, // 524288
|
{hexToBytes("00000800"), 0, defaultScriptNumLen, true, ErrStackMinimalData}, // 524288
|
||||||
{hexToBytes("00007000"), 0, true, ErrStackMinimalData}, // 7340032
|
{hexToBytes("00007000"), 0, defaultScriptNumLen, true, ErrStackMinimalData}, // 7340032
|
||||||
|
{hexToBytes("0009000100"), 0, 5, true, ErrStackMinimalData}, // 16779520
|
||||||
|
|
||||||
// Non-minimally encoded, but otherwise valid values without
|
// Non-minimally encoded, but otherwise valid values without
|
||||||
// minimal encoding flag. Should not error and return expected
|
// minimal encoding flag. Should not error and return expected
|
||||||
// integral number.
|
// integral number.
|
||||||
{hexToBytes("00"), 0, false, nil},
|
{hexToBytes("00"), 0, defaultScriptNumLen, false, nil},
|
||||||
{hexToBytes("0100"), 1, false, nil},
|
{hexToBytes("0100"), 1, defaultScriptNumLen, false, nil},
|
||||||
{hexToBytes("7f00"), 127, false, nil},
|
{hexToBytes("7f00"), 127, defaultScriptNumLen, false, nil},
|
||||||
{hexToBytes("800000"), 128, false, nil},
|
{hexToBytes("800000"), 128, defaultScriptNumLen, false, nil},
|
||||||
{hexToBytes("810000"), 129, false, nil},
|
{hexToBytes("810000"), 129, defaultScriptNumLen, false, nil},
|
||||||
{hexToBytes("000100"), 256, false, nil},
|
{hexToBytes("000100"), 256, defaultScriptNumLen, false, nil},
|
||||||
{hexToBytes("ff7f00"), 32767, false, nil},
|
{hexToBytes("ff7f00"), 32767, defaultScriptNumLen, false, nil},
|
||||||
{hexToBytes("00800000"), 32768, false, nil},
|
{hexToBytes("00800000"), 32768, defaultScriptNumLen, false, nil},
|
||||||
{hexToBytes("ffff0000"), 65535, false, nil},
|
{hexToBytes("ffff0000"), 65535, defaultScriptNumLen, false, nil},
|
||||||
{hexToBytes("00000800"), 524288, false, nil},
|
{hexToBytes("00000800"), 524288, defaultScriptNumLen, false, nil},
|
||||||
{hexToBytes("00007000"), 7340032, false, nil},
|
{hexToBytes("00007000"), 7340032, defaultScriptNumLen, false, nil},
|
||||||
|
{hexToBytes("0009000100"), 16779520, 5, false, nil},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
gotNum, err := makeScriptNum(test.serialized, test.minimalEncoding)
|
gotNum, err := makeScriptNum(test.serialized, test.minimalEncoding,
|
||||||
|
test.numLen)
|
||||||
if err != test.err {
|
if err != test.err {
|
||||||
t.Errorf("makeScriptNum: did not received expected "+
|
t.Errorf("makeScriptNum: did not received expected "+
|
||||||
"error for %x - got %v, want %v",
|
"error for %x - got %v, want %v",
|
||||||
|
|
|
@ -83,7 +83,7 @@ func (s *stack) PopInt() (scriptNum, error) {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return makeScriptNum(so, s.verifyMinimalData)
|
return makeScriptNum(so, s.verifyMinimalData, defaultScriptNumLen)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PopBool pops the value off the top of the stack, converts it into a bool, and
|
// PopBool pops the value off the top of the stack, converts it into a bool, and
|
||||||
|
@ -118,7 +118,7 @@ func (s *stack) PeekInt(idx int32) (scriptNum, error) {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return makeScriptNum(so, s.verifyMinimalData)
|
return makeScriptNum(so, s.verifyMinimalData, defaultScriptNumLen)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PeekBool returns the Nth item on the stack as a bool without removing it.
|
// PeekBool returns the Nth item on the stack as a bool without removing it.
|
||||||
|
|
Loading…
Reference in a new issue