-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathbackend_mv.py
More file actions
203 lines (180 loc) · 6.85 KB
/
Copy pathbackend_mv.py
File metadata and controls
203 lines (180 loc) · 6.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
"""
Backend for using MaskVerif.
We use a custom version of maskverif that uses the same verification algorithm
but exposes it through a different interface.
"""
# Protocol for interaction with maskverif based on json lines
#
# Maskverif stdin:
# - 1st line is circuit description
# - each consecutive line is a Tuple
#
# Maskverif stdout:
# - each line is the Result for a Tuple
#
# Circuit description is an object that contains a single entry "gates".
# The "gates" entry is a list, whose element are objects with entries:
# - "name": string with a unique gate name
# - "kind": can be "secret", "random", "operation", "constant"
# - "operation": can be "mul", "add", "neg" (only present when "kind" is "operation")
# - "operands" is a list of gate names (only present when "kind" is "operation")
# - "value" is an integer (only present when "kind" is "constant")
#
# Tuple is an object with a single entry "probes" which is a list of gate names.
#
# Result is an object with a single entry "result" which is true (no secret
# dependency) or false (possible dependency).
import os
import logging
import json
import shutil
import subprocess as sp
from typing import Iterable
import tqdm
import gates
import sp_line_pool
import backends
class MvWorker(sp_line_pool.LineAsyncWorker):
async def post_init(self, circuit):
init_res = await self.execute_job(circuit)
assert init_res["done"] is True
async def execute_job(self, probes):
logging.debug(f"Backend {self._idx} job %s", json.dumps(probes))
await self.write_line(json.dumps(probes))
MV_PREFIX = "MVRES: "
res = ""
while not res.startswith(MV_PREFIX):
res = await self.read_line()
if res:
logging.debug(f"Backend {self._idx} from mv: %s", res)
return json.loads(res.removeprefix(MV_PREFIX))
class MaskVerifBackendMT(backends.Backend):
def __init__(
self,
all_gates: list[gates.Gate],
mv_exec,
num_workers: int = 1,
debug: bool = False,
):
self._debug = debug
if self._debug:
self._f = open("mv_input.txt", "w")
self._gates = dict()
self._circuit = backends.CircuitBuilder(all_gates)
self.timings = dict()
circuit = dict(gates=self._circuit.list_gates())
logging.info(f"Initializing maskverif circuit ({len(circuit['gates'])} gates)")
self._pool = sp_line_pool.SyncWorkPool(
num_workers, lambda i: MvWorker.create(i, mv_exec, circuit)
)
if self._debug:
self._f.write(json.dumps(circuit))
self._f.write("\n")
logging.info("Maskverif circuit initialized")
def name(self):
return "Maskverif"
def tuple2probes(self, all_gates: list[gates.Gate]):
probes = dict(probes=[self._circuit.name_of_gate(gate) for gate in all_gates])
if self._debug:
self._f.write(json.dumps(probes))
self._f.write("\n")
return probes
def process_res(self, tuple_res):
for k, v in tuple_res.items():
if k.endswith("_time"):
self.timings[k] = self.timings.get(k, 0.0) + v
return tuple_res["result"]
def check_gate_tuple(self, all_gates: list[gates.Gate]) -> bool:
probes = self.tuple2probes(all_gates)
logging.debug("Maskverif backend probes: %s", json.dumps(probes))
res = self._pool.exec(probes)
return self.process_res(res)
def check_gate_tuples(self, tuples: Iterable[list[gates.Gate]]) -> int:
"""Return the number of failures"""
tuples = tqdm.tqdm(tuples, desc="MV backend check")
n_fail = sum(
not self.process_res(res)
for res in self._pool.map(map(self.tuple2probes, tuples))
)
logging.info(f"MV timings (s): {self.timings}, {n_fail=}")
return n_fail
class MaskVerifBackend(backends.Backend):
def __init__(
self,
all_gates: list[gates.Gate],
mv_exec,
idx: int = 0,
debug: bool = False,
):
self._debug = debug
if self._debug:
self._f = open("mv_input.txt", "w")
self._idx = idx
self._gates = dict()
self._circuit = backends.CircuitBuilder(all_gates)
self.timings = dict()
circuit = dict(gates=self._circuit.list_gates())
self._sp = sp.Popen(
[mv_exec], stdin=sp.PIPE, stdout=sp.PIPE, text=True, bufsize=1
)
c_json = json.dumps(circuit)
# print("MV circuit", c_json)
logging.info(f"Backend {self._idx} writing MV circuit")
self._sp.stdin.write(c_json)
self._sp.stdin.write("\n")
if self._debug:
self._f.write(c_json)
self._f.write("\n")
logging.info(
f"Backend {self._idx} Initializing maskverif circuit... ({len(circuit['gates'])} gates)"
)
assert self._read_mv_res()["done"] is True
def name(self):
return "Maskverif"
def check_gate_tuple(self, all_gates: list[gates.Gate]) -> bool:
probes = dict(probes=[self._circuit.name_of_gate(gate) for gate in all_gates])
probes_json = json.dumps(probes)
logging.debug(f"Backend {self._idx}: probes_json: %s", probes_json)
self._sp.stdin.write(probes_json)
self._sp.stdin.write("\n")
if self._debug:
self._f.write(probes_json)
self._f.write("\n")
res = self._read_mv_res()
for k, v in res.items():
if k.endswith("_time"):
self.timings[k] = self.timings.get(k, 0.0) + v
# print("got", res)
return res["result"]
def check_gate_tuples(self, tuples: Iterable[list[gates.Gate]]) -> int:
"""Return the number of failures"""
logging.info("Enter MV check_gate_tuples")
res = sum(
not self.check_gate_tuple(p)
for p in tqdm.tqdm(tuples, desc="MV backend check")
)
logging.info(f"MV timings (s): {self.timings}")
return res
def _read_mv_res(self):
MV_PREFIX = "MVRES: "
res = ""
while not res.startswith(MV_PREFIX):
res = self._sp.stdout.readline()[:-1]
if res:
logging.debug(f"Backend {self._idx} from mv: %s", res)
return json.loads(res.removeprefix(MV_PREFIX))
def get_backend(all_gates: list[gates.Gate], n_bits: int):
mv_exec = shutil.which("maskverif.exe")
if mv_exec is None:
mv_exec = shutil.which("maskverif")
if mv_exec is None:
raise Exception(
"Could not find 'maskverif.exe', PATH={}".format(os.environ.get("PATH"))
)
if "NUM_THREADS" in os.environ:
num_threads = int(os.environ["NUM_THREADS"])
else:
num_threads = os.cpu_count()
if num_threads is None:
num_threads = 1
return MaskVerifBackendMT(all_gates, num_workers=num_threads, mv_exec=mv_exec)