diff --git a/txscript/data/script_invalid.json b/txscript/data/script_invalid.json index 600e3fb895..382e7d94b0 100644 --- a/txscript/data/script_invalid.json +++ b/txscript/data/script_invalid.json @@ -88,6 +88,16 @@ ["2147483648 1", "ROTL 1 EQUAL", "P2SH,STRICTENC", "ROTL must fail with input value >4 bytes"], ["1 2147483648", "ROTL TRUE", "P2SH,STRICTENC", "ROTL must fail with rotation count >4 bytes"], +["Left bit shift related test coverage"], +["", "LSHIFT NOT", "P2SH,STRICTENC", "LSHIFT requires an input value"], +["1", "LSHIFT TRUE", "P2SH,STRICTENC", "LSHIFT requires a shift count"], +["NOP 1", "LSHIFT NOT", "P2SH,STRICTENC", "LSHIFT input value must be numeric"], +["1 NOP", "LSHIFT TRUE", "P2SH,STRICTENC", "LSHIFT shift count must be numeric"], +["2 -1", "LSHIFT 1 EQUAL", "P2SH,STRICTENC", "LSHIFT must fail with negative shift count"], +["1 33", "LSHIFT 0 EQUAL", "P2SH,STRICTENC", "LSHIFT must fail with shift count >32"], +["2147483648 1", "LSHIFT TRUE", "P2SH,STRICTENC", "LSHIFT must fail with input value >4 bytes"], +["1 2147483648", "LSHIFT TRUE", "P2SH,STRICTENC", "LSHIFT must fail with shift count >4 bytes"], + ["1", "IF 0x50 ENDIF 1", "P2SH,STRICTENC", "0x50 is reserved"], ["0x52", "0x5f ADD 0x60 EQUAL", "P2SH,STRICTENC", "0x51 through 0x60 push 1 through 16 onto stack"], ["0","NOP", "P2SH,STRICTENC"], diff --git a/txscript/data/script_valid.json b/txscript/data/script_valid.json index 67b64ecb8e..ba057149f0 100644 --- a/txscript/data/script_valid.json +++ b/txscript/data/script_valid.json @@ -153,9 +153,44 @@ ["2 DUP MUL", "4 EQUAL", "P2SH,STRICTENC"], ["2 DUP DIV", "1 EQUAL", "P2SH,STRICTENC"], ["7 3 MOD", "1 EQUAL", "P2SH,STRICTENC"], -["2 2 LSHIFT", "8 EQUAL", "P2SH,STRICTENC"], ["2 1 RSHIFT", "1 EQUAL", "P2SH,STRICTENC"], +["Left bit shift related test coverage"], +["1 1", "LSHIFT 2 EQUAL", "P2SH,STRICTENC", "LSHIFT 0x1 << 1"], +["1 2", "LSHIFT 4 EQUAL", "P2SH,STRICTENC", "LSHIFT 0x1 << 2"], +["1 3", "LSHIFT 8 EQUAL", "P2SH,STRICTENC", "LSHIFT 0x1 << 3"], +["1 4", "LSHIFT 16 EQUAL", "P2SH,STRICTENC", "LSHIFT 0x1 << 4"], +["1 5", "LSHIFT 32 EQUAL", "P2SH,STRICTENC", "LSHIFT 0x1 << 5"], +["1 6", "LSHIFT 64 EQUAL", "P2SH,STRICTENC", "LSHIFT 0x1 << 6"], +["1 7", "LSHIFT 128 EQUAL", "P2SH,STRICTENC", "LSHIFT 0x1 << 7"], +["1 8", "LSHIFT 256 EQUAL", "P2SH,STRICTENC", "LSHIFT 0x1 << 8"], +["1 9", "LSHIFT 512 EQUAL", "P2SH,STRICTENC", "LSHIFT 0x1 << 9"], +["1 10", "LSHIFT 1024 EQUAL", "P2SH,STRICTENC", "LSHIFT 0x1 << 10"], +["1 11", "LSHIFT 2048 EQUAL", "P2SH,STRICTENC", "LSHIFT 0x1 << 11"], +["1 12", "LSHIFT 4096 EQUAL", "P2SH,STRICTENC", "LSHIFT 0x1 << 12"], +["1 13", "LSHIFT 8192 EQUAL", "P2SH,STRICTENC", "LSHIFT 0x1 << 13"], +["1 14", "LSHIFT 16384 EQUAL", "P2SH,STRICTENC", "LSHIFT 0x1 << 14"], +["1 15", "LSHIFT 32768 EQUAL", "P2SH,STRICTENC", "LSHIFT 0x1 << 15"], +["1 16", "LSHIFT 65536 EQUAL", "P2SH,STRICTENC", "LSHIFT 0x1 << 16"], +["1 17", "LSHIFT 131072 EQUAL", "P2SH,STRICTENC", "LSHIFT 0x1 << 17"], +["1 18", "LSHIFT 262144 EQUAL", "P2SH,STRICTENC", "LSHIFT 0x1 << 18"], +["1 19", "LSHIFT 524288 EQUAL", "P2SH,STRICTENC", "LSHIFT 0x1 << 19"], +["1 20", "LSHIFT 1048576 EQUAL", "P2SH,STRICTENC", "LSHIFT 0x1 << 20"], +["1 21", "LSHIFT 2097152 EQUAL", "P2SH,STRICTENC", "LSHIFT 0x1 << 21"], +["1 22", "LSHIFT 4194304 EQUAL", "P2SH,STRICTENC", "LSHIFT 0x1 << 22"], +["1 23", "LSHIFT 8388608 EQUAL", "P2SH,STRICTENC", "LSHIFT 0x1 << 23"], +["1 24", "LSHIFT 16777216 EQUAL", "P2SH,STRICTENC", "LSHIFT 0x1 << 24"], +["1 25", "LSHIFT 33554432 EQUAL", "P2SH,STRICTENC", "LSHIFT 0x1 << 25"], +["1 26", "LSHIFT 67108864 EQUAL", "P2SH,STRICTENC", "LSHIFT 0x1 << 26"], +["1 27", "LSHIFT 134217728 EQUAL", "P2SH,STRICTENC", "LSHIFT 0x1 << 27"], +["1 28", "LSHIFT 268435456 EQUAL", "P2SH,STRICTENC", "LSHIFT 0x1 << 28"], +["1 29", "LSHIFT 536870912 EQUAL", "P2SH,STRICTENC", "LSHIFT 0x1 << 29"], +["1 30", "LSHIFT 1073741824 EQUAL", "P2SH,STRICTENC", "LSHIFT 0x1 << 30"], +["1 31", "LSHIFT -2147483648 EQUAL", "P2SH,STRICTENC", "LSHIFT must be able to produce a 5-byte result (0x1 << 31)"], +["1 32", "LSHIFT 0 EQUAL", "P2SH,STRICTENC", "LSHIFT 0x1 << 32"], +["-1431655766 1", "LSHIFT 1431655764 EQUAL", "P2SH,STRICTENC", "LSHIFT 0xaaaaaaaa << 1"], +["1431655765 1", "LSHIFT -1431655766 EQUAL", "P2SH,STRICTENC", "LSHIFT 0x55555555 << 1"], + ["0x4c 0x00","0 EQUAL", "P2SH,STRICTENC"], ["0x4d 0x0000","0 EQUAL", "P2SH,STRICTENC"], ["0x4e 0x00000000","0 EQUAL", "P2SH,STRICTENC"], diff --git a/txscript/opcode.go b/txscript/opcode.go index 8054553459..e120f88d7f 100644 --- a/txscript/opcode.go +++ b/txscript/opcode.go @@ -1994,34 +1994,47 @@ func opcodeMod(op *parsedOpcode, vm *Engine) error { return nil } -// opcodeLShift pushes the top two items off the stack as integers. Both ints are -// interpreted as int32s. The first item becomes the depth to shift left, while -// the second item is shifted that depth to the left. The shifted item is pushed -// back to the stack as an integer. +// opcodeLShift treats the top two items of the data stack as 32-bit integers +// where the top item represents the number of bits to left shift (up to 32), +// and the second item represents the value to shift, and replaces them both +// with the result of the shift. +// // Stack transformation: [... x1 x2] -> [... x1 << x2] func opcodeLShift(op *parsedOpcode, vm *Engine) error { + // WARNING: Since scriptNums are signed, a standard 4-byte scriptNum only + // supports up to a maximum of 2^31-1. The value (v1) really should allow + // 5-byte scriptNums and have an overflow check later to clamp it to uint32, + // so the full range of uint32 could be covered. This has undesirable + // consequences on the semantics of left shift such that attempting to + // do ((0x40000000 << 1) << 1) will fail due to the first shift producing + // a value greater than the max int32 while (0x40000000 << 2) will work as + // expected. + // + // Unfortunately, a 4-byte scriptNum is now part of consensus, so changing + // it requires a consensus vote. v0, err := vm.dstack.PopInt(mathOpCodeMaxScriptNumLen) // x2 if err != nil { return err } - v1, err := vm.dstack.PopInt(mathOpCodeMaxScriptNumLen) // x1 if err != nil { return err } - v032 := v0.Int32() - v132 := v1.Int32() + // The count and value are limited to int32 via the above, so it is safe to + // cast them. + count := v0.Int32() + value := v1.Int32() // Don't allow invalid or pointless shifts. - if v032 < 0 { + if count < 0 { return ErrNegativeShift } - if v032 > 32 { + if count > 32 { return ErrShiftOverflow } - vm.dstack.PushInt(scriptNum(v132 << uint(v032))) + vm.dstack.PushInt(scriptNum(value << uint(count))) return nil }