txscript: Cleanup and add tests for rotl opcode.

This cleans up the code for handling the left rotation opcode to
explicitly call out its semantics which are likely not otherwise obvious
as well as improve its readability.

It also adds several tests to the reference script tests which exercise
the semantics of the left rotation opcode including both positive and
negative tests.
This commit is contained in:
Dave Collins 2018-06-12 17:31:45 -05:00
parent 9644ec433e
commit 71f86bec32
No known key found for this signature in database
GPG Key ID: B8904D9D9C93D1F2
3 changed files with 69 additions and 10 deletions

View File

@ -78,6 +78,16 @@
["2147483648 1", "ROTR 1073741824 EQUAL", "P2SH,STRICTENC", "ROTR must fail with input value >4 bytes"],
["1 2147483648", "ROTR TRUE", "P2SH,STRICTENC", "ROTR must fail with rotation count >4 bytes"],
["Left bit rotation related test coverage"],
["", "ROTL NOT", "P2SH,STRICTENC", "ROTL requires an input value"],
["1", "ROTL TRUE", "P2SH,STRICTENC", "ROTL requires a rotation count"],
["NOP 1", "ROTL NOT", "P2SH,STRICTENC", "ROTL input value must be numeric"],
["1 NOP", "ROTL TRUE", "P2SH,STRICTENC", "ROTL rotation count must be numeric"],
["2 -1", "ROTL 1 EQUAL", "P2SH,STRICTENC", "ROTL must fail with negative rotation count"],
["1 32", "ROTL 1 EQUAL", "P2SH,STRICTENC", "ROTL must fail with rotation count >31"],
["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"],
["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"],

View File

@ -103,6 +103,42 @@
["1431655765 1", "ROTR -1431655766 EQUAL", "P2SH,STRICTENC", "ROTR 0x55555555 >>> 1"],
["1 1", "ROTR -2147483648 EQUAL", "P2SH,STRICTENC", "ROTR must be able to produce a 5-byte result (0x1 >>> 1)"],
["Left bit rotation related test coverage"],
["-2147483647 1", "ROTL 3 EQUAL", "P2SH,STRICTENC", "ROTL 0x80000001 <<< 1"],
["-2147483647 2", "ROTL 6 EQUAL", "P2SH,STRICTENC", "ROTL 0x80000001 <<< 2"],
["-2147483647 3", "ROTL 12 EQUAL", "P2SH,STRICTENC", "ROTL 0x80000001 <<< 3"],
["-2147483647 4", "ROTL 24 EQUAL", "P2SH,STRICTENC", "ROTL 0x80000001 <<< 4"],
["-2147483647 5", "ROTL 48 EQUAL", "P2SH,STRICTENC", "ROTL 0x80000001 <<< 5"],
["-2147483647 6", "ROTL 96 EQUAL", "P2SH,STRICTENC", "ROTL 0x80000001 <<< 6"],
["-2147483647 7", "ROTL 192 EQUAL", "P2SH,STRICTENC", "ROTL 0x80000001 <<< 7"],
["-2147483647 8", "ROTL 384 EQUAL", "P2SH,STRICTENC", "ROTL 0x80000001 <<< 8"],
["-2147483647 9", "ROTL 768 EQUAL", "P2SH,STRICTENC", "ROTL 0x80000001 <<< 9"],
["-2147483647 10", "ROTL 1536 EQUAL", "P2SH,STRICTENC", "ROTL 0x80000001 <<< 10"],
["-2147483647 11", "ROTL 3072 EQUAL", "P2SH,STRICTENC", "ROTL 0x80000001 <<< 11"],
["-2147483647 12", "ROTL 6144 EQUAL", "P2SH,STRICTENC", "ROTL 0x80000001 <<< 12"],
["-2147483647 13", "ROTL 12288 EQUAL", "P2SH,STRICTENC", "ROTL 0x80000001 <<< 13"],
["-2147483647 14", "ROTL 24576 EQUAL", "P2SH,STRICTENC", "ROTL 0x80000001 <<< 14"],
["-2147483647 15", "ROTL 49152 EQUAL", "P2SH,STRICTENC", "ROTL 0x80000001 <<< 15"],
["-2147483647 16", "ROTL 98304 EQUAL", "P2SH,STRICTENC", "ROTL 0x80000001 <<< 16"],
["-2147483647 17", "ROTL 196608 EQUAL", "P2SH,STRICTENC", "ROTL 0x80000001 <<< 17"],
["-2147483647 18", "ROTL 393216 EQUAL", "P2SH,STRICTENC", "ROTL 0x80000001 <<< 18"],
["-2147483647 19", "ROTL 786432 EQUAL", "P2SH,STRICTENC", "ROTL 0x80000001 <<< 19"],
["-2147483647 20", "ROTL 1572864 EQUAL", "P2SH,STRICTENC", "ROTL 0x80000001 <<< 20"],
["-2147483647 21", "ROTL 3145728 EQUAL", "P2SH,STRICTENC", "ROTL 0x80000001 <<< 21"],
["-2147483647 22", "ROTL 6291456 EQUAL", "P2SH,STRICTENC", "ROTL 0x80000001 <<< 22"],
["-2147483647 23", "ROTL 12582912 EQUAL", "P2SH,STRICTENC", "ROTL 0x80000001 <<< 23"],
["-2147483647 24", "ROTL 25165824 EQUAL", "P2SH,STRICTENC", "ROTL 0x80000001 <<< 24"],
["-2147483647 25", "ROTL 50331648 EQUAL", "P2SH,STRICTENC", "ROTL 0x80000001 <<< 25"],
["-2147483647 26", "ROTL 100663296 EQUAL", "P2SH,STRICTENC", "ROTL 0x80000001 <<< 26"],
["-2147483647 27", "ROTL 201326592 EQUAL", "P2SH,STRICTENC", "ROTL 0x80000001 <<< 27"],
["-2147483647 28", "ROTL 402653184 EQUAL", "P2SH,STRICTENC", "ROTL 0x80000001 <<< 28"],
["-2147483647 29", "ROTL 805306368 EQUAL", "P2SH,STRICTENC", "ROTL 0x80000001 <<< 29"],
["-2147483647 30", "ROTL 1610612736 EQUAL", "P2SH,STRICTENC", "ROTL 0x80000001 <<< 30"],
["-2147483647 31", "ROTL -1073741824 EQUAL", "P2SH,STRICTENC", "ROTL 0x80000001 <<< 31"],
["-1431655766 1", "ROTL 1431655765 EQUAL", "P2SH,STRICTENC", "ROTL 0xaaaaaaaa <<< 1"],
["1431655765 1", "ROTL -1431655766 EQUAL", "P2SH,STRICTENC", "ROTL 0x55555555 <<< 1"],
["1 31", "ROTL -2147483648 EQUAL", "P2SH,STRICTENC", "ROTL must be able to produce a 5-byte result (0x1 <<< 31)"],
["1 2 0 IF AND ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC"],
["1 2 0 IF OR ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC"],
["1 2 0 IF XOR ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC"],

View File

@ -1733,34 +1733,47 @@ func rotateLeft(value int32, count int32) int32 {
return int32((v << c) | (v >> (32 - c)))
}
// opcodeRotl pushes the top two items off the stack as integers. Both ints are
// interpreted as int32s. The first item becomes the depth to rotate (up to 31),
// while the second item is rotated to the left after recasting to a uint32. The
// rotated item is pushed back to the stack.
// opcodeRotl treats the top two items of the data stack as 32-bit integers
// where the top item represents the number of bits to rotate to the left (up to
// 31), and the second item represents the value to rotate (after casting to a
// uint32), and replaces them both with the result of the rotation.
//
// Stack transformation: [... x1 x2] -> [... rotl(x1, x2)]
func opcodeRotl(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 rotations such that attempting to
// do rotl(rotl(0x40000000, 1), 1) will fail due to the first rotation
// producing a value greater than the max int32 while rotl(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 rotations.
if v032 < 0 {
if count < 0 {
return ErrNegativeRotation
}
if v032 > 31 {
if count > 31 {
return ErrRotationOverflow
}
vm.dstack.PushInt(scriptNum(rotateLeft(v132, v032)))
vm.dstack.PushInt(scriptNum(rotateLeft(value, count)))
return nil
}