vm: add MODMUL and MODPOW opcodes

Close #2470.
This commit is contained in:
Anna Shaleva 2022-05-11 09:33:19 +03:00
parent 7fd0eb14b5
commit a94ccf9236
6 changed files with 122 additions and 42 deletions

View file

@ -165,6 +165,8 @@ var coefficients = [256]uint16{
opcode.MOD: 1 << 3, opcode.MOD: 1 << 3,
opcode.POW: 1 << 6, opcode.POW: 1 << 6,
opcode.SQRT: 1 << 6, opcode.SQRT: 1 << 6,
opcode.MODMUL: 1 << 5,
opcode.MODPOW: 1 << 11,
opcode.SHL: 1 << 3, opcode.SHL: 1 << 3,
opcode.SHR: 1 << 3, opcode.SHR: 1 << 3,
opcode.NOT: 1 << 2, opcode.NOT: 1 << 2,

View file

@ -179,6 +179,8 @@ const (
MOD Opcode = 0xA2 MOD Opcode = 0xA2
POW Opcode = 0xA3 POW Opcode = 0xA3
SQRT Opcode = 0xA4 SQRT Opcode = 0xA4
MODMUL Opcode = 0xA5
MODPOW Opcode = 0xA6
SHL Opcode = 0xA8 SHL Opcode = 0xA8
SHR Opcode = 0xA9 SHR Opcode = 0xA9
NOT Opcode = 0xAA NOT Opcode = 0xAA

View file

@ -161,6 +161,8 @@ func _() {
_ = x[MOD-162] _ = x[MOD-162]
_ = x[POW-163] _ = x[POW-163]
_ = x[SQRT-164] _ = x[SQRT-164]
_ = x[MODMUL-165]
_ = x[MODPOW-166]
_ = x[SHL-168] _ = x[SHL-168]
_ = x[SHR-169] _ = x[SHR-169]
_ = x[NOT-170] _ = x[NOT-170]
@ -202,7 +204,7 @@ func _() {
_ = x[CONVERT-219] _ = x[CONVERT-219]
} }
const _Opcode_name = "PUSHINT8PUSHINT16PUSHINT32PUSHINT64PUSHINT128PUSHINT256PUSHAPUSHNULLPUSHDATA1PUSHDATA2PUSHDATA4PUSHM1PUSH0PUSH1PUSH2PUSH3PUSH4PUSH5PUSH6PUSH7PUSH8PUSH9PUSH10PUSH11PUSH12PUSH13PUSH14PUSH15PUSH16NOPJMPJMP_LJMPIFJMPIF_LJMPIFNOTJMPIFNOT_LJMPEQJMPEQ_LJMPNEJMPNE_LJMPGTJMPGT_LJMPGEJMPGE_LJMPLTJMPLT_LJMPLEJMPLE_LCALLCALL_LCALLACALLTABORTASSERTTHROWTRYTRY_LENDTRYENDTRY_LENDFINALLYRETSYSCALLDEPTHDROPNIPXDROPCLEARDUPOVERPICKTUCKSWAPROTROLLREVERSE3REVERSE4REVERSENINITSSLOTINITSLOTLDSFLD0LDSFLD1LDSFLD2LDSFLD3LDSFLD4LDSFLD5LDSFLD6LDSFLDSTSFLD0STSFLD1STSFLD2STSFLD3STSFLD4STSFLD5STSFLD6STSFLDLDLOC0LDLOC1LDLOC2LDLOC3LDLOC4LDLOC5LDLOC6LDLOCSTLOC0STLOC1STLOC2STLOC3STLOC4STLOC5STLOC6STLOCLDARG0LDARG1LDARG2LDARG3LDARG4LDARG5LDARG6LDARGSTARG0STARG1STARG2STARG3STARG4STARG5STARG6STARGNEWBUFFERMEMCPYCATSUBSTRLEFTRIGHTINVERTANDORXOREQUALNOTEQUALSIGNABSNEGATEINCDECADDSUBMULDIVMODPOWSQRTSHLSHRNOTBOOLANDBOOLORNZNUMEQUALNUMNOTEQUALLTLEGTGEMINMAXWITHINPACKMAPPACKSTRUCTPACKUNPACKNEWARRAY0NEWARRAYNEWARRAY_TNEWSTRUCT0NEWSTRUCTNEWMAPSIZEHASKEYKEYSVALUESPICKITEMAPPENDSETITEMREVERSEITEMSREMOVECLEARITEMSPOPITEMISNULLISTYPECONVERT" const _Opcode_name = "PUSHINT8PUSHINT16PUSHINT32PUSHINT64PUSHINT128PUSHINT256PUSHAPUSHNULLPUSHDATA1PUSHDATA2PUSHDATA4PUSHM1PUSH0PUSH1PUSH2PUSH3PUSH4PUSH5PUSH6PUSH7PUSH8PUSH9PUSH10PUSH11PUSH12PUSH13PUSH14PUSH15PUSH16NOPJMPJMP_LJMPIFJMPIF_LJMPIFNOTJMPIFNOT_LJMPEQJMPEQ_LJMPNEJMPNE_LJMPGTJMPGT_LJMPGEJMPGE_LJMPLTJMPLT_LJMPLEJMPLE_LCALLCALL_LCALLACALLTABORTASSERTTHROWTRYTRY_LENDTRYENDTRY_LENDFINALLYRETSYSCALLDEPTHDROPNIPXDROPCLEARDUPOVERPICKTUCKSWAPROTROLLREVERSE3REVERSE4REVERSENINITSSLOTINITSLOTLDSFLD0LDSFLD1LDSFLD2LDSFLD3LDSFLD4LDSFLD5LDSFLD6LDSFLDSTSFLD0STSFLD1STSFLD2STSFLD3STSFLD4STSFLD5STSFLD6STSFLDLDLOC0LDLOC1LDLOC2LDLOC3LDLOC4LDLOC5LDLOC6LDLOCSTLOC0STLOC1STLOC2STLOC3STLOC4STLOC5STLOC6STLOCLDARG0LDARG1LDARG2LDARG3LDARG4LDARG5LDARG6LDARGSTARG0STARG1STARG2STARG3STARG4STARG5STARG6STARGNEWBUFFERMEMCPYCATSUBSTRLEFTRIGHTINVERTANDORXOREQUALNOTEQUALSIGNABSNEGATEINCDECADDSUBMULDIVMODPOWSQRTMODMULMODPOWSHLSHRNOTBOOLANDBOOLORNZNUMEQUALNUMNOTEQUALLTLEGTGEMINMAXWITHINPACKMAPPACKSTRUCTPACKUNPACKNEWARRAY0NEWARRAYNEWARRAY_TNEWSTRUCT0NEWSTRUCTNEWMAPSIZEHASKEYKEYSVALUESPICKITEMAPPENDSETITEMREVERSEITEMSREMOVECLEARITEMSPOPITEMISNULLISTYPECONVERT"
var _Opcode_map = map[Opcode]string{ var _Opcode_map = map[Opcode]string{
0: _Opcode_name[0:8], 0: _Opcode_name[0:8],
@ -356,45 +358,47 @@ var _Opcode_map = map[Opcode]string{
162: _Opcode_name[862:865], 162: _Opcode_name[862:865],
163: _Opcode_name[865:868], 163: _Opcode_name[865:868],
164: _Opcode_name[868:872], 164: _Opcode_name[868:872],
168: _Opcode_name[872:875], 165: _Opcode_name[872:878],
169: _Opcode_name[875:878], 166: _Opcode_name[878:884],
170: _Opcode_name[878:881], 168: _Opcode_name[884:887],
171: _Opcode_name[881:888], 169: _Opcode_name[887:890],
172: _Opcode_name[888:894], 170: _Opcode_name[890:893],
177: _Opcode_name[894:896], 171: _Opcode_name[893:900],
179: _Opcode_name[896:904], 172: _Opcode_name[900:906],
180: _Opcode_name[904:915], 177: _Opcode_name[906:908],
181: _Opcode_name[915:917], 179: _Opcode_name[908:916],
182: _Opcode_name[917:919], 180: _Opcode_name[916:927],
183: _Opcode_name[919:921], 181: _Opcode_name[927:929],
184: _Opcode_name[921:923], 182: _Opcode_name[929:931],
185: _Opcode_name[923:926], 183: _Opcode_name[931:933],
186: _Opcode_name[926:929], 184: _Opcode_name[933:935],
187: _Opcode_name[929:935], 185: _Opcode_name[935:938],
190: _Opcode_name[935:942], 186: _Opcode_name[938:941],
191: _Opcode_name[942:952], 187: _Opcode_name[941:947],
192: _Opcode_name[952:956], 190: _Opcode_name[947:954],
193: _Opcode_name[956:962], 191: _Opcode_name[954:964],
194: _Opcode_name[962:971], 192: _Opcode_name[964:968],
195: _Opcode_name[971:979], 193: _Opcode_name[968:974],
196: _Opcode_name[979:989], 194: _Opcode_name[974:983],
197: _Opcode_name[989:999], 195: _Opcode_name[983:991],
198: _Opcode_name[999:1008], 196: _Opcode_name[991:1001],
200: _Opcode_name[1008:1014], 197: _Opcode_name[1001:1011],
202: _Opcode_name[1014:1018], 198: _Opcode_name[1011:1020],
203: _Opcode_name[1018:1024], 200: _Opcode_name[1020:1026],
204: _Opcode_name[1024:1028], 202: _Opcode_name[1026:1030],
205: _Opcode_name[1028:1034], 203: _Opcode_name[1030:1036],
206: _Opcode_name[1034:1042], 204: _Opcode_name[1036:1040],
207: _Opcode_name[1042:1048], 205: _Opcode_name[1040:1046],
208: _Opcode_name[1048:1055], 206: _Opcode_name[1046:1054],
209: _Opcode_name[1055:1067], 207: _Opcode_name[1054:1060],
210: _Opcode_name[1067:1073], 208: _Opcode_name[1060:1067],
211: _Opcode_name[1073:1083], 209: _Opcode_name[1067:1079],
212: _Opcode_name[1083:1090], 210: _Opcode_name[1079:1085],
216: _Opcode_name[1090:1096], 211: _Opcode_name[1085:1095],
217: _Opcode_name[1096:1102], 212: _Opcode_name[1095:1102],
219: _Opcode_name[1102:1109], 216: _Opcode_name[1102:1108],
217: _Opcode_name[1108:1114],
219: _Opcode_name[1114:1121],
} }
func (i Opcode) String() string { func (i Opcode) String() string {

View file

@ -33,5 +33,5 @@ func TestIsValid(t *testing.T) {
require.True(t, IsValid(ADD)) require.True(t, IsValid(ADD))
require.True(t, IsValid(CONVERT)) require.True(t, IsValid(CONVERT))
require.False(t, IsValid(0xff)) require.False(t, IsValid(0xff))
require.False(t, IsValid(0xa5)) require.False(t, IsValid(0xa7))
} }

View file

@ -89,7 +89,12 @@ type VM struct {
invTree *InvocationTree invTree *InvocationTree
} }
var bigOne = big.NewInt(1) var (
bigMinusOne = big.NewInt(-1)
bigZero = big.NewInt(0)
bigOne = big.NewInt(1)
bigTwo = big.NewInt(2)
)
// New returns a new VM object ready to load AVM bytecode scripts. // New returns a new VM object ready to load AVM bytecode scripts.
func New() *VM { func New() *VM {
@ -950,6 +955,44 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
v.estack.PushItem(stackitem.NewBigInteger(new(big.Int).Sqrt(a))) v.estack.PushItem(stackitem.NewBigInteger(new(big.Int).Sqrt(a)))
case opcode.MODMUL:
modulus := v.estack.Pop().BigInt()
if modulus.Sign() == 0 {
panic("zero modulus")
}
x2 := v.estack.Pop().BigInt()
x1 := v.estack.Pop().BigInt()
res := new(big.Int).Mul(x1, x2)
v.estack.PushItem(stackitem.NewBigInteger(res.Mod(res, modulus)))
case opcode.MODPOW:
modulus := v.estack.Pop().BigInt()
exponent := v.estack.Pop().BigInt()
base := v.estack.Pop().BigInt()
res := new(big.Int)
switch exponent.Cmp(bigMinusOne) {
case -1:
panic("exponent should be >= -1")
case 0:
if base.Cmp(bigZero) <= 0 {
panic("invalid base")
}
if modulus.Cmp(bigTwo) < 0 {
panic("invalid modulus")
}
if res.ModInverse(base, modulus) == nil {
panic("base and modulus are not relatively prime")
}
case 1:
if modulus.Sign() == 0 {
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)
}
v.estack.PushItem(stackitem.NewBigInteger(res))
case opcode.SHL, opcode.SHR: case opcode.SHL, opcode.SHR:
b := toInt(v.estack.Pop().BigInt()) b := toInt(v.estack.Pop().BigInt())
if b == 0 { if b == 0 {

View file

@ -732,6 +732,35 @@ func TestSQRT(t *testing.T) {
t.Run("negative value", getTestFuncForVM(prog, nil, -1)) t.Run("negative value", getTestFuncForVM(prog, nil, -1))
} }
func TestMODMUL(t *testing.T) {
prog := makeProgram(opcode.MODMUL)
t.Run("bad, zero mod", getTestFuncForVM(prog, nil, 1, 2, 0))
t.Run("good, positive base", getTestFuncForVM(prog, 2, 3, 4, 5))
t.Run("good, zero base", getTestFuncForVM(prog, 0, 0, 4, 5))
t.Run("good, negative base", getTestFuncForVM(prog, 3, -3, 4, 5))
t.Run("good, positive base, negative mod", getTestFuncForVM(prog, 2, 3, 4, -5))
t.Run("good, negative base, negative mod", getTestFuncForVM(prog, 3, -3, 4, -5))
}
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, 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("bad, big negative exponent", getTestFuncForVM(prog, nil, 3, -2, 5))
t.Run("bad, zero modulus", getTestFuncForVM(prog, nil, 3, 4, 0))
t.Run("inverse compatibility", func(t *testing.T) { // Tests are taken from C# node.
t.Run("bad mod", getTestFuncForVM(prog, nil, 1, -1, 0))
t.Run("bad mod", getTestFuncForVM(prog, nil, 1, -1, 1))
t.Run("bad base", getTestFuncForVM(prog, nil, 0, -1, 0))
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))
})
}
func TestSHR(t *testing.T) { func TestSHR(t *testing.T) {
prog := makeProgram(opcode.SHR) prog := makeProgram(opcode.SHR)
t.Run("Good", getTestFuncForVM(prog, 1, 4, 2)) t.Run("Good", getTestFuncForVM(prog, 1, 4, 2))