Skip to content

Commit 2813c0e

Browse files
committed
Maintenance.
1 parent 7bc156e commit 2813c0e

File tree

140 files changed

+1598
-388
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

140 files changed

+1598
-388
lines changed

CHANGELOG.md

+14
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
11
The changelog explains changes pulled through from the private development repository. Bug fixes and small enhancements are committed between releases and not documented here.
22

3+
## 0.3.7 (August 14, 2023)
4+
5+
- Path Oblivious Heap (@tskovlund)
6+
- Adjust batch and bucket size to program
7+
- Direct communication available in more protocols
8+
- Option for seed in fake preprocessing (@strieflin)
9+
- Lower memory usage due to improved register allocation
10+
- New instructions to speed up CISC compilation
11+
- Protocol implementation example
12+
- Fixed security bug: missing MAC checks in multi-threaded programs
13+
- Fixed security bug: race condition in MAC check
14+
- Fixed security bug: missing shuffling check in PS mod 2^k and Brain
15+
- Fixed security bug: insufficient drowning in pairwise protocols
16+
317
## 0.3.6 (May 9, 2023)
418

519
- More extensive benchmarking outputs

Compiler/GC/types.py

+5-7
Original file line numberDiff line numberDiff line change
@@ -781,8 +781,6 @@ def store_in_mem(self, address):
781781
for i in range(n):
782782
for j, x in enumerate(v[i].bit_decompose()):
783783
x.store_in_mem(address + i + j * n)
784-
def reveal(self):
785-
return util.untuplify([x.reveal() for x in self.elements()])
786784
@classmethod
787785
def two_power(cls, nn, size=1):
788786
return cls.from_vec(
@@ -919,8 +917,7 @@ def bit_decompose(self, n_bits=None, security=None, maybe_mixed=None):
919917
return self.v[:n_bits]
920918
bit_compose = from_vec
921919
def reveal(self):
922-
assert len(self) == 1
923-
return self.v[0].reveal()
920+
return util.untuplify([x.reveal() for x in self.elements()])
924921
def long_one(self):
925922
return [x.long_one() for x in self.v]
926923
def __rsub__(self, other):
@@ -1279,7 +1276,8 @@ def pow2(self, k):
12791276

12801277
class sbitintvec(sbitvec, _bitint, _number, _sbitintbase):
12811278
"""
1282-
Vector of signed integers for parallel binary computation::
1279+
Vector of signed integers for parallel binary computation.
1280+
The following example uses vectors of size two::
12831281
12841282
sb32 = sbits.get_type(32)
12851283
siv32 = sbitintvec.get_type(32)
@@ -1291,7 +1289,7 @@ class sbitintvec(sbitvec, _bitint, _number, _sbitintbase):
12911289
print_ln('mul: %s, %s', c[0].reveal(), c[1].reveal())
12921290
c = (a - b).elements()
12931291
print_ln('sub: %s, %s', c[0].reveal(), c[1].reveal())
1294-
c = (a < b).bit_decompose()
1292+
c = (a < b).elements()
12951293
print_ln('lt: %s, %s', c[0].reveal(), c[1].reveal())
12961294
12971295
This should output::
@@ -1467,7 +1465,7 @@ class sbitfixvec(_fix, _vec):
14671465
print_ln('mul: %s, %s', c[0].reveal(), c[1].reveal())
14681466
c = (a - b).elements()
14691467
print_ln('sub: %s, %s', c[0].reveal(), c[1].reveal())
1470-
c = (a < b).bit_decompose()
1468+
c = (a < b).elements()
14711469
print_ln('lt: %s, %s', c[0].reveal(), c[1].reveal())
14721470
14731471
This should output roughly::

Compiler/allocator.py

+112-13
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def pop(self, size):
4343
else:
4444
done = False
4545
for x in self.by_logsize[logsize + 1:]:
46-
for block_size, addresses in x.items():
46+
for block_size, addresses in sorted(x.items()):
4747
if len(addresses) > 0:
4848
done = True
4949
break
@@ -60,16 +60,92 @@ def pop(self, size):
6060
self.by_address[addr + size] = diff
6161
return addr
6262

63+
class AllocRange:
64+
def __init__(self, base=0):
65+
self.base = base
66+
self.top = base
67+
self.limit = base
68+
self.grow = True
69+
self.pool = defaultdict(set)
70+
71+
def alloc(self, size):
72+
if self.pool[size]:
73+
return self.pool[size].pop()
74+
elif self.grow or self.top + size <= self.limit:
75+
res = self.top
76+
self.top += size
77+
self.limit = max(self.limit, self.top)
78+
if res >= REG_MAX:
79+
raise RegisterOverflowError()
80+
return res
81+
82+
def free(self, base, size):
83+
assert self.base <= base < self.top
84+
self.pool[size].add(base)
85+
86+
def stop_growing(self):
87+
self.grow = False
88+
89+
def consolidate(self):
90+
regs = []
91+
for size, pool in self.pool.items():
92+
for base in pool:
93+
regs.append((base, size))
94+
for base, size in reversed(sorted(regs)):
95+
if base + size == self.top:
96+
self.top -= size
97+
self.pool[size].remove(base)
98+
regs.pop()
99+
else:
100+
if program.Program.prog.verbose:
101+
print('cannot free %d register blocks '
102+
'by a gap of %d at %d' %
103+
(len(regs), self.top - size - base, base))
104+
break
105+
106+
class AllocPool:
107+
def __init__(self):
108+
self.ranges = defaultdict(lambda: [AllocRange()])
109+
self.by_base = {}
110+
111+
def alloc(self, reg_type, size):
112+
for r in self.ranges[reg_type]:
113+
res = r.alloc(size)
114+
if res is not None:
115+
self.by_base[reg_type, res] = r
116+
return res
117+
118+
def free(self, reg):
119+
r = self.by_base.pop((reg.reg_type, reg.i))
120+
r.free(reg.i, reg.size)
121+
122+
def new_ranges(self, min_usage):
123+
for t, n in min_usage.items():
124+
r = self.ranges[t][-1]
125+
assert (n >= r.limit)
126+
if r.limit < n:
127+
r.stop_growing()
128+
self.ranges[t].append(AllocRange(n))
129+
130+
def consolidate(self):
131+
for r in self.ranges.values():
132+
for rr in r:
133+
rr.consolidate()
134+
135+
def n_fragments(self):
136+
return max(len(r) for r in self.ranges)
137+
63138
class StraightlineAllocator:
64139
"""Allocate variables in a straightline program using n registers.
65140
It is based on the precondition that every register is only defined once."""
66141
def __init__(self, n, program):
67142
self.alloc = dict_by_id()
68-
self.usage = Compiler.program.RegType.create_dict(lambda: 0)
143+
self.max_usage = defaultdict(lambda: 0)
69144
self.defined = dict_by_id()
70145
self.dealloc = set_by_id()
71-
self.n = n
146+
assert(n == REG_MAX)
72147
self.program = program
148+
self.old_pool = None
73149

74150
def alloc_reg(self, reg, free):
75151
base = reg.vectorbase
@@ -79,14 +155,7 @@ def alloc_reg(self, reg, free):
79155

80156
reg_type = reg.reg_type
81157
size = base.size
82-
if free[reg_type, size]:
83-
res = free[reg_type, size].pop()
84-
else:
85-
if self.usage[reg_type] < self.n:
86-
res = self.usage[reg_type]
87-
self.usage[reg_type] += size
88-
else:
89-
raise RegisterOverflowError()
158+
res = free.alloc(reg_type, size)
90159
self.alloc[base] = res
91160

92161
base.i = self.alloc[base]
@@ -126,7 +195,7 @@ def dealloc_reg(self, reg, inst, free):
126195
for x in itertools.chain(dup.duplicates, base.duplicates):
127196
to_check.add(x)
128197

129-
free[reg.reg_type, base.size].append(self.alloc[base])
198+
free.free(base)
130199
if inst.is_vec() and base.vector:
131200
self.defined[base] = inst
132201
for i in base.vector:
@@ -135,6 +204,7 @@ def dealloc_reg(self, reg, inst, free):
135204
self.defined[reg] = inst
136205

137206
def process(self, program, alloc_pool):
207+
self.update_usage(alloc_pool)
138208
for k,i in enumerate(reversed(program)):
139209
unused_regs = []
140210
for j in i.get_def():
@@ -161,12 +231,26 @@ def process(self, program, alloc_pool):
161231
if k % 1000000 == 0 and k > 0:
162232
print("Allocated registers for %d instructions at" % k, time.asctime())
163233

234+
self.update_max_usage(alloc_pool)
235+
alloc_pool.consolidate()
236+
164237
# print "Successfully allocated registers"
165238
# print "modp usage: %d clear, %d secret" % \
166239
# (self.usage[Compiler.program.RegType.ClearModp], self.usage[Compiler.program.RegType.SecretModp])
167240
# print "GF2N usage: %d clear, %d secret" % \
168241
# (self.usage[Compiler.program.RegType.ClearGF2N], self.usage[Compiler.program.RegType.SecretGF2N])
169-
return self.usage
242+
return self.max_usage
243+
244+
def update_max_usage(self, alloc_pool):
245+
for t, r in alloc_pool.ranges.items():
246+
self.max_usage[t] = max(self.max_usage[t], r[-1].limit)
247+
248+
def update_usage(self, alloc_pool):
249+
if self.old_pool:
250+
self.update_max_usage(self.old_pool)
251+
if id(self.old_pool) != id(alloc_pool):
252+
alloc_pool.new_ranges(self.max_usage)
253+
self.old_pool = alloc_pool
170254

171255
def finalize(self, options):
172256
for reg in self.alloc:
@@ -178,6 +262,21 @@ def finalize(self, options):
178262
'\t\t'))
179263
if options.stop:
180264
sys.exit(1)
265+
if self.program.verbose:
266+
def p(sizes):
267+
total = defaultdict(lambda: 0)
268+
for (t, size) in sorted(sizes):
269+
n = sizes[t, size]
270+
total[t] += size * n
271+
print('%s:%d*%d' % (t, size, n), end=' ')
272+
print()
273+
print('Total:', dict(total))
274+
275+
sizes = defaultdict(lambda: 0)
276+
for reg in self.alloc:
277+
x = reg.reg_type, reg.size
278+
print('Used registers: ', end='')
279+
p(sizes)
181280

182281
def determine_scope(block, options):
183282
last_def = defaultdict_by_id(lambda: -1)

Compiler/circuit.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"""
1111

1212
from Compiler.GC.types import *
13-
from Compiler.library import function_block
13+
from Compiler.library import function_block, get_tape
1414
from Compiler import util
1515
import itertools
1616
import struct
@@ -54,7 +54,7 @@ def __call__(self, *inputs):
5454
return self.run(*inputs)
5555

5656
def run(self, *inputs):
57-
n = inputs[0][0].n
57+
n = inputs[0][0].n, get_tape()
5858
if n not in self.functions:
5959
self.functions[n] = function_block(lambda *args:
6060
self.compile(*args))

Compiler/compilerLib.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -270,9 +270,9 @@ def build_program(self, name=None):
270270
self.prog = Program(self.args, self.options, name=name)
271271
if self.execute:
272272
if self.options.execute in \
273-
("emulate", "ring", "rep-field"):
273+
("emulate", "ring", "rep-field", "rep4-ring"):
274274
self.prog.use_trunc_pr = True
275-
if self.options.execute in ("ring",):
275+
if self.options.execute in ("ring", "ps-rep-ring", "sy-rep-ring"):
276276
self.prog.use_split(3)
277277
if self.options.execute in ("semi2k",):
278278
self.prog.use_split(2)
@@ -487,6 +487,7 @@ def local_execution(self, args=[]):
487487
"Cannot produce %s. " % executable + \
488488
"Note that compilation requires a few GB of RAM.")
489489
vm = "%s/Scripts/%s.sh" % (self.root, self.options.execute)
490+
sys.stdout.flush()
490491
os.execl(vm, vm, self.prog.name, *args)
491492

492493
def remote_execution(self, args=[]):

Compiler/decision_tree.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -633,7 +633,8 @@ def preprocess_pandas(data):
633633
res.append(data.iloc[:,i].to_numpy())
634634
types.append('c')
635635
elif pandas.api.types.is_object_dtype(t):
636-
values = data.iloc[:,i].unique()
636+
values = list(filter(lambda x: isinstance(x, str),
637+
list(data.iloc[:,i].unique())))
637638
print('converting the following to unary:', values)
638639
if len(values) == 2:
639640
res.append(data.iloc[:,i].to_numpy() == values[1])

Compiler/instructions.py

+38
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,44 @@ class prefixsums(base.Instruction):
638638
code = base.opcodes['PREFIXSUMS']
639639
arg_format = ['sw','s']
640640

641+
class picks(base.VectorInstruction):
642+
""" Extract part of vector.
643+
644+
:param: result (sint)
645+
:param: input (sint)
646+
:param: start offset (int)
647+
:param: step
648+
649+
"""
650+
__slots__ = []
651+
code = base.opcodes['PICKS']
652+
arg_format = ['sw','s','int','int']
653+
654+
def __init__(self, *args):
655+
super(picks, self).__init__(*args)
656+
assert 0 <= args[2] < len(args[1])
657+
assert 0 <= args[2] + args[3] * len(args[0]) <= len(args[1])
658+
659+
class concats(base.VectorInstruction):
660+
""" Concatenate vectors.
661+
662+
:param: result (sint)
663+
:param: start offset (int)
664+
:param: input (sint)
665+
:param: (repeat from offset)...
666+
667+
"""
668+
__slots__ = []
669+
code = base.opcodes['CONCATS']
670+
arg_format = tools.chain(['sw'], tools.cycle(['int','s']))
671+
672+
def __init__(self, *args):
673+
super(concats, self).__init__(*args)
674+
assert len(args) % 2 == 1
675+
assert len(args[0]) == sum(args[1::2])
676+
for i in range(1, len(args), 2):
677+
assert args[i] == len(args[i + 1])
678+
641679
@base.gf2n
642680
@base.vectorize
643681
class mulc(base.MulBase):

0 commit comments

Comments
 (0)