Skip to content

Commit

Permalink
txscript: Cleanup and add tests for lshift opcode.
Browse files Browse the repository at this point in the history
This cleans up the code for handling the left shift 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 shift opcode including both positive and
negative tests.
  • Loading branch information
davecgh committed Jun 13, 2018
1 parent 37fcf95 commit f8d8dbc
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 11 deletions.
10 changes: 10 additions & 0 deletions txscript/data/script_invalid.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"],
Expand Down
37 changes: 36 additions & 1 deletion txscript/data/script_valid.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"],
Expand Down
33 changes: 23 additions & 10 deletions txscript/opcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down

0 comments on commit f8d8dbc

Please sign in to comment.