@devtion/backend 0.0.0-3df1645 → 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 +2 -2
- package/dist/src/functions/index.js +627 -345
- package/dist/src/functions/index.mjs +628 -348
- 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 +2 -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 +11 -7
- package/src/functions/circuit.ts +414 -384
- package/src/functions/index.ts +2 -0
- package/src/functions/participant.ts +8 -8
- package/src/functions/siwe.ts +77 -0
- package/src/functions/storage.ts +7 -6
- package/src/functions/timeout.ts +14 -13
- package/src/functions/user.ts +6 -5
- package/src/lib/errors.ts +6 -1
- package/src/lib/services.ts +36 -0
- package/src/lib/utils.ts +8 -6
- 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,8 +59,6 @@ import {
|
|
|
57
59
|
sleep,
|
|
58
60
|
uploadFileToBucket
|
|
59
61
|
} from "../lib/utils"
|
|
60
|
-
import { EC2Client } from "@aws-sdk/client-ec2"
|
|
61
|
-
import { HttpsError } from "firebase-functions/v2/https"
|
|
62
62
|
|
|
63
63
|
dotenv.config()
|
|
64
64
|
|
|
@@ -115,7 +115,7 @@ const coordinate = async (
|
|
|
115
115
|
if (isSingleParticipantCoordination) {
|
|
116
116
|
// Scenario (A).
|
|
117
117
|
if (emptyWaitingQueue) {
|
|
118
|
-
printLog(`Coordinate - executing scenario A - emptyWaitingQueue`, LogLevel.
|
|
118
|
+
printLog(`Coordinate - executing scenario A - emptyWaitingQueue`, LogLevel.INFO)
|
|
119
119
|
|
|
120
120
|
// Update.
|
|
121
121
|
newCurrentContributorId = participant.id
|
|
@@ -127,7 +127,7 @@ const coordinate = async (
|
|
|
127
127
|
else if (participantResumingAfterTimeoutExpiration) {
|
|
128
128
|
printLog(
|
|
129
129
|
`Coordinate - executing scenario A - single - participantResumingAfterTimeoutExpiration`,
|
|
130
|
-
LogLevel.
|
|
130
|
+
LogLevel.INFO
|
|
131
131
|
)
|
|
132
132
|
|
|
133
133
|
newParticipantStatus = ParticipantStatus.CONTRIBUTING
|
|
@@ -136,7 +136,7 @@ const coordinate = async (
|
|
|
136
136
|
}
|
|
137
137
|
// Scenario (B).
|
|
138
138
|
else if (participantIsNotCurrentContributor) {
|
|
139
|
-
printLog(`Coordinate - executing scenario B - single - participantIsNotCurrentContributor`, LogLevel.
|
|
139
|
+
printLog(`Coordinate - executing scenario B - single - participantIsNotCurrentContributor`, LogLevel.INFO)
|
|
140
140
|
|
|
141
141
|
newCurrentContributorId = currentContributor
|
|
142
142
|
newParticipantStatus = ParticipantStatus.WAITING
|
|
@@ -160,7 +160,7 @@ const coordinate = async (
|
|
|
160
160
|
} else if (participantIsCurrentContributor && participantCompletedOneOrAllContributions && !!ceremonyId) {
|
|
161
161
|
printLog(
|
|
162
162
|
`Coordinate - executing scenario C - multi - participantIsCurrentContributor && participantCompletedOneOrAllContributions`,
|
|
163
|
-
LogLevel.
|
|
163
|
+
LogLevel.INFO
|
|
164
164
|
)
|
|
165
165
|
|
|
166
166
|
newParticipantStatus = ParticipantStatus.CONTRIBUTING
|
|
@@ -190,7 +190,7 @@ const coordinate = async (
|
|
|
190
190
|
|
|
191
191
|
printLog(
|
|
192
192
|
`Participant ${newCurrentContributorId} is the new current contributor for circuit ${circuit.id}`,
|
|
193
|
-
LogLevel.
|
|
193
|
+
LogLevel.INFO
|
|
194
194
|
)
|
|
195
195
|
}
|
|
196
196
|
}
|
|
@@ -276,8 +276,8 @@ const waitForVMCommandExecution = (ssm: SSMClient, vmInstanceId: string, command
|
|
|
276
276
|
// if it errors out, let's just log it as a warning so the coordinator is aware
|
|
277
277
|
try {
|
|
278
278
|
await stopEC2Instance(ec2, vmInstanceId)
|
|
279
|
-
} catch (
|
|
280
|
-
printLog(`Error while stopping VM instance ${vmInstanceId} - Error ${
|
|
279
|
+
} catch (stopError: any) {
|
|
280
|
+
printLog(`Error while stopping VM instance ${vmInstanceId} - Error ${stopError}`, LogLevel.WARN)
|
|
281
281
|
}
|
|
282
282
|
|
|
283
283
|
if (!error.toString().includes(commandId)) logAndThrowError(COMMON_ERRORS.CM_INVALID_COMMAND_EXECUTION)
|
|
@@ -312,7 +312,7 @@ const waitForVMCommandExecution = (ssm: SSMClient, vmInstanceId: string, command
|
|
|
312
312
|
export const coordinateCeremonyParticipant = functionsV1
|
|
313
313
|
.region("europe-west1")
|
|
314
314
|
.runWith({
|
|
315
|
-
memory: "
|
|
315
|
+
memory: "1GB"
|
|
316
316
|
})
|
|
317
317
|
.firestore.document(
|
|
318
318
|
`${commonTerms.collections.ceremonies.name}/{ceremonyId}/${commonTerms.collections.participants.name}/{participantId}`
|
|
@@ -346,10 +346,10 @@ export const coordinateCeremonyParticipant = functionsV1
|
|
|
346
346
|
contributionStep: changedContributionStep
|
|
347
347
|
} = changedParticipant.data()!
|
|
348
348
|
|
|
349
|
-
printLog(`Coordinate participant ${exParticipant.id} for ceremony ${ceremonyId}`, LogLevel.
|
|
349
|
+
printLog(`Coordinate participant ${exParticipant.id} for ceremony ${ceremonyId}`, LogLevel.INFO)
|
|
350
350
|
printLog(
|
|
351
351
|
`Participant status: ${prevStatus} => ${changedStatus} - Participant contribution step: ${prevContributionStep} => ${changedContributionStep}`,
|
|
352
|
-
LogLevel.
|
|
352
|
+
LogLevel.INFO
|
|
353
353
|
)
|
|
354
354
|
|
|
355
355
|
// Define pre-conditions.
|
|
@@ -370,8 +370,8 @@ export const coordinateCeremonyParticipant = functionsV1
|
|
|
370
370
|
|
|
371
371
|
const participantCompletedContribution =
|
|
372
372
|
prevContributionProgress === changedContributionProgress &&
|
|
373
|
-
prevStatus === ParticipantStatus.CONTRIBUTING
|
|
374
|
-
|
|
373
|
+
(prevStatus === ParticipantStatus.CONTRIBUTING ||
|
|
374
|
+
prevContributionStep === ParticipantContributionStep.VERIFYING) &&
|
|
375
375
|
changedStatus === ParticipantStatus.CONTRIBUTED &&
|
|
376
376
|
changedContributionStep === ParticipantContributionStep.COMPLETED
|
|
377
377
|
|
|
@@ -384,7 +384,7 @@ export const coordinateCeremonyParticipant = functionsV1
|
|
|
384
384
|
// Step (2.A).
|
|
385
385
|
printLog(
|
|
386
386
|
`Participant is ready for first contribution (${participantReadyForFirstContribution}) or for the next contribution (${participantReadyForNextContribution}) or is resuming after a timeout expiration (${participantResumingContributionAfterTimeout})`,
|
|
387
|
-
LogLevel.
|
|
387
|
+
LogLevel.INFO
|
|
388
388
|
)
|
|
389
389
|
|
|
390
390
|
// Get the circuit.
|
|
@@ -398,7 +398,7 @@ export const coordinateCeremonyParticipant = functionsV1
|
|
|
398
398
|
// Step (2.B).
|
|
399
399
|
printLog(
|
|
400
400
|
`Participant completed a contribution (${participantCompletedContribution}) or every contribution for each circuit (${participantCompletedEveryCircuitContribution})`,
|
|
401
|
-
LogLevel.
|
|
401
|
+
LogLevel.INFO
|
|
402
402
|
)
|
|
403
403
|
|
|
404
404
|
// Get the circuit.
|
|
@@ -428,10 +428,9 @@ const checkIfVMRunning = async (ec2: EC2Client, vmInstanceId: string, attempts =
|
|
|
428
428
|
|
|
429
429
|
if (!isVMRunning) {
|
|
430
430
|
printLog(`VM not running, ${attempts - 1} attempts remaining. Retrying in 1 minute...`, LogLevel.DEBUG)
|
|
431
|
-
return
|
|
432
|
-
} else {
|
|
433
|
-
return true
|
|
431
|
+
return checkIfVMRunning(ec2, vmInstanceId, attempts - 1)
|
|
434
432
|
}
|
|
433
|
+
return true
|
|
435
434
|
}
|
|
436
435
|
|
|
437
436
|
/**
|
|
@@ -461,417 +460,448 @@ const checkIfVMRunning = async (ec2: EC2Client, vmInstanceId: string, attempts =
|
|
|
461
460
|
* 2) Send all updates atomically to the Firestore database.
|
|
462
461
|
*/
|
|
463
462
|
export const verifycontribution = functionsV2.https.onCall(
|
|
464
|
-
{ memory: "
|
|
463
|
+
{ memory: "32GiB", timeoutSeconds: 3600, region: "europe-west1", cpu: 8 },
|
|
465
464
|
async (request: functionsV2.https.CallableRequest<VerifyContributionData>): Promise<any> => {
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
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
|
-
// Step (0).
|
|
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)
|
|
485
476
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
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
|
|
489
524
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
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
|
+
)
|
|
494
566
|
|
|
495
|
-
|
|
496
|
-
const { ceremonyId, circuitId, contributorOrCoordinatorIdentifier, bucketName } = request.data
|
|
497
|
-
const userId = request.auth?.uid
|
|
567
|
+
const verificationTaskTimer = new Timer({ label: `${ceremonyId}-${circuitId}-${participantDoc.id}` })
|
|
498
568
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
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
|
+
}
|
|
503
578
|
|
|
504
|
-
|
|
505
|
-
|
|
579
|
+
const completeVerification = async () => {
|
|
580
|
+
// Stop verification task timer.
|
|
581
|
+
printLog("Completing verification", LogLevel.DEBUG)
|
|
582
|
+
verificationTaskTimer.stop()
|
|
583
|
+
verifyCloudFunctionExecutionTime = verificationTaskTimer.ms()
|
|
506
584
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
const {
|
|
513
|
-
contributionComputation: avgContributionComputationTime,
|
|
514
|
-
fullContribution: avgFullContributionTime,
|
|
515
|
-
verifyCloudFunction: avgVerifyCloudFunctionTime
|
|
516
|
-
} = avgTimings
|
|
517
|
-
const { cfOrVm, vm } = verification
|
|
518
|
-
// we might not have it if the circuit is not using VM.
|
|
519
|
-
let vmInstanceId: string = ""
|
|
520
|
-
if (vm) vmInstanceId = vm.vmInstanceId
|
|
585
|
+
if (isUsingVM) {
|
|
586
|
+
// Create temporary path.
|
|
587
|
+
verificationTranscriptTemporaryLocalPath = createTemporaryLocalPath(
|
|
588
|
+
`${circuitId}_${participantDoc.id}.log`
|
|
589
|
+
)
|
|
521
590
|
|
|
522
|
-
|
|
523
|
-
const isFinalizing = state === CeremonyState.CLOSED && request.auth && request.auth.token.coordinator // true only when the coordinator verifies the final contributions.
|
|
524
|
-
const isContributing = status === ParticipantStatus.CONTRIBUTING
|
|
525
|
-
const isUsingVM = cfOrVm === CircuitContributionVerificationMechanism.VM && !!vmInstanceId
|
|
526
|
-
|
|
527
|
-
// Prepare state.
|
|
528
|
-
let isContributionValid = false
|
|
529
|
-
let verifyCloudFunctionExecutionTime = 0 // time spent while executing the verify contribution cloud function.
|
|
530
|
-
let verifyCloudFunctionTime = 0 // time spent while executing the core business logic of this cloud function.
|
|
531
|
-
let fullContributionTime = 0 // time spent while doing non-verification contributions tasks (download, compute, upload).
|
|
532
|
-
let contributionComputationTime = 0 // time spent while computing the contribution.
|
|
533
|
-
let lastZkeyBlake2bHash: string = "" // the Blake2B hash of the last zKey.
|
|
534
|
-
let verificationTranscriptTemporaryLocalPath: string = "" // the local temporary path for the verification transcript.
|
|
535
|
-
let transcriptBlake2bHash: string = "" // the Blake2B hash of the verification transcript.
|
|
536
|
-
let commandId: string = "" // the unique identifier of the VM command.
|
|
537
|
-
|
|
538
|
-
// Derive necessary data.
|
|
539
|
-
const lastZkeyIndex = formatZkeyIndex(completedContributions + 1)
|
|
540
|
-
const verificationTranscriptCompleteFilename = `${prefix}_${
|
|
541
|
-
isFinalizing
|
|
542
|
-
? `${contributorOrCoordinatorIdentifier}_${finalContributionIndex}_verification_transcript.log`
|
|
543
|
-
: `${lastZkeyIndex}_${contributorOrCoordinatorIdentifier}_verification_transcript.log`
|
|
544
|
-
}`
|
|
545
|
-
|
|
546
|
-
const lastZkeyFilename = `${prefix}_${isFinalizing ? finalContributionIndex : lastZkeyIndex}.zkey`
|
|
547
|
-
|
|
548
|
-
// Prepare state for VM verification (if needed).
|
|
549
|
-
const ec2 = await createEC2Client()
|
|
550
|
-
const ssm = await createSSMClient()
|
|
551
|
-
|
|
552
|
-
// Step (1.A.1).
|
|
553
|
-
// Get storage paths.
|
|
554
|
-
const verificationTranscriptStoragePathAndFilename = getTranscriptStorageFilePath(
|
|
555
|
-
prefix,
|
|
556
|
-
verificationTranscriptCompleteFilename
|
|
557
|
-
)
|
|
558
|
-
// the zKey storage path is required to be sent to the VM api
|
|
559
|
-
const lastZkeyStoragePath = getZkeyStorageFilePath(
|
|
560
|
-
prefix,
|
|
561
|
-
`${prefix}_${isFinalizing ? finalContributionIndex : lastZkeyIndex}.zkey`
|
|
562
|
-
)
|
|
591
|
+
await sleep(1000) // wait 1s for file creation.
|
|
563
592
|
|
|
564
|
-
|
|
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
|
+
)
|
|
565
600
|
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
printLog("Completing verification", LogLevel.DEBUG)
|
|
569
|
-
verificationTaskTimer.stop()
|
|
570
|
-
verifyCloudFunctionExecutionTime = verificationTaskTimer.ms()
|
|
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
|
-
|
|
573
|
-
// Create temporary path.
|
|
574
|
-
verificationTranscriptTemporaryLocalPath = createTemporaryLocalPath(
|
|
575
|
-
`${circuitId}_${participantDoc.id}.log`
|
|
576
|
-
)
|
|
604
|
+
if (content.includes("ZKey Ok!")) isContributionValid = true
|
|
577
605
|
|
|
578
|
-
|
|
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, "")
|
|
579
610
|
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
bucketName,
|
|
584
|
-
verificationTranscriptStoragePathAndFilename,
|
|
585
|
-
verificationTranscriptTemporaryLocalPath
|
|
586
|
-
)
|
|
611
|
+
fs.writeFileSync(verificationTranscriptTemporaryLocalPath, updated)
|
|
612
|
+
}
|
|
613
|
+
}
|
|
587
614
|
|
|
588
|
-
|
|
589
|
-
const content = fs.readFileSync(verificationTranscriptTemporaryLocalPath, "utf-8")
|
|
615
|
+
printLog(`The contribution has been verified - Result ${isContributionValid}`, LogLevel.DEBUG)
|
|
590
616
|
|
|
591
|
-
|
|
617
|
+
// Create a new contribution document.
|
|
618
|
+
const contributionDoc = await firestore
|
|
619
|
+
.collection(getContributionsCollectionPath(ceremonyId, circuitId))
|
|
620
|
+
.doc()
|
|
621
|
+
.get()
|
|
592
622
|
|
|
593
|
-
//
|
|
623
|
+
// Step (1.A.4).
|
|
594
624
|
if (isContributionValid) {
|
|
595
|
-
//
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
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
|
+
}
|
|
609
655
|
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
// Sleep ~3 seconds to wait for verification transcription.
|
|
613
|
-
await sleep(3000)
|
|
656
|
+
// Compute verification transcript hash.
|
|
657
|
+
transcriptBlake2bHash = await blake512FromPath(verificationTranscriptTemporaryLocalPath)
|
|
614
658
|
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
// Retrieve the contribution hash from the command output.
|
|
618
|
-
lastZkeyBlake2bHash = await retrieveCommandOutput(ssm, vmInstanceId, commandId)
|
|
659
|
+
// Free resources by unlinking transcript temporary file.
|
|
660
|
+
fs.unlinkSync(verificationTranscriptTemporaryLocalPath)
|
|
619
661
|
|
|
620
|
-
|
|
621
|
-
const
|
|
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
|
|
666
|
+
)
|
|
622
667
|
|
|
623
|
-
|
|
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
|
+
})
|
|
624
697
|
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
bucketName,
|
|
628
|
-
verificationTranscriptStoragePathAndFilename,
|
|
629
|
-
verificationTranscriptTemporaryLocalPath,
|
|
630
|
-
true
|
|
631
|
-
)
|
|
698
|
+
verifyContributionTimer.stop()
|
|
699
|
+
verifyCloudFunctionTime = verifyContributionTimer.ms()
|
|
632
700
|
} else {
|
|
633
|
-
//
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
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
|
+
})
|
|
641
719
|
}
|
|
642
720
|
|
|
643
|
-
//
|
|
644
|
-
|
|
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
|
+
}
|
|
645
777
|
|
|
646
|
-
//
|
|
647
|
-
|
|
778
|
+
// Step (2).
|
|
779
|
+
await batch.commit()
|
|
648
780
|
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
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
|
|
653
788
|
)
|
|
654
|
-
|
|
655
|
-
/// @dev (there must be only one contribution with an empty 'doc' field).
|
|
656
|
-
if (participantContributions.length !== 1)
|
|
657
|
-
logAndThrowError(SPECIFIC_ERRORS.SE_VERIFICATION_NO_PARTICIPANT_CONTRIBUTION_DATA)
|
|
658
|
-
|
|
659
|
-
// Get contribution computation time.
|
|
660
|
-
contributionComputationTime = contributions.at(0).computationTime
|
|
661
|
-
|
|
662
|
-
// Step (1.A.4.A.2).
|
|
663
|
-
batch.create(contributionDoc.ref, {
|
|
664
|
-
participantId: participantDoc.id,
|
|
665
|
-
contributionComputationTime,
|
|
666
|
-
verificationComputationTime: verifyCloudFunctionExecutionTime,
|
|
667
|
-
zkeyIndex: isFinalizing ? finalContributionIndex : lastZkeyIndex,
|
|
668
|
-
files: {
|
|
669
|
-
transcriptFilename: verificationTranscriptCompleteFilename,
|
|
670
|
-
lastZkeyFilename,
|
|
671
|
-
transcriptStoragePath: verificationTranscriptStoragePathAndFilename,
|
|
672
|
-
lastZkeyStoragePath,
|
|
673
|
-
transcriptBlake2bHash,
|
|
674
|
-
lastZkeyBlake2bHash
|
|
675
|
-
},
|
|
676
|
-
verificationSoftware: {
|
|
677
|
-
name: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_NAME),
|
|
678
|
-
version: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_VERSION),
|
|
679
|
-
commitHash: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_COMMIT_HASH)
|
|
680
|
-
},
|
|
681
|
-
valid: isContributionValid,
|
|
682
|
-
lastUpdated: getCurrentServerTimestampInMillis()
|
|
683
|
-
})
|
|
684
|
-
|
|
685
|
-
verifyContributionTimer.stop()
|
|
686
|
-
verifyCloudFunctionTime = verifyContributionTimer.ms()
|
|
687
|
-
} else {
|
|
688
|
-
// Step (1.A.4.B).
|
|
689
|
-
|
|
690
|
-
// Free-up storage by deleting invalid contribution.
|
|
691
|
-
await deleteObject(bucketName, lastZkeyStoragePath)
|
|
692
|
-
|
|
693
|
-
// Step (1.A.4.B.1).
|
|
694
|
-
batch.create(contributionDoc.ref, {
|
|
695
|
-
participantId: participantDoc.id,
|
|
696
|
-
verificationComputationTime: verifyCloudFunctionExecutionTime,
|
|
697
|
-
zkeyIndex: isFinalizing ? finalContributionIndex : lastZkeyIndex,
|
|
698
|
-
verificationSoftware: {
|
|
699
|
-
name: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_NAME),
|
|
700
|
-
version: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_VERSION),
|
|
701
|
-
commitHash: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_COMMIT_HASH)
|
|
702
|
-
},
|
|
703
|
-
valid: isContributionValid,
|
|
704
|
-
lastUpdated: getCurrentServerTimestampInMillis()
|
|
705
|
-
})
|
|
706
789
|
}
|
|
707
790
|
|
|
708
|
-
//
|
|
709
|
-
if (
|
|
710
|
-
//
|
|
711
|
-
|
|
712
|
-
// verification was valid, and inform the coordinator
|
|
713
|
-
try {
|
|
714
|
-
await stopEC2Instance(ec2, vmInstanceId)
|
|
715
|
-
} catch (error: any) {
|
|
716
|
-
printLog(`Error while stopping VM instance ${vmInstanceId} - Error ${error}`, LogLevel.WARN)
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
// Step (1.A.4.C)
|
|
720
|
-
if (!isFinalizing) {
|
|
721
|
-
// Step (1.A.4.C.1)
|
|
722
|
-
// Compute new average contribution/verification time.
|
|
723
|
-
fullContributionTime = Number(verificationStartedAt) - Number(contributionStartedAt)
|
|
724
|
-
|
|
725
|
-
const newAvgContributionComputationTime =
|
|
726
|
-
avgContributionComputationTime > 0
|
|
727
|
-
? (avgContributionComputationTime + contributionComputationTime) / 2
|
|
728
|
-
: contributionComputationTime
|
|
729
|
-
const newAvgFullContributionTime =
|
|
730
|
-
avgFullContributionTime > 0
|
|
731
|
-
? (avgFullContributionTime + fullContributionTime) / 2
|
|
732
|
-
: fullContributionTime
|
|
733
|
-
const newAvgVerifyCloudFunctionTime =
|
|
734
|
-
avgVerifyCloudFunctionTime > 0
|
|
735
|
-
? (avgVerifyCloudFunctionTime + verifyCloudFunctionTime) / 2
|
|
736
|
-
: verifyCloudFunctionTime
|
|
737
|
-
|
|
738
|
-
// Prepare tx to update circuit average contribution/verification time.
|
|
739
|
-
const updatedCircuitDoc = await getDocumentById(getCircuitsCollectionPath(ceremonyId), circuitId)
|
|
740
|
-
const { waitingQueue: updatedWaitingQueue } = updatedCircuitDoc.data()!
|
|
741
|
-
/// @dev this must happen only for valid contributions.
|
|
742
|
-
batch.update(circuitDoc.ref, {
|
|
743
|
-
avgTimings: {
|
|
744
|
-
contributionComputation: isContributionValid
|
|
745
|
-
? newAvgContributionComputationTime
|
|
746
|
-
: avgContributionComputationTime,
|
|
747
|
-
fullContribution: isContributionValid ? newAvgFullContributionTime : avgFullContributionTime,
|
|
748
|
-
verifyCloudFunction: isContributionValid
|
|
749
|
-
? newAvgVerifyCloudFunctionTime
|
|
750
|
-
: avgVerifyCloudFunctionTime
|
|
751
|
-
},
|
|
752
|
-
waitingQueue: {
|
|
753
|
-
...updatedWaitingQueue,
|
|
754
|
-
completedContributions: isContributionValid
|
|
755
|
-
? completedContributions + 1
|
|
756
|
-
: completedContributions,
|
|
757
|
-
failedContributions: isContributionValid ? failedContributions : failedContributions + 1
|
|
758
|
-
},
|
|
759
|
-
lastUpdated: getCurrentServerTimestampInMillis()
|
|
760
|
-
})
|
|
761
|
-
}
|
|
791
|
+
// Step (1).
|
|
792
|
+
if (isContributing || isFinalizing) {
|
|
793
|
+
// Prepare timer.
|
|
794
|
+
verificationTaskTimer.start()
|
|
762
795
|
|
|
763
|
-
|
|
764
|
-
|
|
796
|
+
// Step (1.A.3.0).
|
|
797
|
+
if (isUsingVM) {
|
|
798
|
+
printLog(`Starting the VM mechanism`, LogLevel.DEBUG)
|
|
765
799
|
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
isFinalizing ? finalContributionIndex : lastZkeyIndex
|
|
769
|
-
} of circuit ${circuitId} (ceremony ${ceremonyId}) has been verified as ${
|
|
770
|
-
isContributionValid ? "valid" : "invalid"
|
|
771
|
-
} for the participant ${participantDoc.id}`,
|
|
772
|
-
LogLevel.DEBUG
|
|
773
|
-
)
|
|
774
|
-
}
|
|
800
|
+
// Prepare for VM execution.
|
|
801
|
+
let isVMRunning = false // true when the VM is up, otherwise false.
|
|
775
802
|
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
// Prepare timer.
|
|
779
|
-
verificationTaskTimer.start()
|
|
803
|
+
// Step (1.A.3.1).
|
|
804
|
+
await startEC2Instance(ec2, vmInstanceId)
|
|
780
805
|
|
|
781
|
-
|
|
782
|
-
if (isUsingVM) {
|
|
783
|
-
printLog(`Starting the VM mechanism`, LogLevel.DEBUG)
|
|
806
|
+
await sleep(60000) // nb. wait for VM startup (1 mins + retry).
|
|
784
807
|
|
|
785
|
-
|
|
786
|
-
|
|
808
|
+
// Check if the startup is running.
|
|
809
|
+
isVMRunning = await checkIfVMRunning(ec2, vmInstanceId)
|
|
787
810
|
|
|
788
|
-
|
|
789
|
-
await startEC2Instance(ec2, vmInstanceId)
|
|
811
|
+
printLog(`VM running: ${isVMRunning}`, LogLevel.DEBUG)
|
|
790
812
|
|
|
791
|
-
|
|
813
|
+
// Step (1.A.3.2).
|
|
814
|
+
// Prepare.
|
|
815
|
+
const verificationCommand = vmContributionVerificationCommand(
|
|
816
|
+
bucketName,
|
|
817
|
+
lastZkeyStoragePath,
|
|
818
|
+
verificationTranscriptStoragePathAndFilename
|
|
819
|
+
)
|
|
792
820
|
|
|
793
|
-
|
|
794
|
-
|
|
821
|
+
// Run.
|
|
822
|
+
commandId = await runCommandUsingSSM(ssm, vmInstanceId, verificationCommand)
|
|
795
823
|
|
|
796
|
-
|
|
824
|
+
printLog(`Starting the execution of command ${commandId}`, LogLevel.DEBUG)
|
|
797
825
|
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
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)
|
|
805
836
|
|
|
806
|
-
|
|
807
|
-
|
|
837
|
+
logAndThrowError(COMMON_ERRORS.CM_INVALID_COMMAND_EXECUTION)
|
|
838
|
+
})
|
|
839
|
+
}
|
|
808
840
|
|
|
809
|
-
|
|
841
|
+
// CF approach.
|
|
842
|
+
printLog(`CF mechanism`, LogLevel.DEBUG)
|
|
810
843
|
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
844
|
+
const potStoragePath = getPotStorageFilePath(files.potFilename)
|
|
845
|
+
const firstZkeyStoragePath = getZkeyStorageFilePath(prefix, `${prefix}_${genesisZkeyIndex}.zkey`)
|
|
846
|
+
printLog(`pot file: ${potStoragePath}`, LogLevel.DEBUG)
|
|
847
|
+
printLog(`zkey file: ${firstZkeyStoragePath}`, LogLevel.DEBUG)
|
|
848
|
+
// Prepare temporary file paths.
|
|
849
|
+
// (nb. these are needed to download the necessary artifacts for verification from AWS S3).
|
|
850
|
+
verificationTranscriptTemporaryLocalPath = createTemporaryLocalPath(
|
|
851
|
+
verificationTranscriptCompleteFilename
|
|
852
|
+
)
|
|
853
|
+
const potTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}.pot`)
|
|
854
|
+
const firstZkeyTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}_genesis.zkey`)
|
|
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)
|
|
859
|
+
|
|
860
|
+
// Create and populate transcript.
|
|
861
|
+
const transcriptLogger = createCustomLoggerForFile(verificationTranscriptTemporaryLocalPath)
|
|
862
|
+
transcriptLogger.info(
|
|
863
|
+
`${
|
|
864
|
+
isFinalizing ? `Final verification` : `Verification`
|
|
865
|
+
} transcript for ${prefix} circuit Phase 2 contribution.\n${
|
|
866
|
+
isFinalizing ? `Coordinator ` : `Contributor # ${Number(lastZkeyIndex)}`
|
|
867
|
+
} (${contributorOrCoordinatorIdentifier})\n`
|
|
868
|
+
)
|
|
821
869
|
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
870
|
+
// Step (1.A.2).
|
|
871
|
+
await downloadArtifactFromS3Bucket(bucketName, potStoragePath, potTempFilePath)
|
|
872
|
+
await downloadArtifactFromS3Bucket(bucketName, firstZkeyStoragePath, firstZkeyTempFilePath)
|
|
873
|
+
await downloadArtifactFromS3Bucket(bucketName, lastZkeyStoragePath, lastZkeyTempFilePath)
|
|
874
|
+
|
|
875
|
+
// Step (1.A.4).
|
|
876
|
+
isContributionValid = await zKey.verifyFromInit(
|
|
877
|
+
firstZkeyTempFilePath,
|
|
878
|
+
potTempFilePath,
|
|
879
|
+
lastZkeyTempFilePath,
|
|
880
|
+
transcriptLogger
|
|
881
|
+
)
|
|
825
882
|
|
|
826
|
-
|
|
827
|
-
printLog(`CF mechanism`, LogLevel.DEBUG)
|
|
828
|
-
|
|
829
|
-
const potStoragePath = getPotStorageFilePath(files.potFilename)
|
|
830
|
-
const firstZkeyStoragePath = getZkeyStorageFilePath(prefix, `${prefix}_${genesisZkeyIndex}.zkey`)
|
|
831
|
-
// Prepare temporary file paths.
|
|
832
|
-
// (nb. these are needed to download the necessary artifacts for verification from AWS S3).
|
|
833
|
-
verificationTranscriptTemporaryLocalPath = createTemporaryLocalPath(verificationTranscriptCompleteFilename)
|
|
834
|
-
const potTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}.pot`)
|
|
835
|
-
const firstZkeyTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}_genesis.zkey`)
|
|
836
|
-
const lastZkeyTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}_last.zkey`)
|
|
837
|
-
|
|
838
|
-
// Create and populate transcript.
|
|
839
|
-
const transcriptLogger = createCustomLoggerForFile(verificationTranscriptTemporaryLocalPath)
|
|
840
|
-
transcriptLogger.info(
|
|
841
|
-
`${
|
|
842
|
-
isFinalizing ? `Final verification` : `Verification`
|
|
843
|
-
} transcript for ${prefix} circuit Phase 2 contribution.\n${
|
|
844
|
-
isFinalizing ? `Coordinator ` : `Contributor # ${Number(lastZkeyIndex)}`
|
|
845
|
-
} (${contributorOrCoordinatorIdentifier})\n`
|
|
846
|
-
)
|
|
883
|
+
await dumpLog(verificationTranscriptTemporaryLocalPath)
|
|
847
884
|
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
await downloadArtifactFromS3Bucket(bucketName, firstZkeyStoragePath, firstZkeyTempFilePath)
|
|
851
|
-
await downloadArtifactFromS3Bucket(bucketName, lastZkeyStoragePath, lastZkeyTempFilePath)
|
|
852
|
-
|
|
853
|
-
// Step (1.A.4).
|
|
854
|
-
isContributionValid = await zKey.verifyFromInit(
|
|
855
|
-
firstZkeyTempFilePath,
|
|
856
|
-
potTempFilePath,
|
|
857
|
-
lastZkeyTempFilePath,
|
|
858
|
-
transcriptLogger
|
|
859
|
-
)
|
|
885
|
+
// Compute contribution hash.
|
|
886
|
+
lastZkeyBlake2bHash = await blake512FromPath(lastZkeyTempFilePath)
|
|
860
887
|
|
|
861
|
-
|
|
862
|
-
|
|
888
|
+
// Free resources by unlinking temporary folders.
|
|
889
|
+
// Do not free-up verification transcript path here.
|
|
890
|
+
try {
|
|
891
|
+
fs.unlinkSync(potTempFilePath)
|
|
892
|
+
fs.unlinkSync(firstZkeyTempFilePath)
|
|
893
|
+
fs.unlinkSync(lastZkeyTempFilePath)
|
|
894
|
+
} catch (error: any) {
|
|
895
|
+
printLog(`Error while unlinking temporary files - Error ${error}`, LogLevel.WARN)
|
|
896
|
+
}
|
|
863
897
|
|
|
864
|
-
|
|
865
|
-
// Do not free-up verification transcript path here.
|
|
866
|
-
try {
|
|
867
|
-
fs.unlinkSync(potTempFilePath)
|
|
868
|
-
fs.unlinkSync(firstZkeyTempFilePath)
|
|
869
|
-
fs.unlinkSync(lastZkeyTempFilePath)
|
|
870
|
-
} catch (error: any) {
|
|
871
|
-
printLog(`Error while unlinking temporary files - Error ${error}`, LogLevel.WARN)
|
|
898
|
+
await completeVerification()
|
|
872
899
|
}
|
|
873
900
|
|
|
874
|
-
|
|
901
|
+
return null
|
|
902
|
+
} catch (error: any) {
|
|
903
|
+
logAndThrowError(makeError("unknown", error))
|
|
904
|
+
return null
|
|
875
905
|
}
|
|
876
906
|
}
|
|
877
907
|
)
|
|
@@ -884,7 +914,7 @@ export const verifycontribution = functionsV2.https.onCall(
|
|
|
884
914
|
export const refreshParticipantAfterContributionVerification = functionsV1
|
|
885
915
|
.region("europe-west1")
|
|
886
916
|
.runWith({
|
|
887
|
-
memory: "
|
|
917
|
+
memory: "1GB"
|
|
888
918
|
})
|
|
889
919
|
.firestore.document(
|
|
890
920
|
`/${commonTerms.collections.ceremonies.name}/{ceremony}/${commonTerms.collections.circuits.name}/{circuit}/${commonTerms.collections.contributions.name}/{contributions}`
|
|
@@ -954,8 +984,8 @@ export const refreshParticipantAfterContributionVerification = functionsV1
|
|
|
954
984
|
await batch.commit()
|
|
955
985
|
|
|
956
986
|
printLog(
|
|
957
|
-
`Participant ${participantId} refreshed after contribution ${createdContribution.id} - The participant was finalizing the ceremony ${isFinalizing}`,
|
|
958
|
-
LogLevel.
|
|
987
|
+
`Participant ${participantId} refreshed after contribution ${createdContribution.id} - The participant was finalizing the ceremony? ${isFinalizing}`,
|
|
988
|
+
LogLevel.INFO
|
|
959
989
|
)
|
|
960
990
|
})
|
|
961
991
|
|
|
@@ -967,7 +997,7 @@ export const refreshParticipantAfterContributionVerification = functionsV1
|
|
|
967
997
|
export const finalizeCircuit = functionsV1
|
|
968
998
|
.region("europe-west1")
|
|
969
999
|
.runWith({
|
|
970
|
-
memory: "
|
|
1000
|
+
memory: "1GB"
|
|
971
1001
|
})
|
|
972
1002
|
.https.onCall(async (data: FinalizeCircuitData, context: functionsV1.https.CallableContext) => {
|
|
973
1003
|
if (!context.auth || !context.auth.token.coordinator) logAndThrowError(COMMON_ERRORS.CM_NOT_COORDINATOR_ROLE)
|