lschoe / mpyc

MPyC: Multiparty Computation in Python
MIT License
378 stars 77 forks source link

Some runtime functions errors #11

Closed thinh11 closed 4 years ago

thinh11 commented 4 years ago

mpc.mul gets stuck on SecFxp inputs for more than 2 parties. mpc.div only works for SecFxp inputs with 2 parties. SecInt inputs will give wrong answers. SecFxp inputs with more than 2 parties will result in errors. mpc.reciprocal seems to only work correctly for SecFld

Example code:

from mpyc.runtime import mpc

mpc.run(mpc.start())

secint = mpc.SecInt()
secfxp = mpc.SecFxp(32, 16)
secfld = mpc.SecFld(2**5)

a = mpc.input(secint(3), senders=0) if mpc.pid==0 else mpc.input(secint(None), senders=0)
b = mpc.input(secint(5), senders=1) if mpc.pid==1 else mpc.input(secint(None), senders=1)
c = mpc.input(secfxp(10), senders=0) if mpc.pid==0 else mpc.input(secfxp(None), senders=0)
d = mpc.input(secfxp(200), senders=1) if mpc.pid==1 else mpc.input(secfxp(None), senders=1)
e = mpc.input(secfld(9), senders=0) if mpc.pid==0 else mpc.input(secfld(None), senders=0)

u1 = mpc.mul(a, b) #mpc.mul(c, d) will get stuck if run with command-line argument -M3
u2 = mpc.div(c, d) #will fail if run with command-line argument -M3
u3 = mpc.reciprocal(e) #mpc.reciprocal(c) will give negative number
u4 = mpc.schur_prod([c], [d])[0] #work-around for mpc.mul(c,d)

o1 = mpc.run(mpc.output(u1))
o2 = mpc.run(mpc.output(u2))
o3 = mpc.run(mpc.output(u3))
o4 = mpc.run(mpc.output(u4))

print('output:', o1, o2, o3, o4)

mpc.run(mpc.shutdown())
lschoe commented 4 years ago

Indeed, well spotted! The problem will disappear if you replace secfxp(None) by secfxp(None, integral=True) in the assignments for c and d. This way the parties that do not provide input still get the information that the fixed-point value happens to be a whole number. The secure protocols handling fixed-point numbers take advantage of this.

But this feature is not documented yet. Still working on what a good API is for such cases, and setting the integral attribute directly is the current approach. Nicer approach is that the input party will tell the other parties about this, if the information that the value happens to be integral is not sensitive by itself.

A tip regarding "MPyC idiom." It's best to avoid multiple calls to mpc.input() in different branches of a conditional. So, the assignments to c and d, including the above correction, are better rendered like this:

c = mpc.input(secfxp(10 if mpc.pid==0 else None, integral=True), senders=0)
d = mpc.input(secfxp(200 if mpc.pid==1 else None, integral=True), senders=1)