@aztec/bb-prover 0.75.0-commit.c03ba01a2a4122e43e90d5133ba017e54b90e9d2 → 0.76.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dest/avm_proving_tests/avm_proving_tester.d.ts +25 -0
- package/dest/avm_proving_tests/avm_proving_tester.d.ts.map +1 -0
- package/dest/avm_proving_tests/avm_proving_tester.js +16 -14
- package/dest/bb/cli.d.ts +12 -0
- package/dest/bb/cli.d.ts.map +1 -0
- package/dest/bb/cli.js +9 -4
- package/dest/bb/execute.d.ts +170 -0
- package/dest/bb/execute.d.ts.map +1 -0
- package/dest/bb/execute.js +267 -395
- package/dest/bb/index.d.ts +3 -0
- package/dest/bb/index.d.ts.map +1 -0
- package/dest/bb/index.js +6 -4
- package/dest/config.d.ts +13 -0
- package/dest/config.d.ts.map +1 -0
- package/dest/config.js +2 -1
- package/dest/honk.d.ts +13 -0
- package/dest/honk.d.ts.map +1 -0
- package/dest/honk.js +5 -8
- package/dest/index.d.ts +8 -0
- package/dest/index.d.ts.map +1 -0
- package/dest/index.js +1 -0
- package/dest/instrumentation.d.ts +47 -0
- package/dest/instrumentation.d.ts.map +1 -0
- package/dest/instrumentation.js +41 -44
- package/dest/prover/bb_native_private_kernel_prover.d.ts +25 -0
- package/dest/prover/bb_native_private_kernel_prover.d.ts.map +1 -0
- package/dest/prover/bb_native_private_kernel_prover.js +19 -19
- package/dest/prover/bb_private_kernel_prover.d.ts +31 -0
- package/dest/prover/bb_private_kernel_prover.d.ts.map +1 -0
- package/dest/prover/bb_private_kernel_prover.js +11 -11
- package/dest/prover/bb_prover.d.ts +123 -0
- package/dest/prover/bb_prover.d.ts.map +1 -0
- package/dest/prover/bb_prover.js +445 -431
- package/dest/prover/client_ivc_proof_utils.d.ts +25 -0
- package/dest/prover/client_ivc_proof_utils.d.ts.map +1 -0
- package/dest/prover/client_ivc_proof_utils.js +9 -15
- package/dest/prover/index.d.ts +4 -0
- package/dest/prover/index.d.ts.map +1 -0
- package/dest/prover/index.js +1 -0
- package/dest/stats.d.ts +5 -0
- package/dest/stats.d.ts.map +1 -0
- package/dest/stats.js +14 -15
- package/dest/test/index.d.ts +3 -0
- package/dest/test/index.d.ts.map +1 -0
- package/dest/test/index.js +1 -0
- package/dest/test/test_circuit_prover.d.ts +72 -0
- package/dest/test/test_circuit_prover.d.ts.map +1 -0
- package/dest/test/test_circuit_prover.js +160 -156
- package/dest/test/test_verifier.d.ts +5 -0
- package/dest/test/test_verifier.d.ts.map +1 -0
- package/dest/test/test_verifier.js +1 -0
- package/dest/verification_key/verification_key_data.d.ts +9 -0
- package/dest/verification_key/verification_key_data.d.ts.map +1 -0
- package/dest/verification_key/verification_key_data.js +8 -10
- package/dest/verifier/bb_verifier.d.ts +15 -0
- package/dest/verifier/bb_verifier.d.ts.map +1 -0
- package/dest/verifier/bb_verifier.js +13 -13
- package/dest/verifier/index.d.ts +2 -0
- package/dest/verifier/index.d.ts.map +1 -0
- package/dest/verifier/index.js +1 -0
- package/dest/wasm/bb_wasm_private_kernel_prover.d.ts +16 -0
- package/dest/wasm/bb_wasm_private_kernel_prover.d.ts.map +1 -0
- package/dest/wasm/bb_wasm_private_kernel_prover.js +10 -11
- package/dest/wasm/bundle.d.ts +6 -0
- package/dest/wasm/bundle.d.ts.map +1 -0
- package/dest/wasm/bundle.js +2 -1
- package/dest/wasm/lazy.d.ts +6 -0
- package/dest/wasm/lazy.d.ts.map +1 -0
- package/dest/wasm/lazy.js +2 -1
- package/package.json +10 -10
- package/src/avm_proving_tests/avm_proving_tester.ts +15 -3
package/dest/bb/execute.js
CHANGED
|
@@ -12,12 +12,12 @@ export const PROOF_FIELDS_FILENAME = 'proof_fields.json';
|
|
|
12
12
|
export const AVM_BYTECODE_FILENAME = 'avm_bytecode.bin';
|
|
13
13
|
export const AVM_PUBLIC_INPUTS_FILENAME = 'avm_public_inputs.bin';
|
|
14
14
|
export const AVM_HINTS_FILENAME = 'avm_hints.bin';
|
|
15
|
-
export var BB_RESULT
|
|
15
|
+
export var BB_RESULT;
|
|
16
|
+
(function (BB_RESULT) {
|
|
16
17
|
BB_RESULT[BB_RESULT["SUCCESS"] = 0] = "SUCCESS";
|
|
17
18
|
BB_RESULT[BB_RESULT["FAILURE"] = 1] = "FAILURE";
|
|
18
19
|
BB_RESULT[BB_RESULT["ALREADY_PRESENT"] = 2] = "ALREADY_PRESENT";
|
|
19
|
-
|
|
20
|
-
}({});
|
|
20
|
+
})(BB_RESULT || (BB_RESULT = {}));
|
|
21
21
|
/**
|
|
22
22
|
* Invokes the Barretenberg binary with the provided command and args
|
|
23
23
|
* @param pathToBB - The path to the BB binary
|
|
@@ -26,66 +26,51 @@ export var BB_RESULT = /*#__PURE__*/ function(BB_RESULT) {
|
|
|
26
26
|
* @param logger - A log function
|
|
27
27
|
* @param resultParser - An optional handler for detecting success or failure
|
|
28
28
|
* @returns The completed partial witness outputted from the circuit
|
|
29
|
-
*/
|
|
30
|
-
|
|
29
|
+
*/
|
|
30
|
+
export function executeBB(pathToBB, command, args, logger, resultParser = (code) => code === 0) {
|
|
31
|
+
return new Promise(resolve => {
|
|
31
32
|
// spawn the bb process
|
|
32
33
|
const { HARDWARE_CONCURRENCY: _, ...envWithoutConcurrency } = process.env;
|
|
33
34
|
const env = process.env.HARDWARE_CONCURRENCY ? process.env : envWithoutConcurrency;
|
|
34
35
|
logger(`Executing BB with: ${pathToBB} ${command} ${args.join(' ')}`);
|
|
35
|
-
const bb = proc.spawn(pathToBB, [
|
|
36
|
-
|
|
37
|
-
...args
|
|
38
|
-
], {
|
|
39
|
-
env
|
|
36
|
+
const bb = proc.spawn(pathToBB, [command, ...args], {
|
|
37
|
+
env,
|
|
40
38
|
});
|
|
41
|
-
bb.stdout.on('data',
|
|
39
|
+
bb.stdout.on('data', data => {
|
|
42
40
|
const message = data.toString('utf-8').replace(/\n$/, '');
|
|
43
41
|
logger(message);
|
|
44
42
|
});
|
|
45
|
-
bb.stderr.on('data',
|
|
43
|
+
bb.stderr.on('data', data => {
|
|
46
44
|
const message = data.toString('utf-8').replace(/\n$/, '');
|
|
47
45
|
logger(message);
|
|
48
46
|
});
|
|
49
|
-
bb.on('close', (exitCode, signal)=>{
|
|
47
|
+
bb.on('close', (exitCode, signal) => {
|
|
50
48
|
if (resultParser(exitCode)) {
|
|
51
|
-
resolve({
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
});
|
|
56
|
-
} else {
|
|
57
|
-
resolve({
|
|
58
|
-
status: 1,
|
|
59
|
-
exitCode,
|
|
60
|
-
signal
|
|
61
|
-
});
|
|
49
|
+
resolve({ status: BB_RESULT.SUCCESS, exitCode, signal });
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
resolve({ status: BB_RESULT.FAILURE, exitCode, signal });
|
|
62
53
|
}
|
|
63
54
|
});
|
|
64
|
-
}).catch(
|
|
65
|
-
status: 1,
|
|
66
|
-
exitCode: -1,
|
|
67
|
-
signal: undefined
|
|
68
|
-
}));
|
|
55
|
+
}).catch(_ => ({ status: BB_RESULT.FAILURE, exitCode: -1, signal: undefined }));
|
|
69
56
|
}
|
|
70
57
|
// TODO(#7369) comment this etc (really just take inspiration from this and rewrite it all O:))
|
|
71
58
|
export async function executeBbClientIvcProof(pathToBB, workingDirectory, bytecodeStackPath, witnessStackPath, log) {
|
|
72
59
|
// Check that the working directory exists
|
|
73
60
|
try {
|
|
74
61
|
await fs.access(workingDirectory);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
reason: `Working directory ${workingDirectory} does not exist`
|
|
79
|
-
};
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
return { status: BB_RESULT.FAILURE, reason: `Working directory ${workingDirectory} does not exist` };
|
|
80
65
|
}
|
|
81
66
|
// The proof is written to e.g. /workingDirectory/proof
|
|
82
67
|
const outputPath = `${workingDirectory}`;
|
|
83
|
-
const binaryPresent = await fs
|
|
68
|
+
const binaryPresent = await fs
|
|
69
|
+
.access(pathToBB, fs.constants.R_OK)
|
|
70
|
+
.then(_ => true)
|
|
71
|
+
.catch(_ => false);
|
|
84
72
|
if (!binaryPresent) {
|
|
85
|
-
return {
|
|
86
|
-
status: 1,
|
|
87
|
-
reason: `Failed to find bb binary at ${pathToBB}`
|
|
88
|
-
};
|
|
73
|
+
return { status: BB_RESULT.FAILURE, reason: `Failed to find bb binary at ${pathToBB}` };
|
|
89
74
|
}
|
|
90
75
|
try {
|
|
91
76
|
// Write the bytecode to the working directory
|
|
@@ -102,35 +87,33 @@ export async function executeBbClientIvcProof(pathToBB, workingDirectory, byteco
|
|
|
102
87
|
'--scheme',
|
|
103
88
|
'client_ivc',
|
|
104
89
|
'--input_type',
|
|
105
|
-
'runtime_stack'
|
|
90
|
+
'runtime_stack',
|
|
106
91
|
];
|
|
107
92
|
const timer = new Timer();
|
|
108
|
-
const logFunction = (message)=>{
|
|
93
|
+
const logFunction = (message) => {
|
|
109
94
|
log(`bb - ${message}`);
|
|
110
95
|
};
|
|
111
96
|
const result = await executeBB(pathToBB, 'prove', args, logFunction);
|
|
112
97
|
const durationMs = timer.ms();
|
|
113
|
-
if (result.status ==
|
|
98
|
+
if (result.status == BB_RESULT.SUCCESS) {
|
|
114
99
|
return {
|
|
115
|
-
status:
|
|
100
|
+
status: BB_RESULT.SUCCESS,
|
|
116
101
|
durationMs,
|
|
117
102
|
proofPath: `${outputPath}`,
|
|
118
103
|
pkPath: undefined,
|
|
119
|
-
vkPath: `${outputPath}
|
|
104
|
+
vkPath: `${outputPath}`,
|
|
120
105
|
};
|
|
121
106
|
}
|
|
122
107
|
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
123
108
|
return {
|
|
124
|
-
status:
|
|
109
|
+
status: BB_RESULT.FAILURE,
|
|
125
110
|
reason: `Failed to generate proof. Exit code ${result.exitCode}. Signal ${result.signal}.`,
|
|
126
|
-
retry: !!result.signal
|
|
127
|
-
};
|
|
128
|
-
} catch (error) {
|
|
129
|
-
return {
|
|
130
|
-
status: 1,
|
|
131
|
-
reason: `${error}`
|
|
111
|
+
retry: !!result.signal,
|
|
132
112
|
};
|
|
133
113
|
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
116
|
+
}
|
|
134
117
|
}
|
|
135
118
|
/**
|
|
136
119
|
* Used for generating verification keys of noir circuits.
|
|
@@ -142,77 +125,58 @@ export async function executeBbClientIvcProof(pathToBB, workingDirectory, byteco
|
|
|
142
125
|
* @param inputWitnessFile - The circuit input witness
|
|
143
126
|
* @param log - A logging function
|
|
144
127
|
* @returns An object containing a result indication, the location of the VK and the duration taken
|
|
145
|
-
*/
|
|
128
|
+
*/
|
|
129
|
+
export async function computeVerificationKey(pathToBB, workingDirectory, circuitName, bytecode, recursive, flavor, log) {
|
|
146
130
|
// Check that the working directory exists
|
|
147
131
|
try {
|
|
148
132
|
await fs.access(workingDirectory);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
reason: `Working directory ${workingDirectory} does not exist`
|
|
153
|
-
};
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
return { status: BB_RESULT.FAILURE, reason: `Working directory ${workingDirectory} does not exist` };
|
|
154
136
|
}
|
|
155
137
|
// The bytecode is written to e.g. /workingDirectory/BaseParityArtifact-bytecode
|
|
156
138
|
const bytecodePath = `${workingDirectory}/${circuitName}-bytecode`;
|
|
157
139
|
// The verification key is written to this path
|
|
158
140
|
const outputPath = `${workingDirectory}/vk`;
|
|
159
|
-
const binaryPresent = await fs
|
|
141
|
+
const binaryPresent = await fs
|
|
142
|
+
.access(pathToBB, fs.constants.R_OK)
|
|
143
|
+
.then(_ => true)
|
|
144
|
+
.catch(_ => false);
|
|
160
145
|
if (!binaryPresent) {
|
|
161
|
-
return {
|
|
162
|
-
status: 1,
|
|
163
|
-
reason: `Failed to find bb binary at ${pathToBB}`
|
|
164
|
-
};
|
|
146
|
+
return { status: BB_RESULT.FAILURE, reason: `Failed to find bb binary at ${pathToBB}` };
|
|
165
147
|
}
|
|
166
148
|
try {
|
|
167
149
|
// Write the bytecode to the working directory
|
|
168
150
|
await fs.writeFile(bytecodePath, bytecode);
|
|
169
151
|
const timer = new Timer();
|
|
170
|
-
const logFunction = (message)=>{
|
|
152
|
+
const logFunction = (message) => {
|
|
171
153
|
log(`computeVerificationKey(${circuitName}) BB out - ${message}`);
|
|
172
154
|
};
|
|
173
|
-
const args = [
|
|
174
|
-
'-o',
|
|
175
|
-
outputPath,
|
|
176
|
-
'-b',
|
|
177
|
-
bytecodePath,
|
|
178
|
-
'-v',
|
|
179
|
-
recursive ? '--recursive' : ''
|
|
180
|
-
];
|
|
155
|
+
const args = ['-o', outputPath, '-b', bytecodePath, '-v', recursive ? '--recursive' : ''];
|
|
181
156
|
let result = await executeBB(pathToBB, `write_vk_${flavor}`, args, logFunction);
|
|
182
|
-
if (result.status ==
|
|
183
|
-
return {
|
|
184
|
-
status: 1,
|
|
185
|
-
reason: 'Failed writing VK.'
|
|
186
|
-
};
|
|
157
|
+
if (result.status == BB_RESULT.FAILURE) {
|
|
158
|
+
return { status: BB_RESULT.FAILURE, reason: 'Failed writing VK.' };
|
|
187
159
|
}
|
|
188
|
-
result = await executeBB(pathToBB, `vk_as_fields_${flavor}`, [
|
|
189
|
-
'-o',
|
|
190
|
-
outputPath + '_fields.json',
|
|
191
|
-
'-k',
|
|
192
|
-
outputPath,
|
|
193
|
-
'-v'
|
|
194
|
-
], logFunction);
|
|
160
|
+
result = await executeBB(pathToBB, `vk_as_fields_${flavor}`, ['-o', outputPath + '_fields.json', '-k', outputPath, '-v'], logFunction);
|
|
195
161
|
const duration = timer.ms();
|
|
196
|
-
if (result.status ==
|
|
162
|
+
if (result.status == BB_RESULT.SUCCESS) {
|
|
197
163
|
return {
|
|
198
|
-
status:
|
|
164
|
+
status: BB_RESULT.SUCCESS,
|
|
199
165
|
durationMs: duration,
|
|
200
166
|
pkPath: undefined,
|
|
201
|
-
vkPath: `${outputPath}
|
|
167
|
+
vkPath: `${outputPath}`,
|
|
202
168
|
};
|
|
203
169
|
}
|
|
204
170
|
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
205
171
|
return {
|
|
206
|
-
status:
|
|
172
|
+
status: BB_RESULT.FAILURE,
|
|
207
173
|
reason: `Failed to write VK. Exit code ${result.exitCode}. Signal ${result.signal}.`,
|
|
208
|
-
retry: !!result.signal
|
|
209
|
-
};
|
|
210
|
-
} catch (error) {
|
|
211
|
-
return {
|
|
212
|
-
status: 1,
|
|
213
|
-
reason: `${error}`
|
|
174
|
+
retry: !!result.signal,
|
|
214
175
|
};
|
|
215
176
|
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
179
|
+
}
|
|
216
180
|
}
|
|
217
181
|
/**
|
|
218
182
|
* Used for generating proofs of noir circuits.
|
|
@@ -224,67 +188,55 @@ export async function executeBbClientIvcProof(pathToBB, workingDirectory, byteco
|
|
|
224
188
|
* @param inputWitnessFile - The circuit input witness
|
|
225
189
|
* @param log - A logging function
|
|
226
190
|
* @returns An object containing a result indication, the location of the proof and the duration taken
|
|
227
|
-
*/
|
|
191
|
+
*/
|
|
192
|
+
export async function generateProof(pathToBB, workingDirectory, circuitName, bytecode, recursive, inputWitnessFile, flavor, log) {
|
|
228
193
|
// Check that the working directory exists
|
|
229
194
|
try {
|
|
230
195
|
await fs.access(workingDirectory);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
reason: `Working directory ${workingDirectory} does not exist`
|
|
235
|
-
};
|
|
196
|
+
}
|
|
197
|
+
catch (error) {
|
|
198
|
+
return { status: BB_RESULT.FAILURE, reason: `Working directory ${workingDirectory} does not exist` };
|
|
236
199
|
}
|
|
237
200
|
// The bytecode is written to e.g. /workingDirectory/BaseParityArtifact-bytecode
|
|
238
201
|
const bytecodePath = `${workingDirectory}/${circuitName}-bytecode`;
|
|
239
202
|
// The proof is written to e.g. /workingDirectory/ultra_honk/proof
|
|
240
203
|
const outputPath = `${workingDirectory}`;
|
|
241
|
-
const binaryPresent = await fs
|
|
204
|
+
const binaryPresent = await fs
|
|
205
|
+
.access(pathToBB, fs.constants.R_OK)
|
|
206
|
+
.then(_ => true)
|
|
207
|
+
.catch(_ => false);
|
|
242
208
|
if (!binaryPresent) {
|
|
243
|
-
return {
|
|
244
|
-
status: 1,
|
|
245
|
-
reason: `Failed to find bb binary at ${pathToBB}`
|
|
246
|
-
};
|
|
209
|
+
return { status: BB_RESULT.FAILURE, reason: `Failed to find bb binary at ${pathToBB}` };
|
|
247
210
|
}
|
|
248
211
|
try {
|
|
249
212
|
// Write the bytecode to the working directory
|
|
250
213
|
await fs.writeFile(bytecodePath, bytecode);
|
|
251
|
-
const args = [
|
|
252
|
-
'-o',
|
|
253
|
-
outputPath,
|
|
254
|
-
'-b',
|
|
255
|
-
bytecodePath,
|
|
256
|
-
'-w',
|
|
257
|
-
inputWitnessFile,
|
|
258
|
-
'-v',
|
|
259
|
-
recursive ? '--recursive' : ''
|
|
260
|
-
];
|
|
214
|
+
const args = ['-o', outputPath, '-b', bytecodePath, '-w', inputWitnessFile, '-v', recursive ? '--recursive' : ''];
|
|
261
215
|
const timer = new Timer();
|
|
262
|
-
const logFunction = (message)=>{
|
|
216
|
+
const logFunction = (message) => {
|
|
263
217
|
log(`${circuitName} BB out - ${message}`);
|
|
264
218
|
};
|
|
265
219
|
const result = await executeBB(pathToBB, `prove_${flavor}_output_all`, args, logFunction);
|
|
266
220
|
const duration = timer.ms();
|
|
267
|
-
if (result.status ==
|
|
221
|
+
if (result.status == BB_RESULT.SUCCESS) {
|
|
268
222
|
return {
|
|
269
|
-
status:
|
|
223
|
+
status: BB_RESULT.SUCCESS,
|
|
270
224
|
durationMs: duration,
|
|
271
225
|
proofPath: `${outputPath}`,
|
|
272
226
|
pkPath: undefined,
|
|
273
|
-
vkPath: `${outputPath}
|
|
227
|
+
vkPath: `${outputPath}`,
|
|
274
228
|
};
|
|
275
229
|
}
|
|
276
230
|
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
277
231
|
return {
|
|
278
|
-
status:
|
|
232
|
+
status: BB_RESULT.FAILURE,
|
|
279
233
|
reason: `Failed to generate proof. Exit code ${result.exitCode}. Signal ${result.signal}.`,
|
|
280
|
-
retry: !!result.signal
|
|
281
|
-
};
|
|
282
|
-
} catch (error) {
|
|
283
|
-
return {
|
|
284
|
-
status: 1,
|
|
285
|
-
reason: `${error}`
|
|
234
|
+
retry: !!result.signal,
|
|
286
235
|
};
|
|
287
236
|
}
|
|
237
|
+
catch (error) {
|
|
238
|
+
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
239
|
+
}
|
|
288
240
|
}
|
|
289
241
|
/**
|
|
290
242
|
* Used for generating proofs of the tube circuit
|
|
@@ -296,68 +248,58 @@ export async function executeBbClientIvcProof(pathToBB, workingDirectory, byteco
|
|
|
296
248
|
* @param inputWitnessFile - The circuit input witness
|
|
297
249
|
* @param log - A logging function
|
|
298
250
|
* @returns An object containing a result indication, the location of the proof and the duration taken
|
|
299
|
-
*/
|
|
251
|
+
*/
|
|
252
|
+
export async function generateTubeProof(pathToBB, workingDirectory, log) {
|
|
300
253
|
// Check that the working directory exists
|
|
301
254
|
try {
|
|
302
255
|
await fs.access(workingDirectory);
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
reason: `Working directory ${workingDirectory} does not exist`
|
|
307
|
-
};
|
|
256
|
+
}
|
|
257
|
+
catch (error) {
|
|
258
|
+
return { status: BB_RESULT.FAILURE, reason: `Working directory ${workingDirectory} does not exist` };
|
|
308
259
|
}
|
|
309
260
|
// // Paths for the inputs
|
|
310
261
|
const vkPath = join(workingDirectory, CLIENT_IVC_VK_FILE_NAME);
|
|
311
262
|
const proofPath = join(workingDirectory, CLIENT_IVC_PROOF_FILE_NAME);
|
|
312
263
|
// The proof is written to e.g. /workingDirectory/proof
|
|
313
264
|
const outputPath = workingDirectory;
|
|
314
|
-
const filePresent = async (file)=>await fs
|
|
265
|
+
const filePresent = async (file) => await fs
|
|
266
|
+
.access(file, fs.constants.R_OK)
|
|
267
|
+
.then(_ => true)
|
|
268
|
+
.catch(_ => false);
|
|
315
269
|
const binaryPresent = await filePresent(pathToBB);
|
|
316
270
|
if (!binaryPresent) {
|
|
317
|
-
return {
|
|
318
|
-
status: 1,
|
|
319
|
-
reason: `Failed to find bb binary at ${pathToBB}`
|
|
320
|
-
};
|
|
271
|
+
return { status: BB_RESULT.FAILURE, reason: `Failed to find bb binary at ${pathToBB}` };
|
|
321
272
|
}
|
|
322
273
|
try {
|
|
323
|
-
if (!await filePresent(vkPath) || !await filePresent(proofPath)) {
|
|
324
|
-
return {
|
|
325
|
-
status: 1,
|
|
326
|
-
reason: `Client IVC input files not present in ${workingDirectory}`
|
|
327
|
-
};
|
|
274
|
+
if (!(await filePresent(vkPath)) || !(await filePresent(proofPath))) {
|
|
275
|
+
return { status: BB_RESULT.FAILURE, reason: `Client IVC input files not present in ${workingDirectory}` };
|
|
328
276
|
}
|
|
329
|
-
const args = [
|
|
330
|
-
'-o',
|
|
331
|
-
outputPath,
|
|
332
|
-
'-v'
|
|
333
|
-
];
|
|
277
|
+
const args = ['-o', outputPath, '-v'];
|
|
334
278
|
const timer = new Timer();
|
|
335
|
-
const logFunction = (message)=>{
|
|
279
|
+
const logFunction = (message) => {
|
|
336
280
|
log(`TubeCircuit (prove) BB out - ${message}`);
|
|
337
281
|
};
|
|
338
282
|
const result = await executeBB(pathToBB, 'prove_tube', args, logFunction);
|
|
339
283
|
const durationMs = timer.ms();
|
|
340
|
-
if (result.status ==
|
|
284
|
+
if (result.status == BB_RESULT.SUCCESS) {
|
|
341
285
|
return {
|
|
342
|
-
status:
|
|
286
|
+
status: BB_RESULT.SUCCESS,
|
|
343
287
|
durationMs,
|
|
344
288
|
proofPath: outputPath,
|
|
345
289
|
pkPath: undefined,
|
|
346
|
-
vkPath: outputPath
|
|
290
|
+
vkPath: outputPath,
|
|
347
291
|
};
|
|
348
292
|
}
|
|
349
293
|
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
350
294
|
return {
|
|
351
|
-
status:
|
|
295
|
+
status: BB_RESULT.FAILURE,
|
|
352
296
|
reason: `Failed to generate proof. Exit code ${result.exitCode}. Signal ${result.signal}.`,
|
|
353
|
-
retry: !!result.signal
|
|
354
|
-
};
|
|
355
|
-
} catch (error) {
|
|
356
|
-
return {
|
|
357
|
-
status: 1,
|
|
358
|
-
reason: `${error}`
|
|
297
|
+
retry: !!result.signal,
|
|
359
298
|
};
|
|
360
299
|
}
|
|
300
|
+
catch (error) {
|
|
301
|
+
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
302
|
+
}
|
|
361
303
|
}
|
|
362
304
|
/**
|
|
363
305
|
* Used for generating AVM proofs.
|
|
@@ -367,71 +309,65 @@ export async function executeBbClientIvcProof(pathToBB, workingDirectory, byteco
|
|
|
367
309
|
* @param input - The inputs for the public function to be proven
|
|
368
310
|
* @param log - A logging function
|
|
369
311
|
* @returns An object containing a result indication, the location of the proof and the duration taken
|
|
370
|
-
*/
|
|
312
|
+
*/
|
|
313
|
+
export async function generateAvmProofV2(pathToBB, workingDirectory, input, logger) {
|
|
371
314
|
// Check that the working directory exists
|
|
372
315
|
try {
|
|
373
316
|
await fs.access(workingDirectory);
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
reason: `Working directory ${workingDirectory} does not exist`
|
|
378
|
-
};
|
|
317
|
+
}
|
|
318
|
+
catch (error) {
|
|
319
|
+
return { status: BB_RESULT.FAILURE, reason: `Working directory ${workingDirectory} does not exist` };
|
|
379
320
|
}
|
|
380
321
|
// The proof is written to e.g. /workingDirectory/proof
|
|
381
322
|
const outputPath = workingDirectory;
|
|
382
|
-
const filePresent = async (file)=>await fs
|
|
323
|
+
const filePresent = async (file) => await fs
|
|
324
|
+
.access(file, fs.constants.R_OK)
|
|
325
|
+
.then(_ => true)
|
|
326
|
+
.catch(_ => false);
|
|
383
327
|
const binaryPresent = await filePresent(pathToBB);
|
|
384
328
|
if (!binaryPresent) {
|
|
385
|
-
return {
|
|
386
|
-
status: 1,
|
|
387
|
-
reason: `Failed to find bb binary at ${pathToBB}`
|
|
388
|
-
};
|
|
329
|
+
return { status: BB_RESULT.FAILURE, reason: `Failed to find bb binary at ${pathToBB}` };
|
|
389
330
|
}
|
|
390
331
|
const inputsBuffer = input.serializeForAvm2();
|
|
391
332
|
try {
|
|
392
333
|
// Write the inputs to the working directory.
|
|
393
334
|
const avmInputsPath = join(workingDirectory, 'avm_inputs.bin');
|
|
394
335
|
await fs.writeFile(avmInputsPath, inputsBuffer);
|
|
395
|
-
if (!await filePresent(avmInputsPath)) {
|
|
396
|
-
return {
|
|
397
|
-
status: 1,
|
|
398
|
-
reason: `Could not write avm inputs to ${avmInputsPath}`
|
|
399
|
-
};
|
|
336
|
+
if (!(await filePresent(avmInputsPath))) {
|
|
337
|
+
return { status: BB_RESULT.FAILURE, reason: `Could not write avm inputs to ${avmInputsPath}` };
|
|
400
338
|
}
|
|
401
339
|
const args = [
|
|
402
340
|
'--avm-inputs',
|
|
403
341
|
avmInputsPath,
|
|
404
342
|
'-o',
|
|
405
343
|
outputPath,
|
|
406
|
-
logger.level === 'debug' || logger.level === 'trace' ? '-d' : logger.level === 'verbose' ? '-v' : ''
|
|
344
|
+
logger.level === 'debug' || logger.level === 'trace' ? '-d' : logger.level === 'verbose' ? '-v' : '',
|
|
407
345
|
];
|
|
408
346
|
const timer = new Timer();
|
|
409
|
-
const logFunction = (message)=>{
|
|
347
|
+
const logFunction = (message) => {
|
|
410
348
|
logger.verbose(`AvmCircuit (prove) BB out - ${message}`);
|
|
411
349
|
};
|
|
412
350
|
const result = await executeBB(pathToBB, 'avm2_prove', args, logFunction);
|
|
413
351
|
const duration = timer.ms();
|
|
414
|
-
if (result.status ==
|
|
352
|
+
if (result.status == BB_RESULT.SUCCESS) {
|
|
415
353
|
return {
|
|
416
|
-
status:
|
|
354
|
+
status: BB_RESULT.SUCCESS,
|
|
417
355
|
durationMs: duration,
|
|
418
356
|
proofPath: join(outputPath, PROOF_FILENAME),
|
|
419
357
|
pkPath: undefined,
|
|
420
|
-
vkPath: outputPath
|
|
358
|
+
vkPath: outputPath,
|
|
421
359
|
};
|
|
422
360
|
}
|
|
423
361
|
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
424
362
|
return {
|
|
425
|
-
status:
|
|
363
|
+
status: BB_RESULT.FAILURE,
|
|
426
364
|
reason: `Failed to generate proof. Exit code ${result.exitCode}. Signal ${result.signal}.`,
|
|
427
|
-
retry: !!result.signal
|
|
428
|
-
};
|
|
429
|
-
} catch (error) {
|
|
430
|
-
return {
|
|
431
|
-
status: 1,
|
|
432
|
-
reason: `${error}`
|
|
365
|
+
retry: !!result.signal,
|
|
433
366
|
};
|
|
434
367
|
}
|
|
368
|
+
catch (error) {
|
|
369
|
+
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
370
|
+
}
|
|
435
371
|
}
|
|
436
372
|
/**
|
|
437
373
|
* Used for generating AVM proofs (or doing check-circuit).
|
|
@@ -441,44 +377,37 @@ export async function executeBbClientIvcProof(pathToBB, workingDirectory, byteco
|
|
|
441
377
|
* @param bytecode - The AVM bytecode for the public function to be proven (expected to be decompressed)
|
|
442
378
|
* @param log - A logging function
|
|
443
379
|
* @returns An object containing a result indication, the location of the proof and the duration taken
|
|
444
|
-
*/
|
|
380
|
+
*/
|
|
381
|
+
export async function generateAvmProof(pathToBB, workingDirectory, input, logger, checkCircuitOnly = false) {
|
|
445
382
|
// Check that the working directory exists
|
|
446
383
|
try {
|
|
447
384
|
await fs.access(workingDirectory);
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
reason: `Working directory ${workingDirectory} does not exist`
|
|
452
|
-
};
|
|
385
|
+
}
|
|
386
|
+
catch (error) {
|
|
387
|
+
return { status: BB_RESULT.FAILURE, reason: `Working directory ${workingDirectory} does not exist` };
|
|
453
388
|
}
|
|
454
389
|
// Paths for the inputs
|
|
455
390
|
const publicInputsPath = join(workingDirectory, AVM_PUBLIC_INPUTS_FILENAME);
|
|
456
391
|
const avmHintsPath = join(workingDirectory, AVM_HINTS_FILENAME);
|
|
457
392
|
// The proof is written to e.g. /workingDirectory/proof
|
|
458
393
|
const outputPath = workingDirectory;
|
|
459
|
-
const filePresent = async (file)=>await fs
|
|
394
|
+
const filePresent = async (file) => await fs
|
|
395
|
+
.access(file, fs.constants.R_OK)
|
|
396
|
+
.then(_ => true)
|
|
397
|
+
.catch(_ => false);
|
|
460
398
|
const binaryPresent = await filePresent(pathToBB);
|
|
461
399
|
if (!binaryPresent) {
|
|
462
|
-
return {
|
|
463
|
-
status: 1,
|
|
464
|
-
reason: `Failed to find bb binary at ${pathToBB}`
|
|
465
|
-
};
|
|
400
|
+
return { status: BB_RESULT.FAILURE, reason: `Failed to find bb binary at ${pathToBB}` };
|
|
466
401
|
}
|
|
467
402
|
try {
|
|
468
403
|
// Write the inputs to the working directory.
|
|
469
404
|
await fs.writeFile(publicInputsPath, input.publicInputs.toBuffer());
|
|
470
|
-
if (!await filePresent(publicInputsPath)) {
|
|
471
|
-
return {
|
|
472
|
-
status: 1,
|
|
473
|
-
reason: `Could not write publicInputs at ${publicInputsPath}`
|
|
474
|
-
};
|
|
405
|
+
if (!(await filePresent(publicInputsPath))) {
|
|
406
|
+
return { status: BB_RESULT.FAILURE, reason: `Could not write publicInputs at ${publicInputsPath}` };
|
|
475
407
|
}
|
|
476
408
|
await fs.writeFile(avmHintsPath, input.avmHints.toBuffer());
|
|
477
|
-
if (!await filePresent(avmHintsPath)) {
|
|
478
|
-
return {
|
|
479
|
-
status: 1,
|
|
480
|
-
reason: `Could not write avmHints at ${avmHintsPath}`
|
|
481
|
-
};
|
|
409
|
+
if (!(await filePresent(avmHintsPath))) {
|
|
410
|
+
return { status: BB_RESULT.FAILURE, reason: `Could not write avmHints at ${avmHintsPath}` };
|
|
482
411
|
}
|
|
483
412
|
const args = [
|
|
484
413
|
'--avm-public-inputs',
|
|
@@ -488,36 +417,34 @@ export async function executeBbClientIvcProof(pathToBB, workingDirectory, byteco
|
|
|
488
417
|
'-o',
|
|
489
418
|
outputPath,
|
|
490
419
|
logger.level === 'debug' || logger.level === 'trace' ? '-d' : logger.level === 'verbose' ? '-v' : '',
|
|
491
|
-
checkCircuitOnly ? '--check-circuit-only' : ''
|
|
420
|
+
checkCircuitOnly ? '--check-circuit-only' : '',
|
|
492
421
|
];
|
|
493
422
|
const timer = new Timer();
|
|
494
423
|
const cmd = checkCircuitOnly ? 'check_circuit' : 'prove';
|
|
495
|
-
const logFunction = (message)=>{
|
|
424
|
+
const logFunction = (message) => {
|
|
496
425
|
logger.verbose(`AvmCircuit (${cmd}) BB out - ${message}`);
|
|
497
426
|
};
|
|
498
427
|
const result = await executeBB(pathToBB, `avm_${cmd}`, args, logFunction);
|
|
499
428
|
const duration = timer.ms();
|
|
500
|
-
if (result.status ==
|
|
429
|
+
if (result.status == BB_RESULT.SUCCESS) {
|
|
501
430
|
return {
|
|
502
|
-
status:
|
|
431
|
+
status: BB_RESULT.SUCCESS,
|
|
503
432
|
durationMs: duration,
|
|
504
433
|
proofPath: join(outputPath, PROOF_FILENAME),
|
|
505
434
|
pkPath: undefined,
|
|
506
|
-
vkPath: outputPath
|
|
435
|
+
vkPath: outputPath,
|
|
507
436
|
};
|
|
508
437
|
}
|
|
509
438
|
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
510
439
|
return {
|
|
511
|
-
status:
|
|
440
|
+
status: BB_RESULT.FAILURE,
|
|
512
441
|
reason: `Failed to generate proof. Exit code ${result.exitCode}. Signal ${result.signal}.`,
|
|
513
|
-
retry: !!result.signal
|
|
514
|
-
};
|
|
515
|
-
} catch (error) {
|
|
516
|
-
return {
|
|
517
|
-
status: 1,
|
|
518
|
-
reason: `${error}`
|
|
442
|
+
retry: !!result.signal,
|
|
519
443
|
};
|
|
520
444
|
}
|
|
445
|
+
catch (error) {
|
|
446
|
+
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
447
|
+
}
|
|
521
448
|
}
|
|
522
449
|
/**
|
|
523
450
|
* Used for verifying proofs of noir circuits
|
|
@@ -526,7 +453,8 @@ export async function executeBbClientIvcProof(pathToBB, workingDirectory, byteco
|
|
|
526
453
|
* @param verificationKeyPath - The full path to the circuit verification key
|
|
527
454
|
* @param log - A logging function
|
|
528
455
|
* @returns An object containing a result indication and duration taken
|
|
529
|
-
*/
|
|
456
|
+
*/
|
|
457
|
+
export async function verifyProof(pathToBB, proofFullPath, verificationKeyPath, ultraHonkFlavor, log) {
|
|
530
458
|
return await verifyProofInternal(pathToBB, proofFullPath, verificationKeyPath, `verify_${ultraHonkFlavor}`, log);
|
|
531
459
|
}
|
|
532
460
|
/**
|
|
@@ -536,24 +464,25 @@ export async function executeBbClientIvcProof(pathToBB, workingDirectory, byteco
|
|
|
536
464
|
* @param verificationKeyPath - The full path to the circuit verification key
|
|
537
465
|
* @param log - A logging function
|
|
538
466
|
* @returns An object containing a result indication and duration taken
|
|
539
|
-
*/
|
|
467
|
+
*/
|
|
468
|
+
export async function verifyAvmProof(pathToBB, proofFullPath, verificationKeyPath, logger) {
|
|
540
469
|
return await verifyProofInternal(pathToBB, proofFullPath, verificationKeyPath, 'avm_verify', logger);
|
|
541
470
|
}
|
|
542
471
|
export async function verifyAvmProofV2(pathToBB, workingDirectory, proofFullPath, publicInputs, verificationKeyPath, logger) {
|
|
543
472
|
const inputsBuffer = serializeWithMessagePack(publicInputs);
|
|
544
473
|
// Write the inputs to the working directory.
|
|
545
|
-
const filePresent = async (file)=>await fs
|
|
474
|
+
const filePresent = async (file) => await fs
|
|
475
|
+
.access(file, fs.constants.R_OK)
|
|
476
|
+
.then(_ => true)
|
|
477
|
+
.catch(_ => false);
|
|
546
478
|
const avmInputsPath = join(workingDirectory, 'avm_public_inputs.bin');
|
|
547
479
|
await fs.writeFile(avmInputsPath, inputsBuffer);
|
|
548
|
-
if (!await filePresent(avmInputsPath)) {
|
|
549
|
-
return {
|
|
550
|
-
status: 1,
|
|
551
|
-
reason: `Could not write avm inputs to ${avmInputsPath}`
|
|
552
|
-
};
|
|
480
|
+
if (!(await filePresent(avmInputsPath))) {
|
|
481
|
+
return { status: BB_RESULT.FAILURE, reason: `Could not write avm inputs to ${avmInputsPath}` };
|
|
553
482
|
}
|
|
554
483
|
return await verifyProofInternal(pathToBB, proofFullPath, verificationKeyPath, 'avm2_verify', logger, [
|
|
555
484
|
'--avm-public-inputs',
|
|
556
|
-
avmInputsPath
|
|
485
|
+
avmInputsPath,
|
|
557
486
|
]);
|
|
558
487
|
}
|
|
559
488
|
/**
|
|
@@ -563,43 +492,34 @@ export async function verifyAvmProofV2(pathToBB, workingDirectory, proofFullPath
|
|
|
563
492
|
* @param targetPath - The path to the folder with the proof, accumulator, and verification keys
|
|
564
493
|
* @param log - A logging function
|
|
565
494
|
* @returns An object containing a result indication and duration taken
|
|
566
|
-
*/
|
|
567
|
-
|
|
495
|
+
*/
|
|
496
|
+
export async function verifyClientIvcProof(pathToBB, targetPath, log) {
|
|
497
|
+
const binaryPresent = await fs
|
|
498
|
+
.access(pathToBB, fs.constants.R_OK)
|
|
499
|
+
.then(_ => true)
|
|
500
|
+
.catch(_ => false);
|
|
568
501
|
if (!binaryPresent) {
|
|
569
|
-
return {
|
|
570
|
-
status: 1,
|
|
571
|
-
reason: `Failed to find bb binary at ${pathToBB}`
|
|
572
|
-
};
|
|
502
|
+
return { status: BB_RESULT.FAILURE, reason: `Failed to find bb binary at ${pathToBB}` };
|
|
573
503
|
}
|
|
574
504
|
try {
|
|
575
|
-
const args = [
|
|
576
|
-
'-o',
|
|
577
|
-
targetPath,
|
|
578
|
-
'--scheme',
|
|
579
|
-
'client_ivc'
|
|
580
|
-
];
|
|
505
|
+
const args = ['-o', targetPath, '--scheme', 'client_ivc'];
|
|
581
506
|
const timer = new Timer();
|
|
582
507
|
const command = 'verify';
|
|
583
508
|
const result = await executeBB(pathToBB, command, args, log);
|
|
584
509
|
const duration = timer.ms();
|
|
585
|
-
if (result.status ==
|
|
586
|
-
return {
|
|
587
|
-
status: 0,
|
|
588
|
-
durationMs: duration
|
|
589
|
-
};
|
|
510
|
+
if (result.status == BB_RESULT.SUCCESS) {
|
|
511
|
+
return { status: BB_RESULT.SUCCESS, durationMs: duration };
|
|
590
512
|
}
|
|
591
513
|
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
592
514
|
return {
|
|
593
|
-
status:
|
|
515
|
+
status: BB_RESULT.FAILURE,
|
|
594
516
|
reason: `Failed to verify proof. Exit code ${result.exitCode}. Signal ${result.signal}.`,
|
|
595
|
-
retry: !!result.signal
|
|
596
|
-
};
|
|
597
|
-
} catch (error) {
|
|
598
|
-
return {
|
|
599
|
-
status: 1,
|
|
600
|
-
reason: `${error}`
|
|
517
|
+
retry: !!result.signal,
|
|
601
518
|
};
|
|
602
519
|
}
|
|
520
|
+
catch (error) {
|
|
521
|
+
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
522
|
+
}
|
|
603
523
|
}
|
|
604
524
|
/**
|
|
605
525
|
* Used for verifying proofs with BB
|
|
@@ -609,15 +529,16 @@ export async function verifyAvmProofV2(pathToBB, workingDirectory, proofFullPath
|
|
|
609
529
|
* @param command - The BB command to execute (verify/avm_verify)
|
|
610
530
|
* @param log - A logging function
|
|
611
531
|
* @returns An object containing a result indication and duration taken
|
|
612
|
-
*/
|
|
613
|
-
|
|
532
|
+
*/
|
|
533
|
+
async function verifyProofInternal(pathToBB, proofFullPath, verificationKeyPath, command, logger, extraArgs = []) {
|
|
534
|
+
const binaryPresent = await fs
|
|
535
|
+
.access(pathToBB, fs.constants.R_OK)
|
|
536
|
+
.then(_ => true)
|
|
537
|
+
.catch(_ => false);
|
|
614
538
|
if (!binaryPresent) {
|
|
615
|
-
return {
|
|
616
|
-
status: 1,
|
|
617
|
-
reason: `Failed to find bb binary at ${pathToBB}`
|
|
618
|
-
};
|
|
539
|
+
return { status: BB_RESULT.FAILURE, reason: `Failed to find bb binary at ${pathToBB}` };
|
|
619
540
|
}
|
|
620
|
-
const logFunction = (message)=>{
|
|
541
|
+
const logFunction = (message) => {
|
|
621
542
|
logger.verbose(`AvmCircuit (verify) BB out - ${message}`);
|
|
622
543
|
};
|
|
623
544
|
try {
|
|
@@ -627,29 +548,24 @@ export async function verifyAvmProofV2(pathToBB, workingDirectory, proofFullPath
|
|
|
627
548
|
'-k',
|
|
628
549
|
verificationKeyPath,
|
|
629
550
|
logger.level === 'debug' || logger.level === 'trace' ? '-d' : logger.level === 'verbose' ? '-v' : '',
|
|
630
|
-
...extraArgs
|
|
551
|
+
...extraArgs,
|
|
631
552
|
];
|
|
632
553
|
const timer = new Timer();
|
|
633
554
|
const result = await executeBB(pathToBB, command, args, logFunction);
|
|
634
555
|
const duration = timer.ms();
|
|
635
|
-
if (result.status ==
|
|
636
|
-
return {
|
|
637
|
-
status: 0,
|
|
638
|
-
durationMs: duration
|
|
639
|
-
};
|
|
556
|
+
if (result.status == BB_RESULT.SUCCESS) {
|
|
557
|
+
return { status: BB_RESULT.SUCCESS, durationMs: duration };
|
|
640
558
|
}
|
|
641
559
|
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
642
560
|
return {
|
|
643
|
-
status:
|
|
561
|
+
status: BB_RESULT.FAILURE,
|
|
644
562
|
reason: `Failed to verify proof. Exit code ${result.exitCode}. Signal ${result.signal}.`,
|
|
645
|
-
retry: !!result.signal
|
|
646
|
-
};
|
|
647
|
-
} catch (error) {
|
|
648
|
-
return {
|
|
649
|
-
status: 1,
|
|
650
|
-
reason: `${error}`
|
|
563
|
+
retry: !!result.signal,
|
|
651
564
|
};
|
|
652
565
|
}
|
|
566
|
+
catch (error) {
|
|
567
|
+
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
568
|
+
}
|
|
653
569
|
}
|
|
654
570
|
/**
|
|
655
571
|
* Used for verifying proofs of noir circuits
|
|
@@ -658,42 +574,33 @@ export async function verifyAvmProofV2(pathToBB, workingDirectory, proofFullPath
|
|
|
658
574
|
* @param verificationKeyFilename - The filename of the verification key
|
|
659
575
|
* @param log - A logging function
|
|
660
576
|
* @returns An object containing a result indication and duration taken
|
|
661
|
-
*/
|
|
662
|
-
|
|
577
|
+
*/
|
|
578
|
+
export async function writeVkAsFields(pathToBB, verificationKeyPath, verificationKeyFilename, log) {
|
|
579
|
+
const binaryPresent = await fs
|
|
580
|
+
.access(pathToBB, fs.constants.R_OK)
|
|
581
|
+
.then(_ => true)
|
|
582
|
+
.catch(_ => false);
|
|
663
583
|
if (!binaryPresent) {
|
|
664
|
-
return {
|
|
665
|
-
status: 1,
|
|
666
|
-
reason: `Failed to find bb binary at ${pathToBB}`
|
|
667
|
-
};
|
|
584
|
+
return { status: BB_RESULT.FAILURE, reason: `Failed to find bb binary at ${pathToBB}` };
|
|
668
585
|
}
|
|
669
586
|
try {
|
|
670
|
-
const args = [
|
|
671
|
-
'-k',
|
|
672
|
-
`${verificationKeyPath}/${verificationKeyFilename}`,
|
|
673
|
-
'-v'
|
|
674
|
-
];
|
|
587
|
+
const args = ['-k', `${verificationKeyPath}/${verificationKeyFilename}`, '-v'];
|
|
675
588
|
const timer = new Timer();
|
|
676
589
|
const result = await executeBB(pathToBB, 'vk_as_fields_ultra_honk', args, log);
|
|
677
590
|
const duration = timer.ms();
|
|
678
|
-
if (result.status ==
|
|
679
|
-
return {
|
|
680
|
-
status: 0,
|
|
681
|
-
durationMs: duration,
|
|
682
|
-
vkPath: verificationKeyPath
|
|
683
|
-
};
|
|
591
|
+
if (result.status == BB_RESULT.SUCCESS) {
|
|
592
|
+
return { status: BB_RESULT.SUCCESS, durationMs: duration, vkPath: verificationKeyPath };
|
|
684
593
|
}
|
|
685
594
|
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
686
595
|
return {
|
|
687
|
-
status:
|
|
596
|
+
status: BB_RESULT.FAILURE,
|
|
688
597
|
reason: `Failed to create vk as fields. Exit code ${result.exitCode}. Signal ${result.signal}.`,
|
|
689
|
-
retry: !!result.signal
|
|
690
|
-
};
|
|
691
|
-
} catch (error) {
|
|
692
|
-
return {
|
|
693
|
-
status: 1,
|
|
694
|
-
reason: `${error}`
|
|
598
|
+
retry: !!result.signal,
|
|
695
599
|
};
|
|
696
600
|
}
|
|
601
|
+
catch (error) {
|
|
602
|
+
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
603
|
+
}
|
|
697
604
|
}
|
|
698
605
|
/**
|
|
699
606
|
* Used for verifying proofs of noir circuits
|
|
@@ -703,100 +610,72 @@ export async function verifyAvmProofV2(pathToBB, workingDirectory, proofFullPath
|
|
|
703
610
|
* @param vkFileName - The filename of the verification key
|
|
704
611
|
* @param log - A logging function
|
|
705
612
|
* @returns An object containing a result indication and duration taken
|
|
706
|
-
*/
|
|
707
|
-
|
|
613
|
+
*/
|
|
614
|
+
export async function writeProofAsFields(pathToBB, proofPath, proofFileName, vkFilePath, log) {
|
|
615
|
+
const binaryPresent = await fs
|
|
616
|
+
.access(pathToBB, fs.constants.R_OK)
|
|
617
|
+
.then(_ => true)
|
|
618
|
+
.catch(_ => false);
|
|
708
619
|
if (!binaryPresent) {
|
|
709
|
-
return {
|
|
710
|
-
status: 1,
|
|
711
|
-
reason: `Failed to find bb binary at ${pathToBB}`
|
|
712
|
-
};
|
|
620
|
+
return { status: BB_RESULT.FAILURE, reason: `Failed to find bb binary at ${pathToBB}` };
|
|
713
621
|
}
|
|
714
622
|
try {
|
|
715
|
-
const args = [
|
|
716
|
-
'-p',
|
|
717
|
-
`${proofPath}/${proofFileName}`,
|
|
718
|
-
'-k',
|
|
719
|
-
vkFilePath,
|
|
720
|
-
'-v'
|
|
721
|
-
];
|
|
623
|
+
const args = ['-p', `${proofPath}/${proofFileName}`, '-k', vkFilePath, '-v'];
|
|
722
624
|
const timer = new Timer();
|
|
723
625
|
const result = await executeBB(pathToBB, 'proof_as_fields_honk', args, log);
|
|
724
626
|
const duration = timer.ms();
|
|
725
|
-
if (result.status ==
|
|
726
|
-
return {
|
|
727
|
-
status: 0,
|
|
728
|
-
durationMs: duration,
|
|
729
|
-
proofPath: proofPath
|
|
730
|
-
};
|
|
627
|
+
if (result.status == BB_RESULT.SUCCESS) {
|
|
628
|
+
return { status: BB_RESULT.SUCCESS, durationMs: duration, proofPath: proofPath };
|
|
731
629
|
}
|
|
732
630
|
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
733
631
|
return {
|
|
734
|
-
status:
|
|
632
|
+
status: BB_RESULT.FAILURE,
|
|
735
633
|
reason: `Failed to create proof as fields. Exit code ${result.exitCode}. Signal ${result.signal}.`,
|
|
736
|
-
retry: !!result.signal
|
|
737
|
-
};
|
|
738
|
-
} catch (error) {
|
|
739
|
-
return {
|
|
740
|
-
status: 1,
|
|
741
|
-
reason: `${error}`
|
|
634
|
+
retry: !!result.signal,
|
|
742
635
|
};
|
|
743
636
|
}
|
|
637
|
+
catch (error) {
|
|
638
|
+
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
639
|
+
}
|
|
744
640
|
}
|
|
745
641
|
export async function generateContractForVerificationKey(pathToBB, vkFilePath, contractPath, log) {
|
|
746
|
-
const binaryPresent = await fs
|
|
642
|
+
const binaryPresent = await fs
|
|
643
|
+
.access(pathToBB, fs.constants.R_OK)
|
|
644
|
+
.then(_ => true)
|
|
645
|
+
.catch(_ => false);
|
|
747
646
|
if (!binaryPresent) {
|
|
748
|
-
return {
|
|
749
|
-
status: 1,
|
|
750
|
-
reason: `Failed to find bb binary at ${pathToBB}`
|
|
751
|
-
};
|
|
647
|
+
return { status: BB_RESULT.FAILURE, reason: `Failed to find bb binary at ${pathToBB}` };
|
|
752
648
|
}
|
|
753
649
|
const outputDir = dirname(contractPath);
|
|
754
650
|
const contractName = basename(contractPath);
|
|
755
651
|
// cache contract generation based on vk file and contract name
|
|
756
|
-
const cacheKey = sha256(Buffer.concat([
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
]));
|
|
760
|
-
await fs.mkdir(outputDir, {
|
|
761
|
-
recursive: true
|
|
762
|
-
});
|
|
763
|
-
const res = await fsCache(outputDir, cacheKey, log, false, async ()=>{
|
|
652
|
+
const cacheKey = sha256(Buffer.concat([Buffer.from(contractName), await fs.readFile(vkFilePath)]));
|
|
653
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
654
|
+
const res = await fsCache(outputDir, cacheKey, log, false, async () => {
|
|
764
655
|
try {
|
|
765
|
-
const args = [
|
|
766
|
-
'-k',
|
|
767
|
-
vkFilePath,
|
|
768
|
-
'-o',
|
|
769
|
-
contractPath,
|
|
770
|
-
'-v'
|
|
771
|
-
];
|
|
656
|
+
const args = ['-k', vkFilePath, '-o', contractPath, '-v'];
|
|
772
657
|
const timer = new Timer();
|
|
773
658
|
const result = await executeBB(pathToBB, 'contract_ultra_honk', args, log);
|
|
774
659
|
const duration = timer.ms();
|
|
775
|
-
if (result.status ==
|
|
776
|
-
return {
|
|
777
|
-
status: 0,
|
|
778
|
-
durationMs: duration,
|
|
779
|
-
contractPath
|
|
780
|
-
};
|
|
660
|
+
if (result.status == BB_RESULT.SUCCESS) {
|
|
661
|
+
return { status: BB_RESULT.SUCCESS, durationMs: duration, contractPath };
|
|
781
662
|
}
|
|
782
663
|
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
783
664
|
return {
|
|
784
|
-
status:
|
|
665
|
+
status: BB_RESULT.FAILURE,
|
|
785
666
|
reason: `Failed to write verifier contract. Exit code ${result.exitCode}. Signal ${result.signal}.`,
|
|
786
|
-
retry: !!result.signal
|
|
787
|
-
};
|
|
788
|
-
} catch (error) {
|
|
789
|
-
return {
|
|
790
|
-
status: 1,
|
|
791
|
-
reason: `${error}`
|
|
667
|
+
retry: !!result.signal,
|
|
792
668
|
};
|
|
793
669
|
}
|
|
670
|
+
catch (error) {
|
|
671
|
+
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
672
|
+
}
|
|
794
673
|
});
|
|
795
674
|
if (!res) {
|
|
796
675
|
return {
|
|
797
|
-
status:
|
|
676
|
+
status: BB_RESULT.ALREADY_PRESENT,
|
|
798
677
|
durationMs: 0,
|
|
799
|
-
contractPath
|
|
678
|
+
contractPath,
|
|
800
679
|
};
|
|
801
680
|
}
|
|
802
681
|
return res;
|
|
@@ -809,28 +688,27 @@ export async function generateContractForVerificationKey(pathToBB, vkFilePath, c
|
|
|
809
688
|
* @param bytecode - The bytecode of the circuit
|
|
810
689
|
* @param flavor - The flavor of the backend - mega_honk or ultra_honk variants
|
|
811
690
|
* @returns An object containing the status, gate count, and time taken
|
|
812
|
-
*/
|
|
691
|
+
*/
|
|
692
|
+
export async function computeGateCountForCircuit(pathToBB, workingDirectory, circuitName, bytecode, flavor, log) {
|
|
813
693
|
// Check that the working directory exists
|
|
814
694
|
try {
|
|
815
695
|
await fs.access(workingDirectory);
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
reason: `Working directory ${workingDirectory} does not exist`
|
|
820
|
-
};
|
|
696
|
+
}
|
|
697
|
+
catch (error) {
|
|
698
|
+
return { status: BB_RESULT.FAILURE, reason: `Working directory ${workingDirectory} does not exist` };
|
|
821
699
|
}
|
|
822
700
|
// The bytecode is written to e.g. /workingDirectory/BaseParityArtifact-bytecode
|
|
823
701
|
const bytecodePath = `${workingDirectory}/${circuitName}-bytecode`;
|
|
824
|
-
const binaryPresent = await fs
|
|
702
|
+
const binaryPresent = await fs
|
|
703
|
+
.access(pathToBB, fs.constants.R_OK)
|
|
704
|
+
.then(_ => true)
|
|
705
|
+
.catch(_ => false);
|
|
825
706
|
if (!binaryPresent) {
|
|
826
|
-
return {
|
|
827
|
-
status: 1,
|
|
828
|
-
reason: `Failed to find bb binary at ${pathToBB}`
|
|
829
|
-
};
|
|
707
|
+
return { status: BB_RESULT.FAILURE, reason: `Failed to find bb binary at ${pathToBB}` };
|
|
830
708
|
}
|
|
831
709
|
// Accumulate the stdout from bb
|
|
832
710
|
let stdout = '';
|
|
833
|
-
const logHandler = (message)=>{
|
|
711
|
+
const logHandler = (message) => {
|
|
834
712
|
stdout += message;
|
|
835
713
|
log(message);
|
|
836
714
|
};
|
|
@@ -838,37 +716,25 @@ export async function generateContractForVerificationKey(pathToBB, vkFilePath, c
|
|
|
838
716
|
// Write the bytecode to the working directory
|
|
839
717
|
await fs.writeFile(bytecodePath, bytecode);
|
|
840
718
|
const timer = new Timer();
|
|
841
|
-
const result = await executeBB(pathToBB, flavor === 'mega_honk' ? `gates_for_ivc` : `gates`, [
|
|
842
|
-
'-b',
|
|
843
|
-
bytecodePath,
|
|
844
|
-
'-v'
|
|
845
|
-
], logHandler);
|
|
719
|
+
const result = await executeBB(pathToBB, flavor === 'mega_honk' ? `gates_for_ivc` : `gates`, ['-b', bytecodePath, '-v'], logHandler);
|
|
846
720
|
const duration = timer.ms();
|
|
847
|
-
if (result.status ==
|
|
721
|
+
if (result.status == BB_RESULT.SUCCESS) {
|
|
848
722
|
// Look for "circuit_size" in the stdout and parse the number
|
|
849
723
|
const circuitSizeMatch = stdout.match(/circuit_size": (\d+)/);
|
|
850
724
|
if (!circuitSizeMatch) {
|
|
851
|
-
return {
|
|
852
|
-
status: 1,
|
|
853
|
-
reason: 'Failed to parse circuit_size from bb gates stdout.'
|
|
854
|
-
};
|
|
725
|
+
return { status: BB_RESULT.FAILURE, reason: 'Failed to parse circuit_size from bb gates stdout.' };
|
|
855
726
|
}
|
|
856
727
|
const circuitSize = parseInt(circuitSizeMatch[1]);
|
|
857
728
|
return {
|
|
858
|
-
status:
|
|
729
|
+
status: BB_RESULT.SUCCESS,
|
|
859
730
|
durationMs: duration,
|
|
860
|
-
circuitSize: circuitSize
|
|
731
|
+
circuitSize: circuitSize,
|
|
861
732
|
};
|
|
862
733
|
}
|
|
863
|
-
return {
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
};
|
|
867
|
-
} catch (error) {
|
|
868
|
-
return {
|
|
869
|
-
status: 1,
|
|
870
|
-
reason: `${error}`
|
|
871
|
-
};
|
|
734
|
+
return { status: BB_RESULT.FAILURE, reason: 'Failed getting the gate count.' };
|
|
735
|
+
}
|
|
736
|
+
catch (error) {
|
|
737
|
+
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
872
738
|
}
|
|
873
739
|
}
|
|
874
740
|
const CACHE_FILENAME = '.cache';
|
|
@@ -877,14 +743,17 @@ async function fsCache(dir, expectedCacheKey, logger, force, action) {
|
|
|
877
743
|
let run;
|
|
878
744
|
if (force) {
|
|
879
745
|
run = true;
|
|
880
|
-
}
|
|
746
|
+
}
|
|
747
|
+
else {
|
|
881
748
|
try {
|
|
882
749
|
run = !expectedCacheKey.equals(await fs.readFile(cacheFilePath));
|
|
883
|
-
}
|
|
750
|
+
}
|
|
751
|
+
catch (err) {
|
|
884
752
|
if (err && 'code' in err && err.code === 'ENOENT') {
|
|
885
753
|
// cache file doesn't exist, swallow error and run
|
|
886
754
|
run = true;
|
|
887
|
-
}
|
|
755
|
+
}
|
|
756
|
+
else {
|
|
888
757
|
throw err;
|
|
889
758
|
}
|
|
890
759
|
}
|
|
@@ -893,14 +762,17 @@ async function fsCache(dir, expectedCacheKey, logger, force, action) {
|
|
|
893
762
|
if (run) {
|
|
894
763
|
logger(`Cache miss or forced run. Running operation in ${dir}...`);
|
|
895
764
|
res = await action();
|
|
896
|
-
}
|
|
765
|
+
}
|
|
766
|
+
else {
|
|
897
767
|
logger(`Cache hit. Skipping operation in ${dir}...`);
|
|
898
768
|
}
|
|
899
769
|
try {
|
|
900
770
|
await fs.writeFile(cacheFilePath, expectedCacheKey);
|
|
901
|
-
}
|
|
771
|
+
}
|
|
772
|
+
catch (err) {
|
|
902
773
|
logger(`Couldn't write cache data to ${cacheFilePath}. Skipping cache...`);
|
|
903
|
-
|
|
774
|
+
// ignore
|
|
904
775
|
}
|
|
905
776
|
return res;
|
|
906
777
|
}
|
|
778
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXhlY3V0ZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9iYi9leGVjdXRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBeUIsd0JBQXdCLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUNyRixPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFFbEQsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBRWhELE9BQU8sS0FBSyxJQUFJLE1BQU0sZUFBZSxDQUFDO0FBQ3RDLE9BQU8sRUFBRSxRQUFRLElBQUksRUFBRSxFQUFFLE1BQU0sSUFBSSxDQUFDO0FBQ3BDLE9BQU8sRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUcvQyxPQUFPLEVBQUUsMEJBQTBCLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSxxQ0FBcUMsQ0FBQztBQUUxRyxNQUFNLENBQUMsTUFBTSxXQUFXLEdBQUcsSUFBSSxDQUFDO0FBQ2hDLE1BQU0sQ0FBQyxNQUFNLGtCQUFrQixHQUFHLGdCQUFnQixDQUFDO0FBQ25ELE1BQU0sQ0FBQyxNQUFNLGNBQWMsR0FBRyxPQUFPLENBQUM7QUFDdEMsTUFBTSxDQUFDLE1BQU0scUJBQXFCLEdBQUcsbUJBQW1CLENBQUM7QUFDekQsTUFBTSxDQUFDLE1BQU0scUJBQXFCLEdBQUcsa0JBQWtCLENBQUM7QUFDeEQsTUFBTSxDQUFDLE1BQU0sMEJBQTBCLEdBQUcsdUJBQXVCLENBQUM7QUFDbEUsTUFBTSxDQUFDLE1BQU0sa0JBQWtCLEdBQUcsZUFBZSxDQUFDO0FBRWxELE1BQU0sQ0FBTixJQUFZLFNBSVg7QUFKRCxXQUFZLFNBQVM7SUFDbkIsK0NBQU8sQ0FBQTtJQUNQLCtDQUFPLENBQUE7SUFDUCwrREFBZSxDQUFBO0FBQ2pCLENBQUMsRUFKVyxTQUFTLEtBQVQsU0FBUyxRQUlwQjtBQWlDRDs7Ozs7Ozs7R0FRRztBQUNILE1BQU0sVUFBVSxTQUFTLENBQ3ZCLFFBQWdCLEVBQ2hCLE9BQWUsRUFDZixJQUFjLEVBQ2QsTUFBYSxFQUNiLGVBQWUsQ0FBQyxJQUFZLEVBQUUsRUFBRSxDQUFDLElBQUksS0FBSyxDQUFDO0lBRTNDLE9BQU8sSUFBSSxPQUFPLENBQWUsT0FBTyxDQUFDLEVBQUU7UUFDekMsdUJBQXVCO1FBQ3ZCLE1BQU0sRUFBRSxvQkFBb0IsRUFBRSxDQUFDLEVBQUUsR0FBRyxxQkFBcUIsRUFBRSxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUM7UUFDMUUsTUFBTSxHQUFHLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMscUJBQXFCLENBQUM7UUFDbkYsTUFBTSxDQUFDLHNCQUFzQixRQUFRLElBQUksT0FBTyxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQ3RFLE1BQU0sRUFBRSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsUUFBUSxFQUFFLENBQUMsT0FBTyxFQUFFLEdBQUcsSUFBSSxDQUFDLEVBQUU7WUFDbEQsR0FBRztTQUNKLENBQUMsQ0FBQztRQUNILEVBQUUsQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDLE1BQU0sRUFBRSxJQUFJLENBQUMsRUFBRTtZQUMxQixNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxDQUFDLE9BQU8sQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLENBQUM7WUFDMUQsTUFBTSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQ2xCLENBQUMsQ0FBQyxDQUFDO1FBQ0gsRUFBRSxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQyxFQUFFO1lBQzFCLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLENBQUMsT0FBTyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsQ0FBQztZQUMxRCxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDbEIsQ0FBQyxDQUFDLENBQUM7UUFDSCxFQUFFLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxDQUFDLFFBQWdCLEVBQUUsTUFBZSxFQUFFLEVBQUU7WUFDbkQsSUFBSSxZQUFZLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQztnQkFDM0IsT0FBTyxDQUFDLEVBQUUsTUFBTSxFQUFFLFNBQVMsQ0FBQyxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sRUFBRSxDQUFDLENBQUM7WUFDM0QsQ0FBQztpQkFBTSxDQUFDO2dCQUNOLE9BQU8sQ0FBQyxFQUFFLE1BQU0sRUFBRSxTQUFTLENBQUMsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLEVBQUUsQ0FBQyxDQUFDO1lBQzNELENBQUM7UUFDSCxDQUFDLENBQUMsQ0FBQztJQUNMLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsRUFBRSxNQUFNLEVBQUUsU0FBUyxDQUFDLE9BQU8sRUFBRSxRQUFRLEVBQUUsQ0FBQyxDQUFDLEVBQUUsTUFBTSxFQUFFLFNBQVMsRUFBRSxDQUFDLENBQUMsQ0FBQztBQUNsRixDQUFDO0FBRUQsK0ZBQStGO0FBQy9GLE1BQU0sQ0FBQyxLQUFLLFVBQVUsdUJBQXVCLENBQzNDLFFBQWdCLEVBQ2hCLGdCQUF3QixFQUN4QixpQkFBeUIsRUFDekIsZ0JBQXdCLEVBQ3hCLEdBQVU7SUFFViwwQ0FBMEM7SUFDMUMsSUFBSSxDQUFDO1FBQ0gsTUFBTSxFQUFFLENBQUMsTUFBTSxDQUFDLGdCQUFnQixDQUFDLENBQUM7SUFDcEMsQ0FBQztJQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7UUFDZixPQUFPLEVBQUUsTUFBTSxFQUFFLFNBQVMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLHFCQUFxQixnQkFBZ0IsaUJBQWlCLEVBQUUsQ0FBQztJQUN2RyxDQUFDO0lBRUQsdURBQXVEO0lBQ3ZELE1BQU0sVUFBVSxHQUFHLEdBQUcsZ0JBQWdCLEVBQUUsQ0FBQztJQUV6QyxNQUFNLGFBQWEsR0FBRyxNQUFNLEVBQUU7U0FDM0IsTUFBTSxDQUFDLFFBQVEsRUFBRSxFQUFFLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQztTQUNuQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUM7U0FDZixLQUFLLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUNyQixJQUFJLENBQUMsYUFBYSxFQUFFLENBQUM7UUFDbkIsT0FBTyxFQUFFLE1BQU0sRUFBRSxTQUFTLENBQUMsT0FBTyxFQUFFLE1BQU0sRUFBRSwrQkFBK0IsUUFBUSxFQUFFLEVBQUUsQ0FBQztJQUMxRixDQUFDO0lBRUQsSUFBSSxDQUFDO1FBQ0gsOENBQThDO1FBQzlDLEdBQUcsQ0FBQyxnQkFBZ0IsaUJBQWlCLEVBQUUsQ0FBQyxDQUFDO1FBQ3pDLEdBQUcsQ0FBQyxjQUFjLFVBQVUsRUFBRSxDQUFDLENBQUM7UUFDaEMsTUFBTSxJQUFJLEdBQUc7WUFDWCxJQUFJO1lBQ0osVUFBVTtZQUNWLElBQUk7WUFDSixpQkFBaUI7WUFDakIsSUFBSTtZQUNKLGdCQUFnQjtZQUNoQixJQUFJO1lBQ0osVUFBVTtZQUNWLFlBQVk7WUFDWixjQUFjO1lBQ2QsZUFBZTtTQUNoQixDQUFDO1FBRUYsTUFBTSxLQUFLLEdBQUcsSUFBSSxLQUFLLEVBQUUsQ0FBQztRQUMxQixNQUFNLFdBQVcsR0FBRyxDQUFDLE9BQWUsRUFBRSxFQUFFO1lBQ3RDLEdBQUcsQ0FBQyxRQUFRLE9BQU8sRUFBRSxDQUFDLENBQUM7UUFDekIsQ0FBQyxDQUFDO1FBRUYsTUFBTSxNQUFNLEdBQUcsTUFBTSxTQUFTLENBQUMsUUFBUSxFQUFFLE9BQU8sRUFBRSxJQUFJLEVBQUUsV0FBVyxDQUFDLENBQUM7UUFDckUsTUFBTSxVQUFVLEdBQUcsS0FBSyxDQUFDLEVBQUUsRUFBRSxDQUFDO1FBRTlCLElBQUksTUFBTSxDQUFDLE1BQU0sSUFBSSxTQUFTLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDdkMsT0FBTztnQkFDTCxNQUFNLEVBQUUsU0FBUyxDQUFDLE9BQU87Z0JBQ3pCLFVBQVU7Z0JBQ1YsU0FBUyxFQUFFLEdBQUcsVUFBVSxFQUFFO2dCQUMxQixNQUFNLEVBQUUsU0FBUztnQkFDakIsTUFBTSxFQUFFLEdBQUcsVUFBVSxFQUFFO2FBQ3hCLENBQUM7UUFDSixDQUFDO1FBQ0Qsb0ZBQW9GO1FBQ3BGLE9BQU87WUFDTCxNQUFNLEVBQUUsU0FBUyxDQUFDLE9BQU87WUFDekIsTUFBTSxFQUFFLHVDQUF1QyxNQUFNLENBQUMsUUFBUSxZQUFZLE1BQU0sQ0FBQyxNQUFNLEdBQUc7WUFDMUYsS0FBSyxFQUFFLENBQUMsQ0FBQyxNQUFNLENBQUMsTUFBTTtTQUN2QixDQUFDO0lBQ0osQ0FBQztJQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7UUFDZixPQUFPLEVBQUUsTUFBTSxFQUFFLFNBQVMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLEdBQUcsS0FBSyxFQUFFLEVBQUUsQ0FBQztJQUMzRCxDQUFDO0FBQ0gsQ0FBQztBQUVEOzs7Ozs7Ozs7O0dBVUc7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLHNCQUFzQixDQUMxQyxRQUFnQixFQUNoQixnQkFBd0IsRUFDeEIsV0FBbUIsRUFDbkIsUUFBZ0IsRUFDaEIsU0FBa0IsRUFDbEIsTUFBcUMsRUFDckMsR0FBVTtJQUVWLDBDQUEwQztJQUMxQyxJQUFJLENBQUM7UUFDSCxNQUFNLEVBQUUsQ0FBQyxNQUFNLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztJQUNwQyxDQUFDO0lBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztRQUNmLE9BQU8sRUFBRSxNQUFNLEVBQUUsU0FBUyxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUscUJBQXFCLGdCQUFnQixpQkFBaUIsRUFBRSxDQUFDO0lBQ3ZHLENBQUM7SUFFRCxnRkFBZ0Y7SUFDaEYsTUFBTSxZQUFZLEdBQUcsR0FBRyxnQkFBZ0IsSUFBSSxXQUFXLFdBQVcsQ0FBQztJQUVuRSwrQ0FBK0M7SUFDL0MsTUFBTSxVQUFVLEdBQUcsR0FBRyxnQkFBZ0IsS0FBSyxDQUFDO0lBRTVDLE1BQU0sYUFBYSxHQUFHLE1BQU0sRUFBRTtTQUMzQixNQUFNLENBQUMsUUFBUSxFQUFFLEVBQUUsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDO1NBQ25DLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQztTQUNmLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQ3JCLElBQUksQ0FBQyxhQUFhLEVBQUUsQ0FBQztRQUNuQixPQUFPLEVBQUUsTUFBTSxFQUFFLFNBQVMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLCtCQUErQixRQUFRLEVBQUUsRUFBRSxDQUFDO0lBQzFGLENBQUM7SUFFRCxJQUFJLENBQUM7UUFDSCw4Q0FBOEM7UUFDOUMsTUFBTSxFQUFFLENBQUMsU0FBUyxDQUFDLFlBQVksRUFBRSxRQUFRLENBQUMsQ0FBQztRQUMzQyxNQUFNLEtBQUssR0FBRyxJQUFJLEtBQUssRUFBRSxDQUFDO1FBQzFCLE1BQU0sV0FBVyxHQUFHLENBQUMsT0FBZSxFQUFFLEVBQUU7WUFDdEMsR0FBRyxDQUFDLDBCQUEwQixXQUFXLGNBQWMsT0FBTyxFQUFFLENBQUMsQ0FBQztRQUNwRSxDQUFDLENBQUM7UUFDRixNQUFNLElBQUksR0FBRyxDQUFDLElBQUksRUFBRSxVQUFVLEVBQUUsSUFBSSxFQUFFLFlBQVksRUFBRSxJQUFJLEVBQUUsU0FBUyxDQUFDLENBQUMsQ0FBQyxhQUFhLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQzFGLElBQUksTUFBTSxHQUFHLE1BQU0sU0FBUyxDQUFDLFFBQVEsRUFBRSxZQUFZLE1BQU0sRUFBRSxFQUFFLElBQUksRUFBRSxXQUFXLENBQUMsQ0FBQztRQUNoRixJQUFJLE1BQU0sQ0FBQyxNQUFNLElBQUksU0FBUyxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQ3ZDLE9BQU8sRUFBRSxNQUFNLEVBQUUsU0FBUyxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsb0JBQW9CLEVBQUUsQ0FBQztRQUNyRSxDQUFDO1FBQ0QsTUFBTSxHQUFHLE1BQU0sU0FBUyxDQUN0QixRQUFRLEVBQ1IsZ0JBQWdCLE1BQU0sRUFBRSxFQUN4QixDQUFDLElBQUksRUFBRSxVQUFVLEdBQUcsY0FBYyxFQUFFLElBQUksRUFBRSxVQUFVLEVBQUUsSUFBSSxDQUFDLEVBQzNELFdBQVcsQ0FDWixDQUFDO1FBQ0YsTUFBTSxRQUFRLEdBQUcsS0FBSyxDQUFDLEVBQUUsRUFBRSxDQUFDO1FBRTVCLElBQUksTUFBTSxDQUFDLE1BQU0sSUFBSSxTQUFTLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDdkMsT0FBTztnQkFDTCxNQUFNLEVBQUUsU0FBUyxDQUFDLE9BQU87Z0JBQ3pCLFVBQVUsRUFBRSxRQUFRO2dCQUNwQixNQUFNLEVBQUUsU0FBUztnQkFDakIsTUFBTSxFQUFFLEdBQUcsVUFBVSxFQUFFO2FBQ3hCLENBQUM7UUFDSixDQUFDO1FBQ0Qsb0ZBQW9GO1FBQ3BGLE9BQU87WUFDTCxNQUFNLEVBQUUsU0FBUyxDQUFDLE9BQU87WUFDekIsTUFBTSxFQUFFLGlDQUFpQyxNQUFNLENBQUMsUUFBUSxZQUFZLE1BQU0sQ0FBQyxNQUFNLEdBQUc7WUFDcEYsS0FBSyxFQUFFLENBQUMsQ0FBQyxNQUFNLENBQUMsTUFBTTtTQUN2QixDQUFDO0lBQ0osQ0FBQztJQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7UUFDZixPQUFPLEVBQUUsTUFBTSxFQUFFLFNBQVMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLEdBQUcsS0FBSyxFQUFFLEVBQUUsQ0FBQztJQUMzRCxDQUFDO0FBQ0gsQ0FBQztBQUVEOzs7Ozs7Ozs7O0dBVUc7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLGFBQWEsQ0FDakMsUUFBZ0IsRUFDaEIsZ0JBQXdCLEVBQ3hCLFdBQW1CLEVBQ25CLFFBQWdCLEVBQ2hCLFNBQWtCLEVBQ2xCLGdCQUF3QixFQUN4QixNQUF1QixFQUN2QixHQUFVO0lBRVYsMENBQTBDO0lBQzFDLElBQUksQ0FBQztRQUNILE1BQU0sRUFBRSxDQUFDLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO0lBQ3BDLENBQUM7SUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1FBQ2YsT0FBTyxFQUFFLE1BQU0sRUFBRSxTQUFTLENBQUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxxQkFBcUIsZ0JBQWdCLGlCQUFpQixFQUFFLENBQUM7SUFDdkcsQ0FBQztJQUVELGdGQUFnRjtJQUNoRixNQUFNLFlBQVksR0FBRyxHQUFHLGdCQUFnQixJQUFJLFdBQVcsV0FBVyxDQUFDO0lBRW5FLGtFQUFrRTtJQUNsRSxNQUFNLFVBQVUsR0FBRyxHQUFHLGdCQUFnQixFQUFFLENBQUM7SUFFekMsTUFBTSxhQUFhLEdBQUcsTUFBTSxFQUFFO1NBQzNCLE1BQU0sQ0FBQyxRQUFRLEVBQUUsRUFBRSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUM7U0FDbkMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDO1NBQ2YsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDckIsSUFBSSxDQUFDLGFBQWEsRUFBRSxDQUFDO1FBQ25CLE9BQU8sRUFBRSxNQUFNLEVBQUUsU0FBUyxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsK0JBQStCLFFBQVEsRUFBRSxFQUFFLENBQUM7SUFDMUYsQ0FBQztJQUVELElBQUksQ0FBQztRQUNILDhDQUE4QztRQUM5QyxNQUFNLEVBQUUsQ0FBQyxTQUFTLENBQUMsWUFBWSxFQUFFLFFBQVEsQ0FBQyxDQUFDO1FBQzNDLE1BQU0sSUFBSSxHQUFHLENBQUMsSUFBSSxFQUFFLFVBQVUsRUFBRSxJQUFJLEVBQUUsWUFBWSxFQUFFLElBQUksRUFBRSxnQkFBZ0IsRUFBRSxJQUFJLEVBQUUsU0FBUyxDQUFDLENBQUMsQ0FBQyxhQUFhLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQ2xILE1BQU0sS0FBSyxHQUFHLElBQUksS0FBSyxFQUFFLENBQUM7UUFDMUIsTUFBTSxXQUFXLEdBQUcsQ0FBQyxPQUFlLEVBQUUsRUFBRTtZQUN0QyxHQUFHLENBQUMsR0FBRyxXQUFXLGFBQWEsT0FBTyxFQUFFLENBQUMsQ0FBQztRQUM1QyxDQUFDLENBQUM7UUFDRixNQUFNLE1BQU0sR0FBRyxNQUFNLFNBQVMsQ0FBQyxRQUFRLEVBQUUsU0FBUyxNQUFNLGFBQWEsRUFBRSxJQUFJLEVBQUUsV0FBVyxDQUFDLENBQUM7UUFDMUYsTUFBTSxRQUFRLEdBQUcsS0FBSyxDQUFDLEVBQUUsRUFBRSxDQUFDO1FBRTVCLElBQUksTUFBTSxDQUFDLE1BQU0sSUFBSSxTQUFTLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDdkMsT0FBTztnQkFDTCxNQUFNLEVBQUUsU0FBUyxDQUFDLE9BQU87Z0JBQ3pCLFVBQVUsRUFBRSxRQUFRO2dCQUNwQixTQUFTLEVBQUUsR0FBRyxVQUFVLEVBQUU7Z0JBQzFCLE1BQU0sRUFBRSxTQUFTO2dCQUNqQixNQUFNLEVBQUUsR0FBRyxVQUFVLEVBQUU7YUFDeEIsQ0FBQztRQUNKLENBQUM7UUFDRCxvRkFBb0Y7UUFDcEYsT0FBTztZQUNMLE1BQU0sRUFBRSxTQUFTLENBQUMsT0FBTztZQUN6QixNQUFNLEVBQUUsdUNBQXVDLE1BQU0sQ0FBQyxRQUFRLFlBQVksTUFBTSxDQUFDLE1BQU0sR0FBRztZQUMxRixLQUFLLEVBQUUsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxNQUFNO1NBQ3ZCLENBQUM7SUFDSixDQUFDO0lBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztRQUNmLE9BQU8sRUFBRSxNQUFNLEVBQUUsU0FBUyxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsR0FBRyxLQUFLLEVBQUUsRUFBRSxDQUFDO0lBQzNELENBQUM7QUFDSCxDQUFDO0FBRUQ7Ozs7Ozs7Ozs7R0FVRztBQUNILE1BQU0sQ0FBQyxLQUFLLFVBQVUsaUJBQWlCLENBQ3JDLFFBQWdCLEVBQ2hCLGdCQUF3QixFQUN4QixHQUFVO0lBRVYsMENBQTBDO0lBQzFDLElBQUksQ0FBQztRQUNILE1BQU0sRUFBRSxDQUFDLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO0lBQ3BDLENBQUM7SUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1FBQ2YsT0FBTyxFQUFFLE1BQU0sRUFBRSxTQUFTLENBQUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxxQkFBcUIsZ0JBQWdCLGlCQUFpQixFQUFFLENBQUM7SUFDdkcsQ0FBQztJQUVELDBCQUEwQjtJQUMxQixNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsZ0JBQWdCLEVBQUUsdUJBQXVCLENBQUMsQ0FBQztJQUMvRCxNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsZ0JBQWdCLEVBQUUsMEJBQTBCLENBQUMsQ0FBQztJQUVyRSx1REFBdUQ7SUFDdkQsTUFBTSxVQUFVLEdBQUcsZ0JBQWdCLENBQUM7SUFDcEMsTUFBTSxXQUFXLEdBQUcsS0FBSyxFQUFFLElBQVksRUFBRSxFQUFFLENBQ3pDLE1BQU0sRUFBRTtTQUNMLE1BQU0sQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUM7U0FDL0IsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDO1NBQ2YsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsS0FBSyxDQUFDLENBQUM7SUFFdkIsTUFBTSxhQUFhLEdBQUcsTUFBTSxXQUFXLENBQUMsUUFBUSxDQUFDLENBQUM7SUFDbEQsSUFBSSxDQUFDLGFBQWEsRUFBRSxDQUFDO1FBQ25CLE9BQU8sRUFBRSxNQUFNLEVBQUUsU0FBUyxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsK0JBQStCLFFBQVEsRUFBRSxFQUFFLENBQUM7SUFDMUYsQ0FBQztJQUVELElBQUksQ0FBQztRQUNILElBQUksQ0FBQyxDQUFDLE1BQU0sV0FBVyxDQUFDLE1BQU0sQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLE1BQU0sV0FBVyxDQUFDLFNBQVMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztZQUNwRSxPQUFPLEVBQUUsTUFBTSxFQUFFLFNBQVMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLDBDQUEwQyxnQkFBZ0IsRUFBRSxFQUFFLENBQUM7UUFDN0csQ0FBQztRQUNELE1BQU0sSUFBSSxHQUFHLENBQUMsSUFBSSxFQUFFLFVBQVUsRUFBRSxJQUFJLENBQUMsQ0FBQztRQUV0QyxNQUFNLEtBQUssR0FBRyxJQUFJLEtBQUssRUFBRSxDQUFDO1FBQzFCLE1BQU0sV0FBVyxHQUFHLENBQUMsT0FBZSxFQUFFLEVBQUU7WUFDdEMsR0FBRyxDQUFDLGdDQUFnQyxPQUFPLEVBQUUsQ0FBQyxDQUFDO1FBQ2pELENBQUMsQ0FBQztRQUNGLE1BQU0sTUFBTSxHQUFHLE1BQU0sU0FBUyxDQUFDLFFBQVEsRUFBRSxZQUFZLEVBQUUsSUFBSSxFQUFFLFdBQVcsQ0FBQyxDQUFDO1FBQzFFLE1BQU0sVUFBVSxHQUFHLEtBQUssQ0FBQyxFQUFFLEVBQUUsQ0FBQztRQUU5QixJQUFJLE1BQU0sQ0FBQyxNQUFNLElBQUksU0FBUyxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQ3ZDLE9BQU87Z0JBQ0wsTUFBTSxFQUFFLFNBQVMsQ0FBQyxPQUFPO2dCQUN6QixVQUFVO2dCQUNWLFNBQVMsRUFBRSxVQUFVO2dCQUNyQixNQUFNLEVBQUUsU0FBUztnQkFDakIsTUFBTSxFQUFFLFVBQVU7YUFDbkIsQ0FBQztRQUNKLENBQUM7UUFDRCxvRkFBb0Y7UUFDcEYsT0FBTztZQUNMLE1BQU0sRUFBRSxTQUFTLENBQUMsT0FBTztZQUN6QixNQUFNLEVBQUUsdUNBQXVDLE1BQU0sQ0FBQyxRQUFRLFlBQVksTUFBTSxDQUFDLE1BQU0sR0FBRztZQUMxRixLQUFLLEVBQUUsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxNQUFNO1NBQ3ZCLENBQUM7SUFDSixDQUFDO0lBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztRQUNmLE9BQU8sRUFBRSxNQUFNLEVBQUUsU0FBUyxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsR0FBRyxLQUFLLEVBQUUsRUFBRSxDQUFDO0lBQzNELENBQUM7QUFDSCxDQUFDO0FBRUQ7Ozs7Ozs7O0dBUUc7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLGtCQUFrQixDQUN0QyxRQUFnQixFQUNoQixnQkFBd0IsRUFDeEIsS0FBdUIsRUFDdkIsTUFBYztJQUVkLDBDQUEwQztJQUMxQyxJQUFJLENBQUM7UUFDSCxNQUFNLEVBQUUsQ0FBQyxNQUFNLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztJQUNwQyxDQUFDO0lBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztRQUNmLE9BQU8sRUFBRSxNQUFNLEVBQUUsU0FBUyxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUscUJBQXFCLGdCQUFnQixpQkFBaUIsRUFBRSxDQUFDO0lBQ3ZHLENBQUM7SUFFRCx1REFBdUQ7SUFDdkQsTUFBTSxVQUFVLEdBQUcsZ0JBQWdCLENBQUM7SUFFcEMsTUFBTSxXQUFXLEdBQUcsS0FBSyxFQUFFLElBQVksRUFBRSxFQUFFLENBQ3pDLE1BQU0sRUFBRTtTQUNMLE1BQU0sQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUM7U0FDL0IsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDO1NBQ2YsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsS0FBSyxDQUFDLENBQUM7SUFFdkIsTUFBTSxhQUFhLEdBQUcsTUFBTSxXQUFXLENBQUMsUUFBUSxDQUFDLENBQUM7SUFDbEQsSUFBSSxDQUFDLGFBQWEsRUFBRSxDQUFDO1FBQ25CLE9BQU8sRUFBRSxNQUFNLEVBQUUsU0FBUyxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsK0JBQStCLFFBQVEsRUFBRSxFQUFFLENBQUM7SUFDMUYsQ0FBQztJQUVELE1BQU0sWUFBWSxHQUFHLEtBQUssQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDO0lBRTlDLElBQUksQ0FBQztRQUNILDZDQUE2QztRQUM3QyxNQUFNLGFBQWEsR0FBRyxJQUFJLENBQUMsZ0JBQWdCLEVBQUUsZ0JBQWdCLENBQUMsQ0FBQztRQUMvRCxNQUFNLEVBQUUsQ0FBQyxTQUFTLENBQUMsYUFBYSxFQUFFLFlBQVksQ0FBQyxDQUFDO1FBQ2hELElBQUksQ0FBQyxDQUFDLE1BQU0sV0FBVyxDQUFDLGFBQWEsQ0FBQyxDQUFDLEVBQUUsQ0FBQztZQUN4QyxPQUFPLEVBQUUsTUFBTSxFQUFFLFNBQVMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLGlDQUFpQyxhQUFhLEVBQUUsRUFBRSxDQUFDO1FBQ2pHLENBQUM7UUFFRCxNQUFNLElBQUksR0FBRztZQUNYLGNBQWM7WUFDZCxhQUFhO1lBQ2IsSUFBSTtZQUNKLFVBQVU7WUFDVixNQUFNLENBQUMsS0FBSyxLQUFLLE9BQU8sSUFBSSxNQUFNLENBQUMsS0FBSyxLQUFLLE9BQU8sQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsS0FBSyxLQUFLLFNBQVMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFO1NBQ3JHLENBQUM7UUFDRixNQUFNLEtBQUssR0FBRyxJQUFJLEtBQUssRUFBRSxDQUFDO1FBQzFCLE1BQU0sV0FBVyxHQUFHLENBQUMsT0FBZSxFQUFFLEVBQUU7WUFDdEMsTUFBTSxDQUFDLE9BQU8sQ0FBQywrQkFBK0IsT0FBTyxFQUFFLENBQUMsQ0FBQztRQUMzRCxDQUFDLENBQUM7UUFDRixNQUFNLE1BQU0sR0FBRyxNQUFNLFNBQVMsQ0FBQyxRQUFRLEVBQUUsWUFBWSxFQUFFLElBQUksRUFBRSxXQUFXLENBQUMsQ0FBQztRQUMxRSxNQUFNLFFBQVEsR0FBRyxLQUFLLENBQUMsRUFBRSxFQUFFLENBQUM7UUFFNUIsSUFBSSxNQUFNLENBQUMsTUFBTSxJQUFJLFNBQVMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUN2QyxPQUFPO2dCQUNMLE1BQU0sRUFBRSxTQUFTLENBQUMsT0FBTztnQkFDekIsVUFBVSxFQUFFLFFBQVE7Z0JBQ3BCLFNBQVMsRUFBRSxJQUFJLENBQUMsVUFBVSxFQUFFLGNBQWMsQ0FBQztnQkFDM0MsTUFBTSxFQUFFLFNBQVM7Z0JBQ2pCLE1BQU0sRUFBRSxVQUFVO2FBQ25CLENBQUM7UUFDSixDQUFDO1FBQ0Qsb0ZBQW9GO1FBQ3BGLE9BQU87WUFDTCxNQUFNLEVBQUUsU0FBUyxDQUFDLE9BQU87WUFDekIsTUFBTSxFQUFFLHVDQUF1QyxNQUFNLENBQUMsUUFBUSxZQUFZLE1BQU0sQ0FBQyxNQUFNLEdBQUc7WUFDMUYsS0FBSyxFQUFFLENBQUMsQ0FBQyxNQUFNLENBQUMsTUFBTTtTQUN2QixDQUFDO0lBQ0osQ0FBQztJQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7UUFDZixPQUFPLEVBQUUsTUFBTSxFQUFFLFNBQVMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLEdBQUcsS0FBSyxFQUFFLEVBQUUsQ0FBQztJQUMzRCxDQUFDO0FBQ0gsQ0FBQztBQUVEOzs7Ozs7OztHQVFHO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSxnQkFBZ0IsQ0FDcEMsUUFBZ0IsRUFDaEIsZ0JBQXdCLEVBQ3hCLEtBQXVCLEVBQ3ZCLE1BQWMsRUFDZCxtQkFBNEIsS0FBSztJQUVqQywwQ0FBMEM7SUFDMUMsSUFBSSxDQUFDO1FBQ0gsTUFBTSxFQUFFLENBQUMsTUFBTSxDQUFDLGdCQUFnQixDQUFDLENBQUM7SUFDcEMsQ0FBQztJQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7UUFDZixPQUFPLEVBQUUsTUFBTSxFQUFFLFNBQVMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLHFCQUFxQixnQkFBZ0IsaUJBQWlCLEVBQUUsQ0FBQztJQUN2RyxDQUFDO0lBRUQsdUJBQXVCO0lBQ3ZCLE1BQU0sZ0JBQWdCLEdBQUcsSUFBSSxDQUFDLGdCQUFnQixFQUFFLDBCQUEwQixDQUFDLENBQUM7SUFDNUUsTUFBTSxZQUFZLEdBQUcsSUFBSSxDQUFDLGdCQUFnQixFQUFFLGtCQUFrQixDQUFDLENBQUM7SUFFaEUsdURBQXVEO0lBQ3ZELE1BQU0sVUFBVSxHQUFHLGdCQUFnQixDQUFDO0lBRXBDLE1BQU0sV0FBVyxHQUFHLEtBQUssRUFBRSxJQUFZLEVBQUUsRUFBRSxDQUN6QyxNQUFNLEVBQUU7U0FDTCxNQUFNLENBQUMsSUFBSSxFQUFFLEVBQUUsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDO1NBQy9CLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQztTQUNmLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBRXZCLE1BQU0sYUFBYSxHQUFHLE1BQU0sV0FBVyxDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQ2xELElBQUksQ0FBQyxhQUFhLEVBQUUsQ0FBQztRQUNuQixPQUFPLEVBQUUsTUFBTSxFQUFFLFNBQVMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLCtCQUErQixRQUFRLEVBQUUsRUFBRSxDQUFDO0lBQzFGLENBQUM7SUFFRCxJQUFJLENBQUM7UUFDSCw2Q0FBNkM7UUFFN0MsTUFBTSxFQUFFLENBQUMsU0FBUyxDQUFDLGdCQUFnQixFQUFFLEtBQUssQ0FBQyxZQUFZLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FBQztRQUNwRSxJQUFJLENBQUMsQ0FBQyxNQUFNLFdBQVcsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDLEVBQUUsQ0FBQztZQUMzQyxPQUFPLEVBQUUsTUFBTSxFQUFFLFNBQVMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLG1DQUFtQyxnQkFBZ0IsRUFBRSxFQUFFLENBQUM7UUFDdEcsQ0FBQztRQUVELE1BQU0sRUFBRSxDQUFDLFNBQVMsQ0FBQyxZQUFZLEVBQUUsS0FBSyxDQUFDLFFBQVEsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxDQUFDO1FBQzVELElBQUksQ0FBQyxDQUFDLE1BQU0sV0FBVyxDQUFDLFlBQVksQ0FBQyxDQUFDLEVBQUUsQ0FBQztZQUN2QyxPQUFPLEVBQUUsTUFBTSxFQUFFLFNBQVMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLCtCQUErQixZQUFZLEVBQUUsRUFBRSxDQUFDO1FBQzlGLENBQUM7UUFFRCxNQUFNLElBQUksR0FBRztZQUNYLHFCQUFxQjtZQUNyQixnQkFBZ0I7WUFDaEIsYUFBYTtZQUNiLFlBQVk7WUFDWixJQUFJO1lBQ0osVUFBVTtZQUNWLE1BQU0sQ0FBQyxLQUFLLEtBQUssT0FBTyxJQUFJLE1BQU0sQ0FBQyxLQUFLLEtBQUssT0FBTyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxLQUFLLEtBQUssU0FBUyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUU7WUFDcEcsZ0JBQWdCLENBQUMsQ0FBQyxDQUFDLHNCQUFzQixDQUFDLENBQUMsQ0FBQyxFQUFFO1NBQy9DLENBQUM7UUFDRixNQUFNLEtBQUssR0FBRyxJQUFJLEtBQUssRUFBRSxDQUFDO1FBQzFCLE1BQU0sR0FBRyxHQUFHLGdCQUFnQixDQUFDLENBQUMsQ0FBQyxlQUFlLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQztRQUN6RCxNQUFNLFdBQVcsR0FBRyxDQUFDLE9BQWUsRUFBRSxFQUFFO1lBQ3RDLE1BQU0sQ0FBQyxPQUFPLENBQUMsZUFBZSxHQUFHLGNBQWMsT0FBTyxFQUFFLENBQUMsQ0FBQztRQUM1RCxDQUFDLENBQUM7UUFDRixNQUFNLE1BQU0sR0FBRyxNQUFNLFNBQVMsQ0FBQyxRQUFRLEVBQUUsT0FBTyxHQUFHLEVBQUUsRUFBRSxJQUFJLEVBQUUsV0FBVyxDQUFDLENBQUM7UUFDMUUsTUFBTSxRQUFRLEdBQUcsS0FBSyxDQUFDLEVBQUUsRUFBRSxDQUFDO1FBRTVCLElBQUksTUFBTSxDQUFDLE1BQU0sSUFBSSxTQUFTLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDdkMsT0FBTztnQkFDTCxNQUFNLEVBQUUsU0FBUyxDQUFDLE9BQU87Z0JBQ3pCLFVBQVUsRUFBRSxRQUFRO2dCQUNwQixTQUFTLEVBQUUsSUFBSSxDQUFDLFVBQVUsRUFBRSxjQUFjLENBQUM7Z0JBQzNDLE1BQU0sRUFBRSxTQUFTO2dCQUNqQixNQUFNLEVBQUUsVUFBVTthQUNuQixDQUFDO1FBQ0osQ0FBQztRQUNELG9GQUFvRjtRQUNwRixPQUFPO1lBQ0wsTUFBTSxFQUFFLFNBQVMsQ0FBQyxPQUFPO1lBQ3pCLE1BQU0sRUFBRSx1Q0FBdUMsTUFBTSxDQUFDLFFBQVEsWUFBWSxNQUFNLENBQUMsTUFBTSxHQUFHO1lBQzFGLEtBQUssRUFBRSxDQUFDLENBQUMsTUFBTSxDQUFDLE1BQU07U0FDdkIsQ0FBQztJQUNKLENBQUM7SUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1FBQ2YsT0FBTyxFQUFFLE1BQU0sRUFBRSxTQUFTLENBQUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxHQUFHLEtBQUssRUFBRSxFQUFFLENBQUM7SUFDM0QsQ0FBQztBQUNILENBQUM7QUFFRDs7Ozs7OztHQU9HO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSxXQUFXLENBQy9CLFFBQWdCLEVBQ2hCLGFBQXFCLEVBQ3JCLG1CQUEyQixFQUMzQixlQUFnQyxFQUNoQyxHQUFXO0lBRVgsT0FBTyxNQUFNLG1CQUFtQixDQUFDLFFBQVEsRUFBRSxhQUFhLEVBQUUsbUJBQW1CLEVBQUUsVUFBVSxlQUFlLEVBQUUsRUFBRSxHQUFHLENBQUMsQ0FBQztBQUNuSCxDQUFDO0FBRUQ7Ozs7Ozs7R0FPRztBQUNILE1BQU0sQ0FBQyxLQUFLLFVBQVUsY0FBYyxDQUNsQyxRQUFnQixFQUNoQixhQUFxQixFQUNyQixtQkFBMkIsRUFDM0IsTUFBYztJQUVkLE9BQU8sTUFBTSxtQkFBbUIsQ0FBQyxRQUFRLEVBQUUsYUFBYSxFQUFFLG1CQUFtQixFQUFFLFlBQVksRUFBRSxNQUFNLENBQUMsQ0FBQztBQUN2RyxDQUFDO0FBRUQsTUFBTSxDQUFDLEtBQUssVUFBVSxnQkFBZ0IsQ0FDcEMsUUFBZ0IsRUFDaEIsZ0JBQXdCLEVBQ3hCLGFBQXFCLEVBQ3JCLFlBQWlCLEVBQ2pCLG1CQUEyQixFQUMzQixNQUFjO0lBRWQsTUFBTSxZQUFZLEdBQUcsd0JBQXdCLENBQUMsWUFBWSxDQUFDLENBQUM7SUFFNUQsNkNBQTZDO0lBQzdDLE1BQU0sV0FBVyxHQUFHLEtBQUssRUFBRSxJQUFZLEVBQUUsRUFBRSxDQUN6QyxNQUFNLEVBQUU7U0FDTCxNQUFNLENBQUMsSUFBSSxFQUFFLEVBQUUsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDO1NBQy9CLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQztTQUNmLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQ3ZCLE1BQU0sYUFBYSxHQUFHLElBQUksQ0FBQyxnQkFBZ0IsRUFBRSx1QkFBdUIsQ0FBQyxDQUFDO0lBQ3RFLE1BQU0sRUFBRSxDQUFDLFNBQVMsQ0FBQyxhQUFhLEVBQUUsWUFBWSxDQUFDLENBQUM7SUFDaEQsSUFBSSxDQUFDLENBQUMsTUFBTSxXQUFXLENBQUMsYUFBYSxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQ3hDLE9BQU8sRUFBRSxNQUFNLEVBQUUsU0FBUyxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsaUNBQWlDLGFBQWEsRUFBRSxFQUFFLENBQUM7SUFDakcsQ0FBQztJQUVELE9BQU8sTUFBTSxtQkFBbUIsQ0FBQyxRQUFRLEVBQUUsYUFBYSxFQUFFLG1CQUFtQixFQUFFLGFBQWEsRUFBRSxNQUFNLEVBQUU7UUFDcEcscUJBQXFCO1FBQ3JCLGFBQWE7S0FDZCxDQUFDLENBQUM7QUFDTCxDQUFDO0FBRUQ7Ozs7Ozs7R0FPRztBQUNILE1BQU0sQ0FBQyxLQUFLLFVBQVUsb0JBQW9CLENBQ3hDLFFBQWdCLEVBQ2hCLFVBQWtCLEVBQ2xCLEdBQVU7SUFFVixNQUFNLGFBQWEsR0FBRyxNQUFNLEVBQUU7U0FDM0IsTUFBTSxDQUFDLFFBQVEsRUFBRSxFQUFFLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQztTQUNuQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUM7U0FDZixLQUFLLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUNyQixJQUFJLENBQUMsYUFBYSxFQUFFLENBQUM7UUFDbkIsT0FBTyxFQUFFLE1BQU0sRUFBRSxTQUFTLENBQUMsT0FBTyxFQUFFLE1BQU0sRUFBRSwrQkFBK0IsUUFBUSxFQUFFLEVBQUUsQ0FBQztJQUMxRixDQUFDO0lBRUQsSUFBSSxDQUFDO1FBQ0gsTUFBTSxJQUFJLEdBQUcsQ0FBQyxJQUFJLEVBQUUsVUFBVSxFQUFFLFVBQVUsRUFBRSxZQUFZLENBQUMsQ0FBQztRQUMxRCxNQUFNLEtBQUssR0FBRyxJQUFJLEtBQUssRUFBRSxDQUFDO1FBQzFCLE1BQU0sT0FBTyxHQUFHLFFBQVEsQ0FBQztRQUN6QixNQUFNLE1BQU0sR0FBRyxNQUFNLFNBQVMsQ0FBQyxRQUFRLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxHQUFHLENBQUMsQ0FBQztRQUM3RCxNQUFNLFFBQVEsR0FBRyxLQUFLLENBQUMsRUFBRSxFQUFFLENBQUM7UUFDNUIsSUFBSSxNQUFNLENBQUMsTUFBTSxJQUFJLFNBQVMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUN2QyxPQUFPLEVBQUUsTUFBTSxFQUFFLFNBQVMsQ0FBQyxPQUFPLEVBQUUsVUFBVSxFQUFFLFFBQVEsRUFBRSxDQUFDO1FBQzdELENBQUM7UUFDRCxvRkFBb0Y7UUFDcEYsT0FBTztZQUNMLE1BQU0sRUFBRSxTQUFTLENBQUMsT0FBTztZQUN6QixNQUFNLEVBQUUscUNBQXFDLE1BQU0sQ0FBQyxRQUFRLFlBQVksTUFBTSxDQUFDLE1BQU0sR0FBRztZQUN4RixLQUFLLEVBQUUsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxNQUFNO1NBQ3ZCLENBQUM7SUFDSixDQUFDO0lBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztRQUNmLE9BQU8sRUFBRSxNQUFNLEVBQUUsU0FBUyxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsR0FBRyxLQUFLLEVBQUUsRUFBRSxDQUFDO0lBQzNELENBQUM7QUFDSCxDQUFDO0FBRUQ7Ozs7Ozs7O0dBUUc7QUFDSCxLQUFLLFVBQVUsbUJBQW1CLENBQ2hDLFFBQWdCLEVBQ2hCLGFBQXFCLEVBQ3JCLG1CQUEyQixFQUMzQixPQUFxSCxFQUNySCxNQUFjLEVBQ2QsWUFBc0IsRUFBRTtJQUV4QixNQUFNLGFBQWEsR0FBRyxNQUFNLEVBQUU7U0FDM0IsTUFBTSxDQUFDLFFBQVEsRUFBRSxFQUFFLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQztTQUNuQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUM7U0FDZixLQUFLLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUNyQixJQUFJLENBQUMsYUFBYSxFQUFFLENBQUM7UUFDbkIsT0FBTyxFQUFFLE1BQU0sRUFBRSxTQUFTLENBQUMsT0FBTyxFQUFFLE1BQU0sRUFBRSwrQkFBK0IsUUFBUSxFQUFFLEVBQUUsQ0FBQztJQUMxRixDQUFDO0lBRUQsTUFBTSxXQUFXLEdBQUcsQ0FBQyxPQUFlLEVBQUUsRUFBRTtRQUN0QyxNQUFNLENBQUMsT0FBTyxDQUFDLGdDQUFnQyxPQUFPLEVBQUUsQ0FBQyxDQUFDO0lBQzVELENBQUMsQ0FBQztJQUVGLElBQUksQ0FBQztRQUNILE1BQU0sSUFBSSxHQUFHO1lBQ1gsSUFBSTtZQUNKLGFBQWE7WUFDYixJQUFJO1lBQ0osbUJBQW1CO1lBQ25CLE1BQU0sQ0FBQyxLQUFLLEtBQUssT0FBTyxJQUFJLE1BQU0sQ0FBQyxLQUFLLEtBQUssT0FBTyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxLQUFLLEtBQUssU0FBUyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUU7WUFDcEcsR0FBRyxTQUFTO1NBQ2IsQ0FBQztRQUNGLE1BQU0sS0FBSyxHQUFHLElBQUksS0FBSyxFQUFFLENBQUM7UUFDMUIsTUFBTSxNQUFNLEdBQUcsTUFBTSxTQUFTLENBQUMsUUFBUSxFQUFFLE9BQU8sRUFBRSxJQUFJLEVBQUUsV0FBVyxDQUFDLENBQUM7UUFDckUsTUFBTSxRQUFRLEdBQUcsS0FBSyxDQUFDLEVBQUUsRUFBRSxDQUFDO1FBQzVCLElBQUksTUFBTSxDQUFDLE1BQU0sSUFBSSxTQUFTLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDdkMsT0FBTyxFQUFFLE1BQU0sRUFBRSxTQUFTLENBQUMsT0FBTyxFQUFFLFVBQVUsRUFBRSxRQUFRLEVBQUUsQ0FBQztRQUM3RCxDQUFDO1FBQ0Qsb0ZBQW9GO1FBQ3BGLE9BQU87WUFDTCxNQUFNLEVBQUUsU0FBUyxDQUFDLE9BQU87WUFDekIsTUFBTSxFQUFFLHFDQUFxQyxNQUFNLENBQUMsUUFBUSxZQUFZLE1BQU0sQ0FBQyxNQUFNLEdBQUc7WUFDeEYsS0FBSyxFQUFFLENBQUMsQ0FBQyxNQUFNLENBQUMsTUFBTTtTQUN2QixDQUFDO0lBQ0osQ0FBQztJQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7UUFDZixPQUFPLEVBQUUsTUFBTSxFQUFFLFNBQVMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLEdBQUcsS0FBSyxFQUFFLEVBQUUsQ0FBQztJQUMzRCxDQUFDO0FBQ0gsQ0FBQztBQUVEOzs7Ozs7O0dBT0c7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLGVBQWUsQ0FDbkMsUUFBZ0IsRUFDaEIsbUJBQTJCLEVBQzNCLHVCQUErQixFQUMvQixHQUFVO0lBRVYsTUFBTSxhQUFhLEdBQUcsTUFBTSxFQUFFO1NBQzNCLE1BQU0sQ0FBQyxRQUFRLEVBQUUsRUFBRSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUM7U0FDbkMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDO1NBQ2YsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDckIsSUFBSSxDQUFDLGFBQWEsRUFBRSxDQUFDO1FBQ25CLE9BQU8sRUFBRSxNQUFNLEVBQUUsU0FBUyxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsK0JBQStCLFFBQVEsRUFBRSxFQUFFLENBQUM7SUFDMUYsQ0FBQztJQUVELElBQUksQ0FBQztRQUNILE1BQU0sSUFBSSxHQUFHLENBQUMsSUFBSSxFQUFFLEdBQUcsbUJBQW1CLElBQUksdUJBQXVCLEVBQUUsRUFBRSxJQUFJLENBQUMsQ0FBQztRQUMvRSxNQUFNLEtBQUssR0FBRyxJQUFJLEtBQUssRUFBRSxDQUFDO1FBQzFCLE1BQU0sTUFBTSxHQUFHLE1BQU0sU0FBUyxDQUFDLFFBQVEsRUFBRSx5QkFBeUIsRUFBRSxJQUFJLEVBQUUsR0FBRyxDQUFDLENBQUM7UUFDL0UsTUFBTSxRQUFRLEdBQUcsS0FBSyxDQUFDLEVBQUUsRUFBRSxDQUFDO1FBQzVCLElBQUksTUFBTSxDQUFDLE1BQU0sSUFBSSxTQUFTLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDdkMsT0FBTyxFQUFFLE1BQU0sRUFBRSxTQUFTLENBQUMsT0FBTyxFQUFFLFVBQVUsRUFBRSxRQUFRLEVBQUUsTUFBTSxFQUFFLG1CQUFtQixFQUFFLENBQUM7UUFDMUYsQ0FBQztRQUNELG9GQUFvRjtRQUNwRixPQUFPO1lBQ0wsTUFBTSxFQUFFLFNBQVMsQ0FBQyxPQUFPO1lBQ3pCLE1BQU0sRUFBRSw0Q0FBNEMsTUFBTSxDQUFDLFFBQVEsWUFBWSxNQUFNLENBQUMsTUFBTSxHQUFHO1lBQy9GLEtBQUssRUFBRSxDQUFDLENBQUMsTUFBTSxDQUFDLE1BQU07U0FDdkIsQ0FBQztJQUNKLENBQUM7SUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1FBQ2YsT0FBTyxFQUFFLE1BQU0sRUFBRSxTQUFTLENBQUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxHQUFHLEtBQUssRUFBRSxFQUFFLENBQUM7SUFDM0QsQ0FBQztBQUNILENBQUM7QUFFRDs7Ozs7Ozs7R0FRRztBQUNILE1BQU0sQ0FBQyxLQUFLLFVBQVUsa0JBQWtCLENBQ3RDLFFBQWdCLEVBQ2hCLFNBQWlCLEVBQ2pCLGFBQXFCLEVBQ3JCLFVBQWtCLEVBQ2xCLEdBQVU7SUFFVixNQUFNLGFBQWEsR0FBRyxNQUFNLEVBQUU7U0FDM0IsTUFBTSxDQUFDLFFBQVEsRUFBRSxFQUFFLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQztTQUNuQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUM7U0FDZixLQUFLLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUNyQixJQUFJLENBQUMsYUFBYSxFQUFFLENBQUM7UUFDbkIsT0FBTyxFQUFFLE1BQU0sRUFBRSxTQUFTLENBQUMsT0FBTyxFQUFFLE1BQU0sRUFBRSwrQkFBK0IsUUFBUSxFQUFFLEVBQUUsQ0FBQztJQUMxRixDQUFDO0lBRUQsSUFBSSxDQUFDO1FBQ0gsTUFBTSxJQUFJLEdBQUcsQ0FBQyxJQUFJLEVBQUUsR0FBRyxTQUFTLElBQUksYUFBYSxFQUFFLEVBQUUsSUFBSSxFQUFFLFVBQVUsRUFBRSxJQUFJLENBQUMsQ0FBQztRQUM3RSxNQUFNLEtBQUssR0FBRyxJQUFJLEtBQUssRUFBRSxDQUFDO1FBQzFCLE1BQU0sTUFBTSxHQUFHLE1BQU0sU0FBUyxDQUFDLFFBQVEsRUFBRSxzQkFBc0IsRUFBRSxJQUFJLEVBQUUsR0FBRyxDQUFDLENBQUM7UUFDNUUsTUFBTSxRQUFRLEdBQUcsS0FBSyxDQUFDLEVBQUUsRUFBRSxDQUFDO1FBQzVCLElBQUksTUFBTSxDQUFDLE1BQU0sSUFBSSxTQUFTLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDdkMsT0FBTyxFQUFFLE1BQU0sRUFBRSxTQUFTLENBQUMsT0FBTyxFQUFFLFVBQVUsRUFBRSxRQUFRLEVBQUUsU0FBUyxFQUFFLFNBQVMsRUFBRSxDQUFDO1FBQ25GLENBQUM7UUFDRCxvRkFBb0Y7UUFDcEYsT0FBTztZQUNMLE1BQU0sRUFBRSxTQUFTLENBQUMsT0FBTztZQUN6QixNQUFNLEVBQUUsK0NBQStDLE1BQU0sQ0FBQyxRQUFRLFlBQVksTUFBTSxDQUFDLE1BQU0sR0FBRztZQUNsRyxLQUFLLEVBQUUsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxNQUFNO1NBQ3ZCLENBQUM7SUFDSixDQUFDO0lBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztRQUNmLE9BQU8sRUFBRSxNQUFNLEVBQUUsU0FBUyxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsR0FBRyxLQUFLLEVBQUUsRUFBRSxDQUFDO0lBQzNELENBQUM7QUFDSCxDQUFDO0FBRUQsTUFBTSxDQUFDLEtBQUssVUFBVSxrQ0FBa0MsQ0FDdEQsUUFBZ0IsRUFDaEIsVUFBa0IsRUFDbEIsWUFBb0IsRUFDcEIsR0FBVTtJQUVWLE1BQU0sYUFBYSxHQUFHLE1BQU0sRUFBRTtTQUMzQixNQUFNLENBQUMsUUFBUSxFQUFFLEVBQUUsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDO1NBQ25DLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQztTQUNmLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBRXJCLElBQUksQ0FBQyxhQUFhLEVBQUUsQ0FBQztRQUNuQixPQUFPLEVBQUUsTUFBTSxFQUFFLFNBQVMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLCtCQUErQixRQUFRLEVBQUUsRUFBRSxDQUFDO0lBQzFGLENBQUM7SUFFRCxNQUFNLFNBQVMsR0FBRyxPQUFPLENBQUMsWUFBWSxDQUFDLENBQUM7SUFDeEMsTUFBTSxZQUFZLEdBQUcsUUFBUSxDQUFDLFlBQVksQ0FBQyxDQUFDO0lBQzVDLCtEQUErRDtJQUMvRCxNQUFNLFFBQVEsR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsWUFBWSxDQUFDLEVBQUUsTUFBTSxFQUFFLENBQUMsUUFBUSxDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBRW5HLE1BQU0sRUFBRSxDQUFDLEtBQUssQ0FBQyxTQUFTLEVBQUUsRUFBRSxTQUFTLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztJQUUvQyxNQUFNLEdBQUcsR0FBRyxNQUFNLE9BQU8sQ0FBd0IsU0FBUyxFQUFFLFFBQVEsRUFBRSxHQUFHLEVBQUUsS0FBSyxFQUFFLEtBQUssSUFBSSxFQUFFO1FBQzNGLElBQUksQ0FBQztZQUNILE1BQU0sSUFBSSxHQUFHLENBQUMsSUFBSSxFQUFFLFVBQVUsRUFBRSxJQUFJLEVBQUUsWUFBWSxFQUFFLElBQUksQ0FBQyxDQUFDO1lBQzFELE1BQU0sS0FBSyxHQUFHLElBQUksS0FBSyxFQUFFLENBQUM7WUFDMUIsTUFBTSxNQUFNLEdBQUcsTUFBTSxTQUFTLENBQUMsUUFBUSxFQUFFLHFCQUFxQixFQUFFLElBQUksRUFBRSxHQUFHLENBQUMsQ0FBQztZQUMzRSxNQUFNLFFBQVEsR0FBRyxLQUFLLENBQUMsRUFBRSxFQUFFLENBQUM7WUFDNUIsSUFBSSxNQUFNLENBQUMsTUFBTSxJQUFJLFNBQVMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztnQkFDdkMsT0FBTyxFQUFFLE1BQU0sRUFBRSxTQUFTLENBQUMsT0FBTyxFQUFFLFVBQVUsRUFBRSxRQUFRLEVBQUUsWUFBWSxFQUFFLENBQUM7WUFDM0UsQ0FBQztZQUNELG9GQUFvRjtZQUNwRixPQUFPO2dCQUNMLE1BQU0sRUFBRSxTQUFTLENBQUMsT0FBTztnQkFDekIsTUFBTSxFQUFFLGdEQUFnRCxNQUFNLENBQUMsUUFBUSxZQUFZLE1BQU0sQ0FBQyxNQUFNLEdBQUc7Z0JBQ25HLEtBQUssRUFBRSxDQUFDLENBQUMsTUFBTSxDQUFDLE1BQU07YUFDdkIsQ0FBQztRQUNKLENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2YsT0FBTyxFQUFFLE1BQU0sRUFBRSxTQUFTLENBQUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxHQUFHLEtBQUssRUFBRSxFQUFFLENBQUM7UUFDM0QsQ0FBQztJQUNILENBQUMsQ0FBQyxDQUFDO0lBRUgsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO1FBQ1QsT0FBTztZQUNMLE1BQU0sRUFBRSxTQUFTLENBQUMsZUFBZTtZQUNqQyxVQUFVLEVBQUUsQ0FBQztZQUNiLFlBQVk7U0FDYixDQUFDO0lBQ0osQ0FBQztJQUVELE9BQU8sR0FBRyxDQUFDO0FBQ2IsQ0FBQztBQUVEOzs7Ozs7OztHQVFHO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSwwQkFBMEIsQ0FDOUMsUUFBZ0IsRUFDaEIsZ0JBQXdCLEVBQ3hCLFdBQW1CLEVBQ25CLFFBQWdCLEVBQ2hCLE1BQXFDLEVBQ3JDLEdBQVU7SUFFViwwQ0FBMEM7SUFDMUMsSUFBSSxDQUFDO1FBQ0gsTUFBTSxFQUFFLENBQUMsTUFBTSxDQUFDLGdCQUFnQixDQUFDLENBQUM7SUFDcEMsQ0FBQztJQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7UUFDZixPQUFPLEVBQUUsTUFBTSxFQUFFLFNBQVMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLHFCQUFxQixnQkFBZ0IsaUJBQWlCLEVBQUUsQ0FBQztJQUN2RyxDQUFDO0lBRUQsZ0ZBQWdGO0lBQ2hGLE1BQU0sWUFBWSxHQUFHLEdBQUcsZ0JBQWdCLElBQUksV0FBVyxXQUFXLENBQUM7SUFFbkUsTUFBTSxhQUFhLEdBQUcsTUFBTSxFQUFFO1NBQzNCLE1BQU0sQ0FBQyxRQUFRLEVBQUUsRUFBRSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUM7U0FDbkMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDO1NBQ2YsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDckIsSUFBSSxDQUFDLGFBQWEsRUFBRSxDQUFDO1FBQ25CLE9BQU8sRUFBRSxNQUFNLEVBQUUsU0FBUyxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsK0JBQStCLFFBQVEsRUFBRSxFQUFFLENBQUM7SUFDMUYsQ0FBQztJQUVELGdDQUFnQztJQUNoQyxJQUFJLE1BQU0sR0FBRyxFQUFFLENBQUM7SUFDaEIsTUFBTSxVQUFVLEdBQUcsQ0FBQyxPQUFlLEVBQUUsRUFBRTtRQUNyQyxNQUFNLElBQUksT0FBTyxDQUFDO1FBQ2xCLEdBQUcsQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUNmLENBQUMsQ0FBQztJQUVGLElBQUksQ0FBQztRQUNILDhDQUE4QztRQUM5QyxNQUFNLEVBQUUsQ0FBQyxTQUFTLENBQUMsWUFBWSxFQUFFLFFBQVEsQ0FBQyxDQUFDO1FBQzNDLE1BQU0sS0FBSyxHQUFHLElBQUksS0FBSyxFQUFFLENBQUM7UUFFMUIsTUFBTSxNQUFNLEdBQUcsTUFBTSxTQUFTLENBQzVCLFFBQVEsRUFDUixNQUFNLEtBQUssV0FBVyxDQUFDLENBQUMsQ0FBQyxlQUFlLENBQUMsQ0FBQyxDQUFDLE9BQU8sRUFDbEQsQ0FBQyxJQUFJLEVBQUUsWUFBWSxFQUFFLElBQUksQ0FBQyxFQUMxQixVQUFVLENBQ1gsQ0FBQztRQUNGLE1BQU0sUUFBUSxHQUFHLEtBQUssQ0FBQyxFQUFFLEVBQUUsQ0FBQztRQUU1QixJQUFJLE1BQU0sQ0FBQyxNQUFNLElBQUksU0FBUyxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQ3ZDLDZEQUE2RDtZQUM3RCxNQUFNLGdCQUFnQixHQUFHLE1BQU0sQ0FBQyxLQUFLLENBQUMsc0JBQXNCLENBQUMsQ0FBQztZQUM5RCxJQUFJLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQztnQkFDdEIsT0FBTyxFQUFFLE1BQU0sRUFBRSxTQUFTLENBQUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxvREFBb0QsRUFBRSxDQUFDO1lBQ3JHLENBQUM7WUFDRCxNQUFNLFdBQVcsR0FBRyxRQUFRLENBQUMsZ0JBQWdCLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUVsRCxPQUFPO2dCQUNMLE1BQU0sRUFBRSxTQUFTLENBQUMsT0FBTztnQkFDekIsVUFBVSxFQUFFLFFBQVE7Z0JBQ3BCLFdBQVcsRUFBRSxXQUFXO2FBQ3pCLENBQUM7UUFDSixDQUFDO1FBRUQsT0FBTyxFQUFFLE1BQU0sRUFBRSxTQUFTLENBQUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxnQ0FBZ0MsRUFBRSxDQUFDO0lBQ2pGLENBQUM7SUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1FBQ2YsT0FBTyxFQUFFLE1BQU0sRUFBRSxTQUFTLENBQUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxHQUFHLEtBQUssRUFBRSxFQUFFLENBQUM7SUFDM0QsQ0FBQztBQUNILENBQUM7QUFFRCxNQUFNLGNBQWMsR0FBRyxRQUFRLENBQUM7QUFDaEMsS0FBSyxVQUFVLE9BQU8sQ0FDcEIsR0FBVyxFQUNYLGdCQUF3QixFQUN4QixNQUFhLEVBQ2IsS0FBYyxFQUNkLE1BQXdCO0lBRXhCLE1BQU0sYUFBYSxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsY0FBYyxDQUFDLENBQUM7SUFFaEQsSUFBSSxHQUFZLENBQUM7SUFDakIsSUFBSSxLQUFLLEVBQUUsQ0FBQztRQUNWLEdBQUcsR0FBRyxJQUFJLENBQUM7SUFDYixDQUFDO1NBQU0sQ0FBQztRQUNOLElBQUksQ0FBQztZQUNILEdBQUcsR0FBRyxDQUFDLGdCQUFnQixDQUFDLE1BQU0sQ0FBQyxNQUFNLEVBQUUsQ0FBQyxRQUFRLENBQUMsYUFBYSxDQUFDLENBQUMsQ0FBQztRQUNuRSxDQUFDO1FBQUMsT0FBTyxHQUFRLEVBQUUsQ0FBQztZQUNsQixJQUFJLEdBQUcsSUFBSSxNQUFNLElBQUksR0FBRyxJQUFJLEdBQUcsQ0FBQyxJQUFJLEtBQUssUUFBUSxFQUFFLENBQUM7Z0JBQ2xELGtEQUFrRDtnQkFDbEQsR0FBRyxHQUFHLElBQUksQ0FBQztZQUNiLENBQUM7aUJBQU0sQ0FBQztnQkFDTixNQUFNLEdBQUcsQ0FBQztZQUNaLENBQUM7UUFDSCxDQUFDO0lBQ0gsQ0FBQztJQUVELElBQUksR0FBa0IsQ0FBQztJQUN2QixJQUFJLEdBQUcsRUFBRSxDQUFDO1FBQ1IsTUFBTSxDQUFDLGtEQUFrRCxHQUFHLEtBQUssQ0FBQyxDQUFDO1FBQ25FLEdBQUcsR0FBRyxNQUFNLE1BQU0sRUFBRSxDQUFDO0lBQ3ZCLENBQUM7U0FBTSxDQUFDO1FBQ04sTUFBTSxDQUFDLG9DQUFvQyxHQUFHLEtBQUssQ0FBQyxDQUFDO0lBQ3ZELENBQUM7SUFFRCxJQUFJLENBQUM7UUFDSCxNQUFNLEVBQUUsQ0FBQyxTQUFTLENBQUMsYUFBYSxFQUFFLGdCQUFnQixDQUFDLENBQUM7SUFDdEQsQ0FBQztJQUFDLE9BQU8sR0FBRyxFQUFFLENBQUM7UUFDYixNQUFNLENBQUMsZ0NBQWdDLGFBQWEscUJBQXFCLENBQUMsQ0FBQztRQUMzRSxTQUFTO0lBQ1gsQ0FBQztJQUVELE9BQU8sR0FBRyxDQUFDO0FBQ2IsQ0FBQyJ9
|