@devtion/backend 0.0.0-4088679 → 0.0.0-477457c
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/README.md +7 -7
- package/dist/src/functions/index.js +669 -354
- package/dist/src/functions/index.mjs +672 -359
- package/dist/src/functions/types/functions/bandada.d.ts +4 -0
- package/dist/src/functions/types/functions/bandada.d.ts.map +1 -0
- package/dist/{types → src/functions/types}/functions/ceremony.d.ts.map +1 -1
- package/dist/{types → src/functions/types}/functions/circuit.d.ts.map +1 -1
- package/dist/{types → src/functions/types}/functions/index.d.ts +2 -0
- package/dist/{types → src/functions/types}/functions/index.d.ts.map +1 -1
- package/dist/src/functions/types/functions/siwe.d.ts +4 -0
- package/dist/src/functions/types/functions/siwe.d.ts.map +1 -0
- package/dist/{types → src/functions/types}/functions/storage.d.ts.map +1 -1
- package/dist/{types → src/functions/types}/functions/timeout.d.ts.map +1 -1
- package/dist/{types → src/functions/types}/functions/user.d.ts.map +1 -1
- package/dist/{types → src/functions/types}/lib/errors.d.ts +3 -1
- package/dist/src/functions/types/lib/errors.d.ts.map +1 -0
- package/dist/{types → src/functions/types}/lib/services.d.ts +7 -0
- package/dist/src/functions/types/lib/services.d.ts.map +1 -0
- package/dist/src/functions/types/lib/utils.d.ts.map +1 -0
- package/dist/{types → src/functions/types}/types/index.d.ts +56 -0
- package/dist/src/functions/types/types/index.d.ts.map +1 -0
- package/package.json +8 -7
- package/src/functions/bandada.ts +154 -0
- package/src/functions/ceremony.ts +16 -8
- package/src/functions/circuit.ts +443 -391
- package/src/functions/index.ts +2 -0
- package/src/functions/participant.ts +15 -15
- package/src/functions/siwe.ts +77 -0
- package/src/functions/storage.ts +12 -8
- package/src/functions/timeout.ts +14 -13
- package/src/functions/user.ts +22 -11
- package/src/lib/errors.ts +11 -1
- package/src/lib/services.ts +36 -0
- package/src/lib/utils.ts +10 -8
- package/src/types/declarations.d.ts +1 -0
- package/src/types/index.ts +60 -0
- package/dist/types/lib/errors.d.ts.map +0 -1
- package/dist/types/lib/services.d.ts.map +0 -1
- package/dist/types/lib/utils.d.ts.map +0 -1
- package/dist/types/types/index.d.ts.map +0 -1
- /package/dist/{types → src/functions/types}/functions/ceremony.d.ts +0 -0
- /package/dist/{types → src/functions/types}/functions/circuit.d.ts +0 -0
- /package/dist/{types → src/functions/types}/functions/participant.d.ts +0 -0
- /package/dist/{types → src/functions/types}/functions/participant.d.ts.map +0 -0
- /package/dist/{types → src/functions/types}/functions/storage.d.ts +0 -0
- /package/dist/{types → src/functions/types}/functions/timeout.d.ts +0 -0
- /package/dist/{types → src/functions/types}/functions/user.d.ts +0 -0
- /package/dist/{types → src/functions/types}/lib/utils.d.ts +0 -0
- /package/dist/{types → src/functions/types}/types/enums.d.ts +0 -0
- /package/dist/{types → src/functions/types}/types/enums.d.ts.map +0 -0
package/src/functions/circuit.ts
CHANGED
|
@@ -40,6 +40,8 @@ import {
|
|
|
40
40
|
} from "@devtion/actions"
|
|
41
41
|
import { zKey } from "snarkjs"
|
|
42
42
|
import { CommandInvocationStatus, SSMClient } from "@aws-sdk/client-ssm"
|
|
43
|
+
import { EC2Client } from "@aws-sdk/client-ec2"
|
|
44
|
+
import { HttpsError } from "firebase-functions/v2/https"
|
|
43
45
|
import { FinalizeCircuitData, VerifyContributionData } from "../types/index"
|
|
44
46
|
import { LogLevel } from "../types/enums"
|
|
45
47
|
import { COMMON_ERRORS, logAndThrowError, makeError, printLog, SPECIFIC_ERRORS } from "../lib/errors"
|
|
@@ -57,7 +59,6 @@ import {
|
|
|
57
59
|
sleep,
|
|
58
60
|
uploadFileToBucket
|
|
59
61
|
} from "../lib/utils"
|
|
60
|
-
import { EC2Client } from "@aws-sdk/client-ec2"
|
|
61
62
|
|
|
62
63
|
dotenv.config()
|
|
63
64
|
|
|
@@ -114,7 +115,7 @@ const coordinate = async (
|
|
|
114
115
|
if (isSingleParticipantCoordination) {
|
|
115
116
|
// Scenario (A).
|
|
116
117
|
if (emptyWaitingQueue) {
|
|
117
|
-
printLog(`Coordinate - executing scenario A - emptyWaitingQueue`, LogLevel.
|
|
118
|
+
printLog(`Coordinate - executing scenario A - emptyWaitingQueue`, LogLevel.INFO)
|
|
118
119
|
|
|
119
120
|
// Update.
|
|
120
121
|
newCurrentContributorId = participant.id
|
|
@@ -126,7 +127,7 @@ const coordinate = async (
|
|
|
126
127
|
else if (participantResumingAfterTimeoutExpiration) {
|
|
127
128
|
printLog(
|
|
128
129
|
`Coordinate - executing scenario A - single - participantResumingAfterTimeoutExpiration`,
|
|
129
|
-
LogLevel.
|
|
130
|
+
LogLevel.INFO
|
|
130
131
|
)
|
|
131
132
|
|
|
132
133
|
newParticipantStatus = ParticipantStatus.CONTRIBUTING
|
|
@@ -135,7 +136,7 @@ const coordinate = async (
|
|
|
135
136
|
}
|
|
136
137
|
// Scenario (B).
|
|
137
138
|
else if (participantIsNotCurrentContributor) {
|
|
138
|
-
printLog(`Coordinate - executing scenario B - single - participantIsNotCurrentContributor`, LogLevel.
|
|
139
|
+
printLog(`Coordinate - executing scenario B - single - participantIsNotCurrentContributor`, LogLevel.INFO)
|
|
139
140
|
|
|
140
141
|
newCurrentContributorId = currentContributor
|
|
141
142
|
newParticipantStatus = ParticipantStatus.WAITING
|
|
@@ -159,7 +160,7 @@ const coordinate = async (
|
|
|
159
160
|
} else if (participantIsCurrentContributor && participantCompletedOneOrAllContributions && !!ceremonyId) {
|
|
160
161
|
printLog(
|
|
161
162
|
`Coordinate - executing scenario C - multi - participantIsCurrentContributor && participantCompletedOneOrAllContributions`,
|
|
162
|
-
LogLevel.
|
|
163
|
+
LogLevel.INFO
|
|
163
164
|
)
|
|
164
165
|
|
|
165
166
|
newParticipantStatus = ParticipantStatus.CONTRIBUTING
|
|
@@ -189,7 +190,7 @@ const coordinate = async (
|
|
|
189
190
|
|
|
190
191
|
printLog(
|
|
191
192
|
`Participant ${newCurrentContributorId} is the new current contributor for circuit ${circuit.id}`,
|
|
192
|
-
LogLevel.
|
|
193
|
+
LogLevel.INFO
|
|
193
194
|
)
|
|
194
195
|
}
|
|
195
196
|
}
|
|
@@ -214,57 +215,80 @@ const coordinate = async (
|
|
|
214
215
|
* Wait until the command has completed its execution inside the VM.
|
|
215
216
|
* @dev this method implements a custom interval to check 5 times after 1 minute if the command execution
|
|
216
217
|
* has been completed or not by calling the `retrieveCommandStatus` method.
|
|
217
|
-
* @param {any} resolve the promise.
|
|
218
|
-
* @param {any} reject the promise.
|
|
219
218
|
* @param {SSMClient} ssm the SSM client.
|
|
220
219
|
* @param {string} vmInstanceId the unique identifier of the VM instance.
|
|
221
220
|
* @param {string} commandId the unique identifier of the VM command.
|
|
222
221
|
* @returns <Promise<void>> true when the command execution succeed; otherwise false.
|
|
223
222
|
*/
|
|
224
|
-
const waitForVMCommandExecution = (
|
|
225
|
-
resolve
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
223
|
+
const waitForVMCommandExecution = (ssm: SSMClient, vmInstanceId: string, commandId: string): Promise<void> =>
|
|
224
|
+
new Promise((resolve, reject) => {
|
|
225
|
+
const poll = async () => {
|
|
226
|
+
try {
|
|
227
|
+
// Get command status.
|
|
228
|
+
const cmdStatus = await retrieveCommandStatus(ssm, vmInstanceId, commandId)
|
|
229
|
+
printLog(`Checking command ${commandId} status => ${cmdStatus}`, LogLevel.DEBUG)
|
|
230
|
+
|
|
231
|
+
let error: HttpsError | undefined
|
|
232
|
+
switch (cmdStatus) {
|
|
233
|
+
case CommandInvocationStatus.CANCELLING:
|
|
234
|
+
case CommandInvocationStatus.CANCELLED: {
|
|
235
|
+
error = SPECIFIC_ERRORS.SE_VM_CANCELLED_COMMAND_EXECUTION
|
|
236
|
+
break
|
|
237
|
+
}
|
|
238
|
+
case CommandInvocationStatus.DELAYED: {
|
|
239
|
+
error = SPECIFIC_ERRORS.SE_VM_DELAYED_COMMAND_EXECUTION
|
|
240
|
+
break
|
|
241
|
+
}
|
|
242
|
+
case CommandInvocationStatus.FAILED: {
|
|
243
|
+
error = SPECIFIC_ERRORS.SE_VM_FAILED_COMMAND_EXECUTION
|
|
244
|
+
break
|
|
245
|
+
}
|
|
246
|
+
case CommandInvocationStatus.TIMED_OUT: {
|
|
247
|
+
error = SPECIFIC_ERRORS.SE_VM_TIMEDOUT_COMMAND_EXECUTION
|
|
248
|
+
break
|
|
249
|
+
}
|
|
250
|
+
case CommandInvocationStatus.IN_PROGRESS:
|
|
251
|
+
case CommandInvocationStatus.PENDING: {
|
|
252
|
+
// wait a minute and poll again
|
|
253
|
+
setTimeout(poll, 60000)
|
|
254
|
+
return
|
|
255
|
+
}
|
|
256
|
+
case CommandInvocationStatus.SUCCESS: {
|
|
257
|
+
printLog(`Command ${commandId} successfully completed`, LogLevel.DEBUG)
|
|
258
|
+
|
|
259
|
+
// Resolve the promise.
|
|
260
|
+
resolve()
|
|
261
|
+
return
|
|
262
|
+
}
|
|
263
|
+
default: {
|
|
264
|
+
logAndThrowError(SPECIFIC_ERRORS.SE_VM_UNKNOWN_COMMAND_STATUS)
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (error) {
|
|
269
|
+
logAndThrowError(error)
|
|
270
|
+
}
|
|
271
|
+
} catch (error: any) {
|
|
272
|
+
printLog(`Invalid command ${commandId} execution`, LogLevel.DEBUG)
|
|
236
273
|
|
|
237
|
-
|
|
238
|
-
printLog(`Command ${commandId} successfully completed`, LogLevel.DEBUG)
|
|
274
|
+
const ec2 = await createEC2Client()
|
|
239
275
|
|
|
240
|
-
//
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
logAndThrowError(SPECIFIC_ERRORS.SE_VM_TIMEDOUT_COMMAND_EXECUTION)
|
|
247
|
-
reject()
|
|
248
|
-
} else if (cmdStatus === CommandInvocationStatus.CANCELLED) {
|
|
249
|
-
logAndThrowError(SPECIFIC_ERRORS.SE_VM_CANCELLED_COMMAND_EXECUTION)
|
|
250
|
-
reject()
|
|
251
|
-
} else if (cmdStatus === CommandInvocationStatus.DELAYED) {
|
|
252
|
-
logAndThrowError(SPECIFIC_ERRORS.SE_VM_DELAYED_COMMAND_EXECUTION)
|
|
253
|
-
reject()
|
|
254
|
-
}
|
|
255
|
-
} catch (error: any) {
|
|
256
|
-
printLog(`Invalid command ${commandId} execution`, LogLevel.DEBUG)
|
|
276
|
+
// if it errors out, let's just log it as a warning so the coordinator is aware
|
|
277
|
+
try {
|
|
278
|
+
await stopEC2Instance(ec2, vmInstanceId)
|
|
279
|
+
} catch (stopError: any) {
|
|
280
|
+
printLog(`Error while stopping VM instance ${vmInstanceId} - Error ${stopError}`, LogLevel.WARN)
|
|
281
|
+
}
|
|
257
282
|
|
|
258
|
-
|
|
283
|
+
if (!error.toString().includes(commandId)) logAndThrowError(COMMON_ERRORS.CM_INVALID_COMMAND_EXECUTION)
|
|
259
284
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
// Clear the interval.
|
|
264
|
-
clearInterval(interval)
|
|
285
|
+
// Reject the promise.
|
|
286
|
+
reject()
|
|
287
|
+
}
|
|
265
288
|
}
|
|
266
|
-
|
|
267
|
-
|
|
289
|
+
|
|
290
|
+
setTimeout(poll, 60000)
|
|
291
|
+
})
|
|
268
292
|
|
|
269
293
|
/**
|
|
270
294
|
* This method is used to coordinate the waiting queues of ceremony circuits.
|
|
@@ -286,9 +310,9 @@ const waitForVMCommandExecution = (
|
|
|
286
310
|
* - Just completed a contribution or all contributions for each circuit. If yes, coordinate (multi-participant scenario).
|
|
287
311
|
*/
|
|
288
312
|
export const coordinateCeremonyParticipant = functionsV1
|
|
289
|
-
.region(
|
|
313
|
+
.region("europe-west1")
|
|
290
314
|
.runWith({
|
|
291
|
-
memory: "
|
|
315
|
+
memory: "1GB"
|
|
292
316
|
})
|
|
293
317
|
.firestore.document(
|
|
294
318
|
`${commonTerms.collections.ceremonies.name}/{ceremonyId}/${commonTerms.collections.participants.name}/{participantId}`
|
|
@@ -322,10 +346,10 @@ export const coordinateCeremonyParticipant = functionsV1
|
|
|
322
346
|
contributionStep: changedContributionStep
|
|
323
347
|
} = changedParticipant.data()!
|
|
324
348
|
|
|
325
|
-
printLog(`Coordinate participant ${exParticipant.id} for ceremony ${ceremonyId}`, LogLevel.
|
|
349
|
+
printLog(`Coordinate participant ${exParticipant.id} for ceremony ${ceremonyId}`, LogLevel.INFO)
|
|
326
350
|
printLog(
|
|
327
351
|
`Participant status: ${prevStatus} => ${changedStatus} - Participant contribution step: ${prevContributionStep} => ${changedContributionStep}`,
|
|
328
|
-
LogLevel.
|
|
352
|
+
LogLevel.INFO
|
|
329
353
|
)
|
|
330
354
|
|
|
331
355
|
// Define pre-conditions.
|
|
@@ -346,8 +370,8 @@ export const coordinateCeremonyParticipant = functionsV1
|
|
|
346
370
|
|
|
347
371
|
const participantCompletedContribution =
|
|
348
372
|
prevContributionProgress === changedContributionProgress &&
|
|
349
|
-
prevStatus === ParticipantStatus.CONTRIBUTING
|
|
350
|
-
|
|
373
|
+
(prevStatus === ParticipantStatus.CONTRIBUTING ||
|
|
374
|
+
prevContributionStep === ParticipantContributionStep.VERIFYING) &&
|
|
351
375
|
changedStatus === ParticipantStatus.CONTRIBUTED &&
|
|
352
376
|
changedContributionStep === ParticipantContributionStep.COMPLETED
|
|
353
377
|
|
|
@@ -360,7 +384,7 @@ export const coordinateCeremonyParticipant = functionsV1
|
|
|
360
384
|
// Step (2.A).
|
|
361
385
|
printLog(
|
|
362
386
|
`Participant is ready for first contribution (${participantReadyForFirstContribution}) or for the next contribution (${participantReadyForNextContribution}) or is resuming after a timeout expiration (${participantResumingContributionAfterTimeout})`,
|
|
363
|
-
LogLevel.
|
|
387
|
+
LogLevel.INFO
|
|
364
388
|
)
|
|
365
389
|
|
|
366
390
|
// Get the circuit.
|
|
@@ -374,7 +398,7 @@ export const coordinateCeremonyParticipant = functionsV1
|
|
|
374
398
|
// Step (2.B).
|
|
375
399
|
printLog(
|
|
376
400
|
`Participant completed a contribution (${participantCompletedContribution}) or every contribution for each circuit (${participantCompletedEveryCircuitContribution})`,
|
|
377
|
-
LogLevel.
|
|
401
|
+
LogLevel.INFO
|
|
378
402
|
)
|
|
379
403
|
|
|
380
404
|
// Get the circuit.
|
|
@@ -387,7 +411,6 @@ export const coordinateCeremonyParticipant = functionsV1
|
|
|
387
411
|
}
|
|
388
412
|
})
|
|
389
413
|
|
|
390
|
-
|
|
391
414
|
/**
|
|
392
415
|
* Recursive function to check whether an EC2 is in a running state
|
|
393
416
|
* @notice required step to run commands
|
|
@@ -396,23 +419,18 @@ export const coordinateCeremonyParticipant = functionsV1
|
|
|
396
419
|
* @param attempts <number> - how many times to retry before failing
|
|
397
420
|
* @returns <Promise<boolean>> - whether the VM was started
|
|
398
421
|
*/
|
|
399
|
-
const checkIfVMRunning = async (
|
|
400
|
-
ec2: EC2Client,
|
|
401
|
-
vmInstanceId: string,
|
|
402
|
-
attempts = 5
|
|
403
|
-
): Promise<boolean> => {
|
|
422
|
+
const checkIfVMRunning = async (ec2: EC2Client, vmInstanceId: string, attempts = 5): Promise<boolean> => {
|
|
404
423
|
// if we tried 5 times, then throw an error
|
|
405
424
|
if (attempts <= 0) logAndThrowError(SPECIFIC_ERRORS.SE_VM_NOT_RUNNING)
|
|
406
425
|
|
|
407
|
-
await sleep(60000)
|
|
408
|
-
const isVMRunning = await checkIfRunning(ec2, vmInstanceId)
|
|
426
|
+
await sleep(60000) // Wait for 1 min
|
|
427
|
+
const isVMRunning = await checkIfRunning(ec2, vmInstanceId)
|
|
409
428
|
|
|
410
429
|
if (!isVMRunning) {
|
|
411
430
|
printLog(`VM not running, ${attempts - 1} attempts remaining. Retrying in 1 minute...`, LogLevel.DEBUG)
|
|
412
|
-
return
|
|
413
|
-
} else {
|
|
414
|
-
return true
|
|
431
|
+
return checkIfVMRunning(ec2, vmInstanceId, attempts - 1)
|
|
415
432
|
}
|
|
433
|
+
return true
|
|
416
434
|
}
|
|
417
435
|
|
|
418
436
|
/**
|
|
@@ -442,367 +460,391 @@ const checkIfVMRunning = async (
|
|
|
442
460
|
* 2) Send all updates atomically to the Firestore database.
|
|
443
461
|
*/
|
|
444
462
|
export const verifycontribution = functionsV2.https.onCall(
|
|
445
|
-
{ memory: "
|
|
463
|
+
{ memory: "32GiB", timeoutSeconds: 3600, region: "europe-west1", cpu: 8 },
|
|
446
464
|
async (request: functionsV2.https.CallableRequest<VerifyContributionData>): Promise<any> => {
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
if (
|
|
459
|
-
!process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_NAME ||
|
|
460
|
-
!process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_VERSION ||
|
|
461
|
-
!process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_COMMIT_HASH
|
|
462
|
-
)
|
|
463
|
-
logAndThrowError(COMMON_ERRORS.CM_WRONG_CONFIGURATION)
|
|
464
|
-
|
|
465
|
-
// Step (0).
|
|
466
|
-
|
|
467
|
-
// Prepare and start timer.
|
|
468
|
-
const verifyContributionTimer = new Timer({ label: commonTerms.cloudFunctionsNames.verifyContribution })
|
|
469
|
-
verifyContributionTimer.start()
|
|
470
|
-
|
|
471
|
-
// Get DB.
|
|
472
|
-
const firestore = admin.firestore()
|
|
473
|
-
// Prepare batch of txs.
|
|
474
|
-
const batch = firestore.batch()
|
|
475
|
-
|
|
476
|
-
// Extract data.
|
|
477
|
-
const { ceremonyId, circuitId, contributorOrCoordinatorIdentifier, bucketName } = request.data
|
|
478
|
-
const userId = request.auth?.uid
|
|
479
|
-
|
|
480
|
-
// Look for the ceremony, circuit and participant document.
|
|
481
|
-
const ceremonyDoc = await getDocumentById(commonTerms.collections.ceremonies.name, ceremonyId)
|
|
482
|
-
const circuitDoc = await getDocumentById(getCircuitsCollectionPath(ceremonyId), circuitId)
|
|
483
|
-
const participantDoc = await getDocumentById(getParticipantsCollectionPath(ceremonyId), userId!)
|
|
465
|
+
try {
|
|
466
|
+
if (!request.auth || (!request.auth.token.participant && !request.auth.token.coordinator))
|
|
467
|
+
logAndThrowError(SPECIFIC_ERRORS.SE_AUTH_NO_CURRENT_AUTH_USER)
|
|
468
|
+
|
|
469
|
+
if (
|
|
470
|
+
!request.data.ceremonyId ||
|
|
471
|
+
!request.data.circuitId ||
|
|
472
|
+
!request.data.contributorOrCoordinatorIdentifier ||
|
|
473
|
+
!request.data.bucketName
|
|
474
|
+
)
|
|
475
|
+
logAndThrowError(COMMON_ERRORS.CM_MISSING_OR_WRONG_INPUT_DATA)
|
|
484
476
|
|
|
485
|
-
|
|
486
|
-
|
|
477
|
+
if (
|
|
478
|
+
!process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_NAME ||
|
|
479
|
+
!process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_VERSION ||
|
|
480
|
+
!process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_COMMIT_HASH
|
|
481
|
+
)
|
|
482
|
+
logAndThrowError(COMMON_ERRORS.CM_WRONG_CONFIGURATION)
|
|
483
|
+
|
|
484
|
+
const BUCKET_NAME_REGEX = /^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$/
|
|
485
|
+
if (!BUCKET_NAME_REGEX.test(request.data.bucketName)) logAndThrowError(SPECIFIC_ERRORS.WRONG_BUCKET_NAME)
|
|
486
|
+
|
|
487
|
+
// Step (0).
|
|
488
|
+
|
|
489
|
+
// Prepare and start timer.
|
|
490
|
+
const verifyContributionTimer = new Timer({ label: commonTerms.cloudFunctionsNames.verifyContribution })
|
|
491
|
+
verifyContributionTimer.start()
|
|
492
|
+
|
|
493
|
+
// Get DB.
|
|
494
|
+
const firestore = admin.firestore()
|
|
495
|
+
// Prepare batch of txs.
|
|
496
|
+
const batch = firestore.batch()
|
|
497
|
+
|
|
498
|
+
// Extract data.
|
|
499
|
+
const { ceremonyId, circuitId, contributorOrCoordinatorIdentifier, bucketName } = request.data
|
|
500
|
+
const userId = request.auth?.uid
|
|
501
|
+
|
|
502
|
+
// Look for the ceremony, circuit and participant document.
|
|
503
|
+
const ceremonyDoc = await getDocumentById(commonTerms.collections.ceremonies.name, ceremonyId)
|
|
504
|
+
const circuitDoc = await getDocumentById(getCircuitsCollectionPath(ceremonyId), circuitId)
|
|
505
|
+
const participantDoc = await getDocumentById(getParticipantsCollectionPath(ceremonyId), userId!)
|
|
506
|
+
|
|
507
|
+
if (!ceremonyDoc.data() || !circuitDoc.data() || !participantDoc.data())
|
|
508
|
+
logAndThrowError(COMMON_ERRORS.CM_INEXISTENT_DOCUMENT_DATA)
|
|
509
|
+
|
|
510
|
+
// Extract documents data.
|
|
511
|
+
const { state } = ceremonyDoc.data()!
|
|
512
|
+
const { status, contributions, verificationStartedAt, contributionStartedAt } = participantDoc.data()!
|
|
513
|
+
const { waitingQueue, prefix, avgTimings, verification, files } = circuitDoc.data()!
|
|
514
|
+
const { completedContributions, failedContributions } = waitingQueue
|
|
515
|
+
const {
|
|
516
|
+
contributionComputation: avgContributionComputationTime,
|
|
517
|
+
fullContribution: avgFullContributionTime,
|
|
518
|
+
verifyCloudFunction: avgVerifyCloudFunctionTime
|
|
519
|
+
} = avgTimings
|
|
520
|
+
const { cfOrVm, vm } = verification
|
|
521
|
+
// we might not have it if the circuit is not using VM.
|
|
522
|
+
let vmInstanceId: string = ""
|
|
523
|
+
if (vm) vmInstanceId = vm.vmInstanceId
|
|
487
524
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
525
|
+
// Define pre-conditions.
|
|
526
|
+
const isFinalizing = state === CeremonyState.CLOSED && request.auth && request.auth.token.coordinator // true only when the coordinator verifies the final contributions.
|
|
527
|
+
const isContributing = status === ParticipantStatus.CONTRIBUTING
|
|
528
|
+
const isUsingVM = cfOrVm === CircuitContributionVerificationMechanism.VM && !!vmInstanceId
|
|
529
|
+
|
|
530
|
+
// Prepare state.
|
|
531
|
+
let isContributionValid = false
|
|
532
|
+
let verifyCloudFunctionExecutionTime = 0 // time spent while executing the verify contribution cloud function.
|
|
533
|
+
let verifyCloudFunctionTime = 0 // time spent while executing the core business logic of this cloud function.
|
|
534
|
+
let fullContributionTime = 0 // time spent while doing non-verification contributions tasks (download, compute, upload).
|
|
535
|
+
let contributionComputationTime = 0 // time spent while computing the contribution.
|
|
536
|
+
let lastZkeyBlake2bHash: string = "" // the Blake2B hash of the last zKey.
|
|
537
|
+
let verificationTranscriptTemporaryLocalPath: string = "" // the local temporary path for the verification transcript.
|
|
538
|
+
let transcriptBlake2bHash: string = "" // the Blake2B hash of the verification transcript.
|
|
539
|
+
let commandId: string = "" // the unique identifier of the VM command.
|
|
540
|
+
|
|
541
|
+
// Derive necessary data.
|
|
542
|
+
const lastZkeyIndex = formatZkeyIndex(completedContributions + 1)
|
|
543
|
+
const verificationTranscriptCompleteFilename = `${prefix}_${
|
|
544
|
+
isFinalizing
|
|
545
|
+
? `${contributorOrCoordinatorIdentifier}_${finalContributionIndex}_verification_transcript.log`
|
|
546
|
+
: `${lastZkeyIndex}_${contributorOrCoordinatorIdentifier}_verification_transcript.log`
|
|
547
|
+
}`
|
|
548
|
+
|
|
549
|
+
const lastZkeyFilename = `${prefix}_${isFinalizing ? finalContributionIndex : lastZkeyIndex}.zkey`
|
|
550
|
+
|
|
551
|
+
// Prepare state for VM verification (if needed).
|
|
552
|
+
const ec2 = await createEC2Client()
|
|
553
|
+
const ssm = await createSSMClient()
|
|
554
|
+
|
|
555
|
+
// Step (1.A.1).
|
|
556
|
+
// Get storage paths.
|
|
557
|
+
const verificationTranscriptStoragePathAndFilename = getTranscriptStorageFilePath(
|
|
558
|
+
prefix,
|
|
559
|
+
verificationTranscriptCompleteFilename
|
|
560
|
+
)
|
|
561
|
+
// the zKey storage path is required to be sent to the VM api
|
|
562
|
+
const lastZkeyStoragePath = getZkeyStorageFilePath(
|
|
563
|
+
prefix,
|
|
564
|
+
`${prefix}_${isFinalizing ? finalContributionIndex : lastZkeyIndex}.zkey`
|
|
565
|
+
)
|
|
502
566
|
|
|
503
|
-
|
|
504
|
-
const isFinalizing = state === CeremonyState.CLOSED && request.auth && request.auth.token.coordinator // true only when the coordinator verifies the final contributions.
|
|
505
|
-
const isContributing = status === ParticipantStatus.CONTRIBUTING
|
|
506
|
-
const isUsingVM = cfOrVm === CircuitContributionVerificationMechanism.VM && !!vmInstanceId
|
|
507
|
-
|
|
508
|
-
// Prepare state.
|
|
509
|
-
let isContributionValid = false
|
|
510
|
-
let verifyCloudFunctionExecutionTime = 0 // time spent while executing the verify contribution cloud function.
|
|
511
|
-
let verifyCloudFunctionTime = 0 // time spent while executing the core business logic of this cloud function.
|
|
512
|
-
let fullContributionTime = 0 // time spent while doing non-verification contributions tasks (download, compute, upload).
|
|
513
|
-
let contributionComputationTime = 0 // time spent while computing the contribution.
|
|
514
|
-
let lastZkeyBlake2bHash: string = "" // the Blake2B hash of the last zKey.
|
|
515
|
-
let verificationTranscriptTemporaryLocalPath: string = "" // the local temporary path for the verification transcript.
|
|
516
|
-
let transcriptBlake2bHash: string = "" // the Blake2B hash of the verification transcript.
|
|
517
|
-
let commandId: string = "" // the unique identifier of the VM command.
|
|
518
|
-
|
|
519
|
-
// Derive necessary data.
|
|
520
|
-
const lastZkeyIndex = formatZkeyIndex(completedContributions + 1)
|
|
521
|
-
const verificationTranscriptCompleteFilename = `${prefix}_${
|
|
522
|
-
isFinalizing
|
|
523
|
-
? `${contributorOrCoordinatorIdentifier}_${finalContributionIndex}_verification_transcript.log`
|
|
524
|
-
: `${lastZkeyIndex}_${contributorOrCoordinatorIdentifier}_verification_transcript.log`
|
|
525
|
-
}`
|
|
526
|
-
|
|
527
|
-
const lastZkeyFilename = `${prefix}_${isFinalizing ? finalContributionIndex : lastZkeyIndex}.zkey`
|
|
528
|
-
|
|
529
|
-
// Prepare state for VM verification (if needed).
|
|
530
|
-
const ec2 = await createEC2Client()
|
|
531
|
-
const ssm = await createSSMClient()
|
|
532
|
-
|
|
533
|
-
// Step (1.A.1).
|
|
534
|
-
// Get storage paths.
|
|
535
|
-
const verificationTranscriptStoragePathAndFilename = getTranscriptStorageFilePath(
|
|
536
|
-
prefix,
|
|
537
|
-
verificationTranscriptCompleteFilename
|
|
538
|
-
)
|
|
539
|
-
// the zKey storage path is required to be sent to the VM api
|
|
540
|
-
const lastZkeyStoragePath = getZkeyStorageFilePath(
|
|
541
|
-
prefix,
|
|
542
|
-
`${prefix}_${isFinalizing ? finalContributionIndex : lastZkeyIndex}.zkey`
|
|
543
|
-
)
|
|
567
|
+
const verificationTaskTimer = new Timer({ label: `${ceremonyId}-${circuitId}-${participantDoc.id}` })
|
|
544
568
|
|
|
545
|
-
|
|
569
|
+
const dumpLog = async (path: string): Promise<void> => {
|
|
570
|
+
printLog(`transcript >>>>>>`, LogLevel.DEBUG)
|
|
571
|
+
try {
|
|
572
|
+
const data = await fs.promises.readFile(path, "utf8")
|
|
573
|
+
printLog(data, LogLevel.DEBUG)
|
|
574
|
+
} catch (readError: any) {
|
|
575
|
+
printLog(readError, LogLevel.ERROR)
|
|
576
|
+
}
|
|
577
|
+
}
|
|
546
578
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
579
|
+
const completeVerification = async () => {
|
|
580
|
+
// Stop verification task timer.
|
|
581
|
+
printLog("Completing verification", LogLevel.DEBUG)
|
|
582
|
+
verificationTaskTimer.stop()
|
|
583
|
+
verifyCloudFunctionExecutionTime = verificationTaskTimer.ms()
|
|
552
584
|
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
585
|
+
if (isUsingVM) {
|
|
586
|
+
// Create temporary path.
|
|
587
|
+
verificationTranscriptTemporaryLocalPath = createTemporaryLocalPath(
|
|
588
|
+
`${circuitId}_${participantDoc.id}.log`
|
|
589
|
+
)
|
|
558
590
|
|
|
559
|
-
|
|
591
|
+
await sleep(1000) // wait 1s for file creation.
|
|
560
592
|
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
593
|
+
// Download from bucket.
|
|
594
|
+
// nb. the transcript MUST be uploaded from the VM by verification commands.
|
|
595
|
+
await downloadArtifactFromS3Bucket(
|
|
596
|
+
bucketName,
|
|
597
|
+
verificationTranscriptStoragePathAndFilename,
|
|
598
|
+
verificationTranscriptTemporaryLocalPath
|
|
599
|
+
)
|
|
568
600
|
|
|
569
|
-
|
|
570
|
-
|
|
601
|
+
// Read the verification trascript and validate data by checking for core info ("ZKey Ok!").
|
|
602
|
+
const content = fs.readFileSync(verificationTranscriptTemporaryLocalPath, "utf-8")
|
|
571
603
|
|
|
572
|
-
|
|
604
|
+
if (content.includes("ZKey Ok!")) isContributionValid = true
|
|
573
605
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
606
|
+
// If the contribution is valid, then format and store the trascript.
|
|
607
|
+
if (isContributionValid) {
|
|
608
|
+
// eslint-disable-next-line no-control-regex
|
|
609
|
+
const updated = content.replace(/\x1b[[0-9;]*m/g, "")
|
|
578
610
|
|
|
579
|
-
|
|
611
|
+
fs.writeFileSync(verificationTranscriptTemporaryLocalPath, updated)
|
|
612
|
+
}
|
|
580
613
|
}
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
printLog(`The contribution has been verified - Result ${isContributionValid}`, LogLevel.DEBUG)
|
|
584
|
-
|
|
585
|
-
// Create a new contribution document.
|
|
586
|
-
const contributionDoc = await firestore
|
|
587
|
-
.collection(getContributionsCollectionPath(ceremonyId, circuitId))
|
|
588
|
-
.doc()
|
|
589
|
-
.get()
|
|
590
614
|
|
|
591
|
-
|
|
592
|
-
if (isContributionValid) {
|
|
593
|
-
// Sleep ~3 seconds to wait for verification transcription.
|
|
594
|
-
await sleep(3000)
|
|
615
|
+
printLog(`The contribution has been verified - Result ${isContributionValid}`, LogLevel.DEBUG)
|
|
595
616
|
|
|
596
|
-
//
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
const hashRegex = /[a-fA-F0-9]{64}/
|
|
602
|
-
const match = lastZkeyBlake2bHash.match(hashRegex)!
|
|
617
|
+
// Create a new contribution document.
|
|
618
|
+
const contributionDoc = await firestore
|
|
619
|
+
.collection(getContributionsCollectionPath(ceremonyId, circuitId))
|
|
620
|
+
.doc()
|
|
621
|
+
.get()
|
|
603
622
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
//
|
|
607
|
-
await
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
623
|
+
// Step (1.A.4).
|
|
624
|
+
if (isContributionValid) {
|
|
625
|
+
// Sleep ~3 seconds to wait for verification transcription.
|
|
626
|
+
await sleep(3000)
|
|
627
|
+
|
|
628
|
+
// Step (1.A.4.A.1).
|
|
629
|
+
if (isUsingVM) {
|
|
630
|
+
// Retrieve the contribution hash from the command output.
|
|
631
|
+
lastZkeyBlake2bHash = await retrieveCommandOutput(ssm, vmInstanceId, commandId)
|
|
632
|
+
|
|
633
|
+
const hashRegex = /[a-fA-F0-9]{64}/
|
|
634
|
+
const match = lastZkeyBlake2bHash.match(hashRegex)!
|
|
635
|
+
|
|
636
|
+
lastZkeyBlake2bHash = match.at(0)!
|
|
637
|
+
|
|
638
|
+
// re upload the formatted verification transcript
|
|
639
|
+
await uploadFileToBucket(
|
|
640
|
+
bucketName,
|
|
641
|
+
verificationTranscriptStoragePathAndFilename,
|
|
642
|
+
verificationTranscriptTemporaryLocalPath,
|
|
643
|
+
true
|
|
644
|
+
)
|
|
645
|
+
} else {
|
|
646
|
+
// Upload verification transcript.
|
|
647
|
+
/// nb. do not use multi-part upload here due to small file size.
|
|
648
|
+
await uploadFileToBucket(
|
|
649
|
+
bucketName,
|
|
650
|
+
verificationTranscriptStoragePathAndFilename,
|
|
651
|
+
verificationTranscriptTemporaryLocalPath,
|
|
652
|
+
true
|
|
653
|
+
)
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// Compute verification transcript hash.
|
|
657
|
+
transcriptBlake2bHash = await blake512FromPath(verificationTranscriptTemporaryLocalPath)
|
|
658
|
+
|
|
659
|
+
// Free resources by unlinking transcript temporary file.
|
|
660
|
+
fs.unlinkSync(verificationTranscriptTemporaryLocalPath)
|
|
661
|
+
|
|
662
|
+
// Filter participant contributions to find the data related to the one verified.
|
|
663
|
+
const participantContributions = contributions.filter(
|
|
664
|
+
(contribution: Contribution) =>
|
|
665
|
+
!!contribution.hash && !!contribution.computationTime && !contribution.doc
|
|
612
666
|
)
|
|
613
667
|
|
|
668
|
+
/// @dev (there must be only one contribution with an empty 'doc' field).
|
|
669
|
+
if (participantContributions.length !== 1)
|
|
670
|
+
logAndThrowError(SPECIFIC_ERRORS.SE_VERIFICATION_NO_PARTICIPANT_CONTRIBUTION_DATA)
|
|
671
|
+
|
|
672
|
+
// Get contribution computation time.
|
|
673
|
+
contributionComputationTime = contributions.at(0).computationTime
|
|
674
|
+
|
|
675
|
+
// Step (1.A.4.A.2).
|
|
676
|
+
batch.create(contributionDoc.ref, {
|
|
677
|
+
participantId: participantDoc.id,
|
|
678
|
+
contributionComputationTime,
|
|
679
|
+
verificationComputationTime: verifyCloudFunctionExecutionTime,
|
|
680
|
+
zkeyIndex: isFinalizing ? finalContributionIndex : lastZkeyIndex,
|
|
681
|
+
files: {
|
|
682
|
+
transcriptFilename: verificationTranscriptCompleteFilename,
|
|
683
|
+
lastZkeyFilename,
|
|
684
|
+
transcriptStoragePath: verificationTranscriptStoragePathAndFilename,
|
|
685
|
+
lastZkeyStoragePath,
|
|
686
|
+
transcriptBlake2bHash,
|
|
687
|
+
lastZkeyBlake2bHash
|
|
688
|
+
},
|
|
689
|
+
verificationSoftware: {
|
|
690
|
+
name: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_NAME),
|
|
691
|
+
version: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_VERSION),
|
|
692
|
+
commitHash: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_COMMIT_HASH)
|
|
693
|
+
},
|
|
694
|
+
valid: isContributionValid,
|
|
695
|
+
lastUpdated: getCurrentServerTimestampInMillis()
|
|
696
|
+
})
|
|
697
|
+
|
|
698
|
+
verifyContributionTimer.stop()
|
|
699
|
+
verifyCloudFunctionTime = verifyContributionTimer.ms()
|
|
614
700
|
} else {
|
|
615
|
-
//
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
701
|
+
// Step (1.A.4.B).
|
|
702
|
+
|
|
703
|
+
// Free-up storage by deleting invalid contribution.
|
|
704
|
+
await deleteObject(bucketName, lastZkeyStoragePath)
|
|
705
|
+
|
|
706
|
+
// Step (1.A.4.B.1).
|
|
707
|
+
batch.create(contributionDoc.ref, {
|
|
708
|
+
participantId: participantDoc.id,
|
|
709
|
+
verificationComputationTime: verifyCloudFunctionExecutionTime,
|
|
710
|
+
zkeyIndex: isFinalizing ? finalContributionIndex : lastZkeyIndex,
|
|
711
|
+
verificationSoftware: {
|
|
712
|
+
name: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_NAME),
|
|
713
|
+
version: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_VERSION),
|
|
714
|
+
commitHash: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_COMMIT_HASH)
|
|
715
|
+
},
|
|
716
|
+
valid: isContributionValid,
|
|
717
|
+
lastUpdated: getCurrentServerTimestampInMillis()
|
|
718
|
+
})
|
|
623
719
|
}
|
|
624
720
|
|
|
625
|
-
//
|
|
626
|
-
|
|
721
|
+
// Stop VM instance
|
|
722
|
+
if (isUsingVM) {
|
|
723
|
+
// using try and catch as the VM stopping function can throw
|
|
724
|
+
// however we want to continue without stopping as the
|
|
725
|
+
// verification was valid, and inform the coordinator
|
|
726
|
+
try {
|
|
727
|
+
await stopEC2Instance(ec2, vmInstanceId)
|
|
728
|
+
} catch (error: any) {
|
|
729
|
+
printLog(`Error while stopping VM instance ${vmInstanceId} - Error ${error}`, LogLevel.WARN)
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
// Step (1.A.4.C)
|
|
733
|
+
if (!isFinalizing) {
|
|
734
|
+
// Step (1.A.4.C.1)
|
|
735
|
+
// Compute new average contribution/verification time.
|
|
736
|
+
fullContributionTime = Number(verificationStartedAt) - Number(contributionStartedAt)
|
|
737
|
+
|
|
738
|
+
const newAvgContributionComputationTime =
|
|
739
|
+
avgContributionComputationTime > 0
|
|
740
|
+
? (avgContributionComputationTime + contributionComputationTime) / 2
|
|
741
|
+
: contributionComputationTime
|
|
742
|
+
const newAvgFullContributionTime =
|
|
743
|
+
avgFullContributionTime > 0
|
|
744
|
+
? (avgFullContributionTime + fullContributionTime) / 2
|
|
745
|
+
: fullContributionTime
|
|
746
|
+
const newAvgVerifyCloudFunctionTime =
|
|
747
|
+
avgVerifyCloudFunctionTime > 0
|
|
748
|
+
? (avgVerifyCloudFunctionTime + verifyCloudFunctionTime) / 2
|
|
749
|
+
: verifyCloudFunctionTime
|
|
750
|
+
|
|
751
|
+
// Prepare tx to update circuit average contribution/verification time.
|
|
752
|
+
const updatedCircuitDoc = await getDocumentById(getCircuitsCollectionPath(ceremonyId), circuitId)
|
|
753
|
+
const { waitingQueue: updatedWaitingQueue } = updatedCircuitDoc.data()!
|
|
754
|
+
/// @dev this must happen only for valid contributions.
|
|
755
|
+
batch.update(circuitDoc.ref, {
|
|
756
|
+
avgTimings: {
|
|
757
|
+
contributionComputation: isContributionValid
|
|
758
|
+
? newAvgContributionComputationTime
|
|
759
|
+
: avgContributionComputationTime,
|
|
760
|
+
fullContribution: isContributionValid
|
|
761
|
+
? newAvgFullContributionTime
|
|
762
|
+
: avgFullContributionTime,
|
|
763
|
+
verifyCloudFunction: isContributionValid
|
|
764
|
+
? newAvgVerifyCloudFunctionTime
|
|
765
|
+
: avgVerifyCloudFunctionTime
|
|
766
|
+
},
|
|
767
|
+
waitingQueue: {
|
|
768
|
+
...updatedWaitingQueue,
|
|
769
|
+
completedContributions: isContributionValid
|
|
770
|
+
? completedContributions + 1
|
|
771
|
+
: completedContributions,
|
|
772
|
+
failedContributions: isContributionValid ? failedContributions : failedContributions + 1
|
|
773
|
+
},
|
|
774
|
+
lastUpdated: getCurrentServerTimestampInMillis()
|
|
775
|
+
})
|
|
776
|
+
}
|
|
627
777
|
|
|
628
|
-
//
|
|
629
|
-
|
|
778
|
+
// Step (2).
|
|
779
|
+
await batch.commit()
|
|
630
780
|
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
781
|
+
printLog(
|
|
782
|
+
`The contribution #${
|
|
783
|
+
isFinalizing ? finalContributionIndex : lastZkeyIndex
|
|
784
|
+
} of circuit ${circuitId} (ceremony ${ceremonyId}) has been verified as ${
|
|
785
|
+
isContributionValid ? "valid" : "invalid"
|
|
786
|
+
} for the participant ${participantDoc.id}`,
|
|
787
|
+
LogLevel.INFO
|
|
635
788
|
)
|
|
636
|
-
|
|
637
|
-
/// @dev (there must be only one contribution with an empty 'doc' field).
|
|
638
|
-
if (participantContributions.length !== 1)
|
|
639
|
-
logAndThrowError(SPECIFIC_ERRORS.SE_VERIFICATION_NO_PARTICIPANT_CONTRIBUTION_DATA)
|
|
640
|
-
|
|
641
|
-
// Get contribution computation time.
|
|
642
|
-
contributionComputationTime = contributions.at(0).computationTime
|
|
643
|
-
|
|
644
|
-
// Step (1.A.4.A.2).
|
|
645
|
-
batch.create(contributionDoc.ref, {
|
|
646
|
-
participantId: participantDoc.id,
|
|
647
|
-
contributionComputationTime,
|
|
648
|
-
verificationComputationTime: verifyCloudFunctionExecutionTime,
|
|
649
|
-
zkeyIndex: isFinalizing ? finalContributionIndex : lastZkeyIndex,
|
|
650
|
-
files: {
|
|
651
|
-
transcriptFilename: verificationTranscriptCompleteFilename,
|
|
652
|
-
lastZkeyFilename,
|
|
653
|
-
transcriptStoragePath: verificationTranscriptStoragePathAndFilename,
|
|
654
|
-
lastZkeyStoragePath,
|
|
655
|
-
transcriptBlake2bHash,
|
|
656
|
-
lastZkeyBlake2bHash
|
|
657
|
-
},
|
|
658
|
-
verificationSoftware: {
|
|
659
|
-
name: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_NAME),
|
|
660
|
-
version: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_VERSION),
|
|
661
|
-
commitHash: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_COMMIT_HASH)
|
|
662
|
-
},
|
|
663
|
-
valid: isContributionValid,
|
|
664
|
-
lastUpdated: getCurrentServerTimestampInMillis()
|
|
665
|
-
})
|
|
666
|
-
|
|
667
|
-
verifyContributionTimer.stop()
|
|
668
|
-
verifyCloudFunctionTime = verifyContributionTimer.ms()
|
|
669
|
-
} else {
|
|
670
|
-
// Step (1.A.4.B).
|
|
671
|
-
|
|
672
|
-
// Free-up storage by deleting invalid contribution.
|
|
673
|
-
await deleteObject(bucketName, lastZkeyStoragePath)
|
|
674
|
-
|
|
675
|
-
// Step (1.A.4.B.1).
|
|
676
|
-
batch.create(contributionDoc.ref, {
|
|
677
|
-
participantId: participantDoc.id,
|
|
678
|
-
verificationComputationTime: verifyCloudFunctionExecutionTime,
|
|
679
|
-
zkeyIndex: isFinalizing ? finalContributionIndex : lastZkeyIndex,
|
|
680
|
-
verificationSoftware: {
|
|
681
|
-
name: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_NAME),
|
|
682
|
-
version: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_VERSION),
|
|
683
|
-
commitHash: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_COMMIT_HASH)
|
|
684
|
-
},
|
|
685
|
-
valid: isContributionValid,
|
|
686
|
-
lastUpdated: getCurrentServerTimestampInMillis()
|
|
687
|
-
})
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
// Stop VM instance
|
|
691
|
-
if (isUsingVM) await stopEC2Instance(ec2, vmInstanceId)
|
|
692
|
-
|
|
693
|
-
// Step (1.A.4.C)
|
|
694
|
-
if (!isFinalizing) {
|
|
695
|
-
// Step (1.A.4.C.1)
|
|
696
|
-
// Compute new average contribution/verification time.
|
|
697
|
-
fullContributionTime = Number(verificationStartedAt) - Number(contributionStartedAt)
|
|
698
|
-
|
|
699
|
-
const newAvgContributionComputationTime =
|
|
700
|
-
avgContributionComputationTime > 0
|
|
701
|
-
? (avgContributionComputationTime + contributionComputationTime) / 2
|
|
702
|
-
: contributionComputationTime
|
|
703
|
-
const newAvgFullContributionTime =
|
|
704
|
-
avgFullContributionTime > 0
|
|
705
|
-
? (avgFullContributionTime + fullContributionTime) / 2
|
|
706
|
-
: fullContributionTime
|
|
707
|
-
const newAvgVerifyCloudFunctionTime =
|
|
708
|
-
avgVerifyCloudFunctionTime > 0
|
|
709
|
-
? (avgVerifyCloudFunctionTime + verifyCloudFunctionTime) / 2
|
|
710
|
-
: verifyCloudFunctionTime
|
|
711
|
-
|
|
712
|
-
// Prepare tx to update circuit average contribution/verification time.
|
|
713
|
-
const updatedCircuitDoc = await getDocumentById(getCircuitsCollectionPath(ceremonyId), circuitId)
|
|
714
|
-
const { waitingQueue: updatedWaitingQueue } = updatedCircuitDoc.data()!
|
|
715
|
-
/// @dev this must happen only for valid contributions.
|
|
716
|
-
batch.update(circuitDoc.ref, {
|
|
717
|
-
avgTimings: {
|
|
718
|
-
contributionComputation: isContributionValid
|
|
719
|
-
? newAvgContributionComputationTime
|
|
720
|
-
: avgContributionComputationTime,
|
|
721
|
-
fullContribution: isContributionValid ? newAvgFullContributionTime : avgFullContributionTime,
|
|
722
|
-
verifyCloudFunction: isContributionValid
|
|
723
|
-
? newAvgVerifyCloudFunctionTime
|
|
724
|
-
: avgVerifyCloudFunctionTime
|
|
725
|
-
},
|
|
726
|
-
waitingQueue: {
|
|
727
|
-
...updatedWaitingQueue,
|
|
728
|
-
completedContributions: isContributionValid
|
|
729
|
-
? completedContributions + 1
|
|
730
|
-
: completedContributions,
|
|
731
|
-
failedContributions: isContributionValid ? failedContributions : failedContributions + 1
|
|
732
|
-
},
|
|
733
|
-
lastUpdated: getCurrentServerTimestampInMillis()
|
|
734
|
-
})
|
|
735
789
|
}
|
|
736
790
|
|
|
737
|
-
// Step (
|
|
738
|
-
|
|
791
|
+
// Step (1).
|
|
792
|
+
if (isContributing || isFinalizing) {
|
|
793
|
+
// Prepare timer.
|
|
794
|
+
verificationTaskTimer.start()
|
|
739
795
|
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
} of circuit ${circuitId} (ceremony ${ceremonyId}) has been verified as ${
|
|
744
|
-
isContributionValid ? "valid" : "invalid"
|
|
745
|
-
} for the participant ${participantDoc.id}`,
|
|
746
|
-
LogLevel.DEBUG
|
|
747
|
-
)
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
// Step (1).
|
|
751
|
-
if (isContributing || isFinalizing) {
|
|
752
|
-
// Prepare timer.
|
|
753
|
-
verificationTaskTimer.start()
|
|
796
|
+
// Step (1.A.3.0).
|
|
797
|
+
if (isUsingVM) {
|
|
798
|
+
printLog(`Starting the VM mechanism`, LogLevel.DEBUG)
|
|
754
799
|
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
printLog(`Starting the VM mechanism`, LogLevel.DEBUG)
|
|
800
|
+
// Prepare for VM execution.
|
|
801
|
+
let isVMRunning = false // true when the VM is up, otherwise false.
|
|
758
802
|
|
|
759
|
-
|
|
760
|
-
|
|
803
|
+
// Step (1.A.3.1).
|
|
804
|
+
await startEC2Instance(ec2, vmInstanceId)
|
|
761
805
|
|
|
762
|
-
|
|
763
|
-
await startEC2Instance(ec2, vmInstanceId)
|
|
806
|
+
await sleep(60000) // nb. wait for VM startup (1 mins + retry).
|
|
764
807
|
|
|
765
|
-
|
|
808
|
+
// Check if the startup is running.
|
|
809
|
+
isVMRunning = await checkIfVMRunning(ec2, vmInstanceId)
|
|
766
810
|
|
|
767
|
-
|
|
768
|
-
isVMRunning = await checkIfVMRunning(ec2, vmInstanceId)
|
|
811
|
+
printLog(`VM running: ${isVMRunning}`, LogLevel.DEBUG)
|
|
769
812
|
|
|
770
|
-
|
|
813
|
+
// Step (1.A.3.2).
|
|
814
|
+
// Prepare.
|
|
815
|
+
const verificationCommand = vmContributionVerificationCommand(
|
|
816
|
+
bucketName,
|
|
817
|
+
lastZkeyStoragePath,
|
|
818
|
+
verificationTranscriptStoragePathAndFilename
|
|
819
|
+
)
|
|
771
820
|
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
const verificationCommand = vmContributionVerificationCommand(
|
|
775
|
-
bucketName,
|
|
776
|
-
lastZkeyStoragePath,
|
|
777
|
-
verificationTranscriptStoragePathAndFilename
|
|
778
|
-
)
|
|
821
|
+
// Run.
|
|
822
|
+
commandId = await runCommandUsingSSM(ssm, vmInstanceId, verificationCommand)
|
|
779
823
|
|
|
780
|
-
|
|
781
|
-
commandId = await runCommandUsingSSM(ssm, vmInstanceId, verificationCommand)
|
|
824
|
+
printLog(`Starting the execution of command ${commandId}`, LogLevel.DEBUG)
|
|
782
825
|
|
|
783
|
-
|
|
826
|
+
// Step (1.A.3.3).
|
|
827
|
+
return await waitForVMCommandExecution(ssm, vmInstanceId, commandId)
|
|
828
|
+
.then(async () => {
|
|
829
|
+
// Command execution successfully completed.
|
|
830
|
+
printLog(`Command ${commandId} execution has been successfully completed`, LogLevel.DEBUG)
|
|
831
|
+
await completeVerification()
|
|
832
|
+
})
|
|
833
|
+
.catch((error: any) => {
|
|
834
|
+
// Command execution aborted.
|
|
835
|
+
printLog(`Command ${commandId} execution has been aborted - Error ${error}`, LogLevel.WARN)
|
|
784
836
|
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
)
|
|
789
|
-
.then(async () => {
|
|
790
|
-
// Command execution successfully completed.
|
|
791
|
-
printLog(`Command ${commandId} execution has been successfully completed`, LogLevel.DEBUG)
|
|
792
|
-
await completeVerification()
|
|
793
|
-
})
|
|
794
|
-
.catch((error: any) => {
|
|
795
|
-
// Command execution aborted.
|
|
796
|
-
printLog(`Command ${commandId} execution has been aborted - Error ${error}`, LogLevel.DEBUG)
|
|
837
|
+
logAndThrowError(COMMON_ERRORS.CM_INVALID_COMMAND_EXECUTION)
|
|
838
|
+
})
|
|
839
|
+
}
|
|
797
840
|
|
|
798
|
-
logAndThrowError(COMMON_ERRORS.CM_INVALID_COMMAND_EXECUTION)
|
|
799
|
-
})
|
|
800
|
-
} else {
|
|
801
841
|
// CF approach.
|
|
802
842
|
printLog(`CF mechanism`, LogLevel.DEBUG)
|
|
803
843
|
|
|
804
844
|
const potStoragePath = getPotStorageFilePath(files.potFilename)
|
|
805
845
|
const firstZkeyStoragePath = getZkeyStorageFilePath(prefix, `${prefix}_${genesisZkeyIndex}.zkey`)
|
|
846
|
+
printLog(`pot file: ${potStoragePath}`, LogLevel.DEBUG)
|
|
847
|
+
printLog(`zkey file: ${firstZkeyStoragePath}`, LogLevel.DEBUG)
|
|
806
848
|
// Prepare temporary file paths.
|
|
807
849
|
// (nb. these are needed to download the necessary artifacts for verification from AWS S3).
|
|
808
850
|
verificationTranscriptTemporaryLocalPath = createTemporaryLocalPath(
|
|
@@ -811,6 +853,9 @@ export const verifycontribution = functionsV2.https.onCall(
|
|
|
811
853
|
const potTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}.pot`)
|
|
812
854
|
const firstZkeyTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}_genesis.zkey`)
|
|
813
855
|
const lastZkeyTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}_last.zkey`)
|
|
856
|
+
printLog(`pot file: ${potTempFilePath}`, LogLevel.DEBUG)
|
|
857
|
+
printLog(`firstZkey file: ${firstZkeyTempFilePath}`, LogLevel.DEBUG)
|
|
858
|
+
printLog(`last zkey file: ${lastZkeyTempFilePath}`, LogLevel.DEBUG)
|
|
814
859
|
|
|
815
860
|
// Create and populate transcript.
|
|
816
861
|
const transcriptLogger = createCustomLoggerForFile(verificationTranscriptTemporaryLocalPath)
|
|
@@ -834,7 +879,9 @@ export const verifycontribution = functionsV2.https.onCall(
|
|
|
834
879
|
lastZkeyTempFilePath,
|
|
835
880
|
transcriptLogger
|
|
836
881
|
)
|
|
837
|
-
|
|
882
|
+
|
|
883
|
+
await dumpLog(verificationTranscriptTemporaryLocalPath)
|
|
884
|
+
|
|
838
885
|
// Compute contribution hash.
|
|
839
886
|
lastZkeyBlake2bHash = await blake512FromPath(lastZkeyTempFilePath)
|
|
840
887
|
|
|
@@ -846,10 +893,15 @@ export const verifycontribution = functionsV2.https.onCall(
|
|
|
846
893
|
fs.unlinkSync(lastZkeyTempFilePath)
|
|
847
894
|
} catch (error: any) {
|
|
848
895
|
printLog(`Error while unlinking temporary files - Error ${error}`, LogLevel.WARN)
|
|
849
|
-
}
|
|
896
|
+
}
|
|
850
897
|
|
|
851
898
|
await completeVerification()
|
|
852
899
|
}
|
|
900
|
+
|
|
901
|
+
return null
|
|
902
|
+
} catch (error: any) {
|
|
903
|
+
logAndThrowError(makeError("unknown", error))
|
|
904
|
+
return null
|
|
853
905
|
}
|
|
854
906
|
}
|
|
855
907
|
)
|
|
@@ -860,9 +912,9 @@ export const verifycontribution = functionsV2.https.onCall(
|
|
|
860
912
|
* this does not happen if the participant is actually the coordinator who is finalizing the ceremony.
|
|
861
913
|
*/
|
|
862
914
|
export const refreshParticipantAfterContributionVerification = functionsV1
|
|
863
|
-
.region(
|
|
915
|
+
.region("europe-west1")
|
|
864
916
|
.runWith({
|
|
865
|
-
memory: "
|
|
917
|
+
memory: "1GB"
|
|
866
918
|
})
|
|
867
919
|
.firestore.document(
|
|
868
920
|
`/${commonTerms.collections.ceremonies.name}/{ceremony}/${commonTerms.collections.circuits.name}/{circuit}/${commonTerms.collections.contributions.name}/{contributions}`
|
|
@@ -932,8 +984,8 @@ export const refreshParticipantAfterContributionVerification = functionsV1
|
|
|
932
984
|
await batch.commit()
|
|
933
985
|
|
|
934
986
|
printLog(
|
|
935
|
-
`Participant ${participantId} refreshed after contribution ${createdContribution.id} - The participant was finalizing the ceremony ${isFinalizing}`,
|
|
936
|
-
LogLevel.
|
|
987
|
+
`Participant ${participantId} refreshed after contribution ${createdContribution.id} - The participant was finalizing the ceremony? ${isFinalizing}`,
|
|
988
|
+
LogLevel.INFO
|
|
937
989
|
)
|
|
938
990
|
})
|
|
939
991
|
|
|
@@ -943,9 +995,9 @@ export const refreshParticipantAfterContributionVerification = functionsV1
|
|
|
943
995
|
* and verification key extracted from the circuit final contribution (as part of the ceremony finalization process).
|
|
944
996
|
*/
|
|
945
997
|
export const finalizeCircuit = functionsV1
|
|
946
|
-
.region(
|
|
998
|
+
.region("europe-west1")
|
|
947
999
|
.runWith({
|
|
948
|
-
memory: "
|
|
1000
|
+
memory: "1GB"
|
|
949
1001
|
})
|
|
950
1002
|
.https.onCall(async (data: FinalizeCircuitData, context: functionsV1.https.CallableContext) => {
|
|
951
1003
|
if (!context.auth || !context.auth.token.coordinator) logAndThrowError(COMMON_ERRORS.CM_NOT_COORDINATOR_ROLE)
|