Skip to content

Commit

Permalink
txscript: Cleanup and add tests for mod opcode.
Browse files Browse the repository at this point in the history
This cleans up the code for handling the mod 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 div opcode including both positive and negative
tests.
  • Loading branch information
davecgh committed Jun 13, 2018
1 parent 1080a97 commit ca7eeee
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 8 deletions.
9 changes: 9 additions & 0 deletions txscript/data/script_invalid.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,15 @@
["2147483648 1073741824", "DIV TRUE", "P2SH,STRICTENC", "DIV must fail with dividend >4 bytes"],
["1 2147483648", "DIV TRUE", "P2SH,STRICTENC", "DIV must fail with divisor >4 bytes"],

["Modular division related test coverage"],
["", "MOD TRUE", "P2SH,STRICTENC", "MOD requires a divisor"],
["3", "MOD TRUE", "P2SH,STRICTENC", "MOD requires a dividend"],
["NOP 3", "MOD TRUE", "P2SH,STRICTENC", "MOD dividend must be numeric"],
["6 NOP", "MOD TRUE", "P2SH,STRICTENC", "MOD divisor must be numeric"],
["6 0", "MOD TRUE", "P2SH,STRICTENC", "MOD must fail with zero divisor"],
["2147483648 1073741824", "MOD TRUE", "P2SH,STRICTENC", "MOD must fail with dividend >4 bytes"],
["1 2147483648", "MOD TRUE", "P2SH,STRICTENC", "MOD must fail with divisor >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"],
Expand Down
9 changes: 8 additions & 1 deletion txscript/data/script_valid.json
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,14 @@
["-2147483647 DUP", "DIV 1 EQUAL", "STRICTENC", "DIV must support 4-byte negative int32"],
["2 DUP DIV", "1 EQUAL", "P2SH,STRICTENC"],

["7 3 MOD", "1 EQUAL", "P2SH,STRICTENC"],
["Modular division related test coverage"],
["7 3", "MOD 1 EQUAL", "STRICTENC", "MOD must produce expected result (7%3)"],
["8 3", "MOD 2 EQUAL", "STRICTENC", "MOD must produce expected result (8%3)"],
["6 3", "MOD 0 EQUAL", "STRICTENC", "MOD must support a positive dividend and divisor"],
["-7 3", "MOD -1 EQUAL", "STRICTENC", "MOD must support a negative dividend with positive divisor with negative result"],
["7 -3", "MOD 1 EQUAL", "STRICTENC", "MOD must support a positive dividend with negative divisor with positve result"],
["-7 -3", "MOD -1 EQUAL", "STRICTENC", "MOD must support a negative dividend and divisor with negative result"],
["-2147483647 1073741823", "MOD -1 EQUAL", "STRICTENC", "MOD must support 4-byte negative int32"],

["Left bit shift related test coverage"],
["1 1", "LSHIFT 2 EQUAL", "P2SH,STRICTENC", "LSHIFT 0x1 << 1"],
Expand Down
19 changes: 12 additions & 7 deletions txscript/opcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -1971,28 +1971,33 @@ func opcodeDiv(op *parsedOpcode, vm *Engine) error {
}

// opcodeMod treats the top two items on the data stack as integers and replaces
// them with the result of the modulus the top entry by the second-to-top entry as
// 4-byte integers.
// them with the result of the modulus of the top entry by the second-to-top
// entry as 4-byte integers. Since the value to shift is treated as a
// signed 32-bit integer, truncated division semantics are used when negative
// values are used. In other words, the result will have the same sign as the
// dividend.
//
// Stack transformation: [... x1 x2] -> [... x1/x2]
func opcodeMod(op *parsedOpcode, vm *Engine) error {
v0, err := vm.dstack.PopInt(mathOpCodeMaxScriptNumLen)
if err != nil {
return err
}

v1, err := vm.dstack.PopInt(mathOpCodeMaxScriptNumLen)
if err != nil {
return err
}

if v0.Int32() == 0 {
// The dividend and divisor are limited to int32 via the above, so it is
// safe to cast them.
divisor := v0.Int32()
dividend := v1.Int32()

if divisor == 0 {
return ErrDivideByZero
}

v2 := v1.Int32() % v0.Int32()

vm.dstack.PushInt(scriptNum(v2))
vm.dstack.PushInt(scriptNum(dividend % divisor))
return nil
}

Expand Down

0 comments on commit ca7eeee

Please sign in to comment.