diff --git a/pkg/compiler/syscall_test.go b/pkg/compiler/syscall_test.go index 0995eb8bd..4bc87feef 100644 --- a/pkg/compiler/syscall_test.go +++ b/pkg/compiler/syscall_test.go @@ -375,9 +375,9 @@ func TestOpcode(t *testing.T) { }` eval(t, src, []stackitem.Item{ stackitem.Make(1), - stackitem.Make(2), + stackitem.Make(-3), stackitem.Make(1), - stackitem.Make(2), + stackitem.Make(-3), stackitem.Make(52), }) }) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 4149f9ac6..1608ebb36 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -1055,6 +1055,12 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro panic("zero modulus") // https://docs.microsoft.com/en-us/dotnet/api/system.numerics.biginteger.modpow?view=net-6.0#exceptions } res.Exp(base, exponent, modulus) + + // https://github.com/nspcc-dev/neo-go/issues/3612 + if base.Sign() < 0 && exponent.Bit(0) == 1 && res.Sign() != 0 { + absModulus := new(big.Int).Abs(modulus) + res.Sub(res, absModulus) + } } v.estack.PushItem(stackitem.NewBigInteger(res)) diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index 38e29f1ff..c4dfb8ba7 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -747,11 +747,12 @@ func TestMODMUL(t *testing.T) { func TestMODPOW(t *testing.T) { prog := makeProgram(opcode.MODPOW) t.Run("good, positive base", getTestFuncForVM(prog, 1, 3, 4, 5)) - t.Run("good, negative base", getTestFuncForVM(prog, 2, -3, 5, 5)) + t.Run("good, negative base", getTestFuncForVM(prog, -3, -3, 5, 5)) t.Run("good, positive base, negative mod", getTestFuncForVM(prog, 1, 3, 4, -5)) - t.Run("good, negative base, negative mod", getTestFuncForVM(prog, 2, -3, 5, -5)) + t.Run("good, negative base, negative mod", getTestFuncForVM(prog, -3, -3, 5, -5)) t.Run("bad, big negative exponent", getTestFuncForVM(prog, nil, 3, -2, 5)) t.Run("bad, zero modulus", getTestFuncForVM(prog, nil, 3, 4, 0)) + t.Run("zero result, negative base, even exponent", getTestFuncForVM(prog, 0, -2, 3, 8)) t.Run("inverse compatibility", func(t *testing.T) { // Tests are taken from C# node. t.Run("bad mod", getTestFuncForVM(prog, nil, 1, -1, 0)) @@ -760,6 +761,7 @@ func TestMODPOW(t *testing.T) { t.Run("bad base", getTestFuncForVM(prog, nil, 0, -1, 1)) t.Run("no inverse exists", getTestFuncForVM(prog, nil, math.MaxUint16, -1, math.MaxUint8)) t.Run("good", getTestFuncForVM(prog, 52, 19, -1, 141)) + t.Run("good", getTestFuncForVM(prog, 1, 5, -1, 4)) }) }