@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.
Files changed (50) hide show
  1. package/README.md +7 -7
  2. package/dist/src/functions/index.js +669 -354
  3. package/dist/src/functions/index.mjs +672 -359
  4. package/dist/src/functions/types/functions/bandada.d.ts +4 -0
  5. package/dist/src/functions/types/functions/bandada.d.ts.map +1 -0
  6. package/dist/{types → src/functions/types}/functions/ceremony.d.ts.map +1 -1
  7. package/dist/{types → src/functions/types}/functions/circuit.d.ts.map +1 -1
  8. package/dist/{types → src/functions/types}/functions/index.d.ts +2 -0
  9. package/dist/{types → src/functions/types}/functions/index.d.ts.map +1 -1
  10. package/dist/src/functions/types/functions/siwe.d.ts +4 -0
  11. package/dist/src/functions/types/functions/siwe.d.ts.map +1 -0
  12. package/dist/{types → src/functions/types}/functions/storage.d.ts.map +1 -1
  13. package/dist/{types → src/functions/types}/functions/timeout.d.ts.map +1 -1
  14. package/dist/{types → src/functions/types}/functions/user.d.ts.map +1 -1
  15. package/dist/{types → src/functions/types}/lib/errors.d.ts +3 -1
  16. package/dist/src/functions/types/lib/errors.d.ts.map +1 -0
  17. package/dist/{types → src/functions/types}/lib/services.d.ts +7 -0
  18. package/dist/src/functions/types/lib/services.d.ts.map +1 -0
  19. package/dist/src/functions/types/lib/utils.d.ts.map +1 -0
  20. package/dist/{types → src/functions/types}/types/index.d.ts +56 -0
  21. package/dist/src/functions/types/types/index.d.ts.map +1 -0
  22. package/package.json +8 -7
  23. package/src/functions/bandada.ts +154 -0
  24. package/src/functions/ceremony.ts +16 -8
  25. package/src/functions/circuit.ts +443 -391
  26. package/src/functions/index.ts +2 -0
  27. package/src/functions/participant.ts +15 -15
  28. package/src/functions/siwe.ts +77 -0
  29. package/src/functions/storage.ts +12 -8
  30. package/src/functions/timeout.ts +14 -13
  31. package/src/functions/user.ts +22 -11
  32. package/src/lib/errors.ts +11 -1
  33. package/src/lib/services.ts +36 -0
  34. package/src/lib/utils.ts +10 -8
  35. package/src/types/declarations.d.ts +1 -0
  36. package/src/types/index.ts +60 -0
  37. package/dist/types/lib/errors.d.ts.map +0 -1
  38. package/dist/types/lib/services.d.ts.map +0 -1
  39. package/dist/types/lib/utils.d.ts.map +0 -1
  40. package/dist/types/types/index.d.ts.map +0 -1
  41. /package/dist/{types → src/functions/types}/functions/ceremony.d.ts +0 -0
  42. /package/dist/{types → src/functions/types}/functions/circuit.d.ts +0 -0
  43. /package/dist/{types → src/functions/types}/functions/participant.d.ts +0 -0
  44. /package/dist/{types → src/functions/types}/functions/participant.d.ts.map +0 -0
  45. /package/dist/{types → src/functions/types}/functions/storage.d.ts +0 -0
  46. /package/dist/{types → src/functions/types}/functions/timeout.d.ts +0 -0
  47. /package/dist/{types → src/functions/types}/functions/user.d.ts +0 -0
  48. /package/dist/{types → src/functions/types}/lib/utils.d.ts +0 -0
  49. /package/dist/{types → src/functions/types}/types/enums.d.ts +0 -0
  50. /package/dist/{types → src/functions/types}/types/enums.d.ts.map +0 -0
@@ -1,6 +1,6 @@
1
1
  /**
2
- * @module @devtion/backend
3
- * @version 1.0.6
2
+ * @module @p0tion/backend
3
+ * @version 1.2.8
4
4
  * @file MPC Phase 2 backend for Firebase services management
5
5
  * @copyright Ethereum Foundation 2022
6
6
  * @license MIT
@@ -9,7 +9,7 @@
9
9
  import admin from 'firebase-admin';
10
10
  import * as functions from 'firebase-functions';
11
11
  import dotenv from 'dotenv';
12
- import { getCircuitsCollectionPath, getTimeoutsCollectionPath, commonTerms, finalContributionIndex, getContributionsCollectionPath, githubReputation, getBucketName, vmBootstrapCommand, vmDependenciesAndCacheArtifactsCommand, vmBootstrapScriptFilename, computeDiskSizeForVM, createEC2Instance, getParticipantsCollectionPath, terminateEC2Instance, formatZkeyIndex, getTranscriptStorageFilePath, getZkeyStorageFilePath, startEC2Instance, vmContributionVerificationCommand, runCommandUsingSSM, getPotStorageFilePath, genesisZkeyIndex, createCustomLoggerForFile, blake512FromPath, getVerificationKeyStorageFilePath, getVerifierContractStorageFilePath, computeSHA256ToHex, retrieveCommandStatus, checkIfRunning, retrieveCommandOutput, stopEC2Instance, verificationKeyAcronym, verifierSmartContractAcronym } from '@devtion/actions';
12
+ import { getCircuitsCollectionPath, getTimeoutsCollectionPath, commonTerms, finalContributionIndex, getContributionsCollectionPath, githubReputation, getBucketName, vmBootstrapCommand, vmDependenciesAndCacheArtifactsCommand, vmBootstrapScriptFilename, computeDiskSizeForVM, createEC2Instance, getParticipantsCollectionPath, terminateEC2Instance, formatZkeyIndex, getTranscriptStorageFilePath, getZkeyStorageFilePath, retrieveCommandOutput, blake512FromPath, stopEC2Instance, startEC2Instance, vmContributionVerificationCommand, runCommandUsingSSM, getPotStorageFilePath, genesisZkeyIndex, createCustomLoggerForFile, getVerificationKeyStorageFilePath, getVerifierContractStorageFilePath, computeSHA256ToHex, checkIfRunning, verificationKeyAcronym, verifierSmartContractAcronym, retrieveCommandStatus } from '@p0tion/actions';
13
13
  import { encode } from 'html-entities';
14
14
  import { Timestamp, FieldValue } from 'firebase-admin/firestore';
15
15
  import { S3Client, GetObjectCommand, PutObjectCommand, DeleteObjectCommand, HeadBucketCommand, CreateBucketCommand, PutPublicAccessBlockCommand, PutBucketCorsCommand, HeadObjectCommand, CreateMultipartUploadCommand, UploadPartCommand, CompleteMultipartUploadCommand } from '@aws-sdk/client-s3';
@@ -19,16 +19,19 @@ import { pipeline } from 'node:stream';
19
19
  import { promisify } from 'node:util';
20
20
  import fs, { readFileSync } from 'fs';
21
21
  import mime from 'mime-types';
22
- import { setTimeout } from 'timers/promises';
22
+ import { setTimeout as setTimeout$1 } from 'timers/promises';
23
23
  import fetch from '@adobe/node-fetch-retry';
24
24
  import path from 'path';
25
25
  import os from 'os';
26
26
  import { SSMClient, CommandInvocationStatus } from '@aws-sdk/client-ssm';
27
27
  import { EC2Client } from '@aws-sdk/client-ec2';
28
+ import ethers from 'ethers';
28
29
  import * as functionsV1 from 'firebase-functions/v1';
29
30
  import * as functionsV2 from 'firebase-functions/v2';
30
31
  import { Timer } from 'timer-node';
31
- import { zKey } from 'snarkjs';
32
+ import { zKey, groth16 } from 'snarkjs';
33
+ import { ApiSdk } from '@bandada/api-sdk';
34
+ import { getAuth } from 'firebase-admin/auth';
32
35
 
33
36
  /**
34
37
  * Log levels.
@@ -49,7 +52,7 @@ var LogLevel;
49
52
  * @notice the set of Firebase Functions status codes. The codes are the same at the
50
53
  * ones exposed by {@link https://github.com/grpc/grpc/blob/master/doc/statuscodes.md | gRPC}.
51
54
  * @param errorCode <FunctionsErrorCode> - the set of possible error codes.
52
- * @param message <string> - the error messge.
55
+ * @param message <string> - the error message.
53
56
  * @param [details] <string> - the details of the error (optional).
54
57
  * @returns <HttpsError>
55
58
  */
@@ -121,7 +124,9 @@ const SPECIFIC_ERRORS = {
121
124
  SE_VM_FAILED_COMMAND_EXECUTION: makeError("failed-precondition", "VM command execution failed", "Please, contact the coordinator if this error persists."),
122
125
  SE_VM_TIMEDOUT_COMMAND_EXECUTION: makeError("deadline-exceeded", "VM command execution took too long and has been timed-out", "Please, contact the coordinator if this error persists."),
123
126
  SE_VM_CANCELLED_COMMAND_EXECUTION: makeError("cancelled", "VM command execution has been cancelled", "Please, contact the coordinator if this error persists."),
124
- SE_VM_DELAYED_COMMAND_EXECUTION: makeError("unavailable", "VM command execution has been delayed since there were no available instance at the moment", "Please, contact the coordinator if this error persists.")
127
+ SE_VM_DELAYED_COMMAND_EXECUTION: makeError("unavailable", "VM command execution has been delayed since there were no available instance at the moment", "Please, contact the coordinator if this error persists."),
128
+ SE_VM_UNKNOWN_COMMAND_STATUS: makeError("unavailable", "VM command execution has failed due to an unknown status code", "Please, contact the coordinator if this error persists."),
129
+ WRONG_BUCKET_NAME: makeError("invalid-argument", "The provided bucket name is not valid.", "Bucket names must be between 3 and 63 characters long, can only contain lowercase letters, numbers, and hyphens, and must start and end with a letter or number.")
125
130
  };
126
131
  /**
127
132
  * A set of common errors.
@@ -140,6 +145,8 @@ const COMMON_ERRORS = {
140
145
  CM_INVALID_COMMAND_EXECUTION: makeError("unknown", "There was an error while executing the command on the VM", "Please, contact the coordinator if the error persists.")
141
146
  };
142
147
 
148
+ dotenv.config();
149
+ let provider;
143
150
  /**
144
151
  * Return a configured and connected instance of the AWS S3 client.
145
152
  * @dev this method check and utilize the environment variables to configure the connection
@@ -162,6 +169,36 @@ const getS3Client = async () => {
162
169
  region: process.env.AWS_REGION
163
170
  });
164
171
  };
172
+ /**
173
+ * Returns a Prvider, connected via a configured JSON URL or else
174
+ * the ethers.js default provider, using configured API keys.
175
+ * @returns <ethers.providers.Provider> An Eth node provider
176
+ */
177
+ const setEthProvider = () => {
178
+ if (provider)
179
+ return provider;
180
+ console.log(`setting new provider`);
181
+ // Use JSON URL if defined
182
+ // if ((hardhat as any).ethers) {
183
+ // console.log(`using hardhat.ethers provider`)
184
+ // provider = (hardhat as any).ethers.provider
185
+ // } else
186
+ if (process.env.ETH_PROVIDER_JSON_URL) {
187
+ console.log(`JSON URL provider at ${process.env.ETH_PROVIDER_JSON_URL}`);
188
+ provider = new ethers.providers.JsonRpcProvider({
189
+ url: process.env.ETH_PROVIDER_JSON_URL,
190
+ skipFetchSetup: true
191
+ });
192
+ }
193
+ else {
194
+ // Otherwise, connect the default provider with ALchemy, Infura, or both
195
+ provider = ethers.providers.getDefaultProvider("homestead", {
196
+ alchemy: process.env.ETH_PROVIDER_ALCHEMY_API_KEY,
197
+ infura: process.env.ETH_PROVIDER_INFURA_API_KEY
198
+ });
199
+ }
200
+ return provider;
201
+ };
165
202
 
166
203
  dotenv.config();
167
204
  /**
@@ -191,7 +228,7 @@ const getCurrentServerTimestampInMillis = () => Timestamp.now().toMillis();
191
228
  * Interrupt the current execution for a specified amount of time.
192
229
  * @param ms <number> - the amount of time expressed in milliseconds.
193
230
  */
194
- const sleep = async (ms) => setTimeout(ms);
231
+ const sleep = async (ms) => setTimeout$1(ms);
195
232
  /**
196
233
  * Query for ceremony circuits.
197
234
  * @notice the order by sequence position is fundamental to maintain parallelism among contributions for different circuits.
@@ -264,7 +301,7 @@ const queryOpenedCeremonies = async () => {
264
301
  const getCircuitDocumentByPosition = async (ceremonyId, sequencePosition) => {
265
302
  // Query for all ceremony circuits.
266
303
  const circuits = await getCeremonyCircuits(ceremonyId);
267
- // Apply a filter using the sequence postion.
304
+ // Apply a filter using the sequence position.
268
305
  const matchedCircuits = circuits.filter((circuit) => circuit.data().sequencePosition === sequencePosition);
269
306
  if (matchedCircuits.length !== 1)
270
307
  logAndThrowError(COMMON_ERRORS.CM_NO_CIRCUIT_FOR_GIVEN_SEQUENCE_POSITION);
@@ -305,7 +342,7 @@ const downloadArtifactFromS3Bucket = async (bucketName, objectKey, localFilePath
305
342
  const writeStream = createWriteStream(localFilePath);
306
343
  const streamPipeline = promisify(pipeline);
307
344
  await streamPipeline(response.body, writeStream);
308
- writeStream.on('finish', () => {
345
+ writeStream.on("finish", () => {
309
346
  writeStream.end();
310
347
  });
311
348
  };
@@ -429,12 +466,14 @@ const htmlEncodeCircuitData = (circuitDocument) => ({
429
466
  const getGitHubVariables = () => {
430
467
  if (!process.env.GITHUB_MINIMUM_FOLLOWERS ||
431
468
  !process.env.GITHUB_MINIMUM_FOLLOWING ||
432
- !process.env.GITHUB_MINIMUM_PUBLIC_REPOS)
469
+ !process.env.GITHUB_MINIMUM_PUBLIC_REPOS ||
470
+ !process.env.GITHUB_MINIMUM_AGE)
433
471
  logAndThrowError(COMMON_ERRORS.CM_WRONG_CONFIGURATION);
434
472
  return {
435
473
  minimumFollowers: Number(process.env.GITHUB_MINIMUM_FOLLOWERS),
436
474
  minimumFollowing: Number(process.env.GITHUB_MINIMUM_FOLLOWING),
437
- minimumPublicRepos: Number(process.env.GITHUB_MINIMUM_PUBLIC_REPOS)
475
+ minimumPublicRepos: Number(process.env.GITHUB_MINIMUM_PUBLIC_REPOS),
476
+ minimumAge: Number(process.env.GITHUB_MINIMUM_AGE)
438
477
  };
439
478
  };
440
479
  /**
@@ -444,7 +483,7 @@ const getGitHubVariables = () => {
444
483
  const getAWSVariables = () => {
445
484
  if (!process.env.AWS_ACCESS_KEY_ID ||
446
485
  !process.env.AWS_SECRET_ACCESS_KEY ||
447
- !process.env.AWS_ROLE_ARN ||
486
+ !process.env.AWS_INSTANCE_PROFILE_ARN ||
448
487
  !process.env.AWS_AMI_ID ||
449
488
  !process.env.AWS_SNS_TOPIC_ARN)
450
489
  logAndThrowError(COMMON_ERRORS.CM_WRONG_CONFIGURATION);
@@ -452,7 +491,7 @@ const getAWSVariables = () => {
452
491
  accessKeyId: process.env.AWS_ACCESS_KEY_ID,
453
492
  secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
454
493
  region: process.env.AWS_REGION || "eu-central-1",
455
- roleArn: process.env.AWS_ROLE_ARN,
494
+ instanceProfileArn: process.env.AWS_INSTANCE_PROFILE_ARN,
456
495
  amiId: process.env.AWS_AMI_ID,
457
496
  snsTopic: process.env.AWS_SNS_TOPIC_ARN
458
497
  };
@@ -498,7 +537,7 @@ dotenv.config();
498
537
  const registerAuthUser = functions
499
538
  .region("europe-west1")
500
539
  .runWith({
501
- memory: "512MB"
540
+ memory: "1GB"
502
541
  })
503
542
  .auth.user()
504
543
  .onCreate(async (user) => {
@@ -530,16 +569,18 @@ const registerAuthUser = functions
530
569
  email === process.env.CUSTOM_CLAIMS_COORDINATOR_EMAIL_ADDRESS_OR_DOMAIN)) {
531
570
  const auth = admin.auth();
532
571
  // if provider == github.com let's use our functions to check the user's reputation
533
- if (user.providerData[0].providerId === "github.com") {
572
+ if (user.providerData.length > 0 && user.providerData[0].providerId === "github.com") {
534
573
  const vars = getGitHubVariables();
535
574
  // this return true or false
536
575
  try {
537
- const { reputable, avatarUrl: avatarURL } = await githubReputation(user.providerData[0].uid, vars.minimumFollowing, vars.minimumFollowers, vars.minimumPublicRepos);
576
+ const { reputable, avatarUrl: avatarURL } = await githubReputation(user.providerData[0].uid, vars.minimumFollowing, vars.minimumFollowers, vars.minimumPublicRepos, vars.minimumAge);
538
577
  if (!reputable) {
539
578
  // Delete user
540
579
  await auth.deleteUser(user.uid);
541
580
  // Throw error
542
- logAndThrowError(makeError("permission-denied", "The user is not allowed to sign up because their Github reputation is not high enough.", `The user ${user.displayName === "Null" || user.displayName === null ? user.uid : user.displayName} is not allowed to sign up because their Github reputation is not high enough. Please contact the administrator if you think this is a mistake.`));
581
+ logAndThrowError(makeError("permission-denied", "The user is not allowed to sign up because their Github reputation is not high enough.", `The user ${user.displayName === "Null" || user.displayName === null
582
+ ? user.uid
583
+ : user.displayName} is not allowed to sign up because their Github reputation is not high enough. Please contact the administrator if you think this is a mistake.`));
543
584
  }
544
585
  // store locally
545
586
  avatarUrl = avatarURL;
@@ -554,13 +595,13 @@ const registerAuthUser = functions
554
595
  }
555
596
  // Set document (nb. we refer to providerData[0] because we use Github OAuth provider only).
556
597
  // In future releases we might want to loop through the providerData array as we support
557
- // more providers.
598
+ // more providers.
558
599
  await userRef.set({
559
600
  name: encodedDisplayName,
560
601
  encodedDisplayName,
561
602
  // Metadata.
562
603
  creationTime,
563
- lastSignInTime,
604
+ lastSignInTime: lastSignInTime || creationTime,
564
605
  // Optional.
565
606
  email: email || "",
566
607
  emailVerified: emailVerified || false,
@@ -570,7 +611,7 @@ const registerAuthUser = functions
570
611
  // we want to create a new collection for the users to store the avatars
571
612
  const avatarRef = firestore.collection(commonTerms.collections.avatars.name).doc(uid);
572
613
  await avatarRef.set({
573
- avatarUrl: avatarUrl || "",
614
+ avatarUrl: avatarUrl || ""
574
615
  });
575
616
  printLog(`Authenticated user document with identifier ${uid} has been correctly stored`, LogLevel.DEBUG);
576
617
  printLog(`Authenticated user avatar with identifier ${uid} has been correctly stored`, LogLevel.DEBUG);
@@ -583,7 +624,7 @@ const registerAuthUser = functions
583
624
  const processSignUpWithCustomClaims = functions
584
625
  .region("europe-west1")
585
626
  .runWith({
586
- memory: "512MB"
627
+ memory: "1GB"
587
628
  })
588
629
  .auth.user()
589
630
  .onCreate(async (user) => {
@@ -624,7 +665,7 @@ dotenv.config();
624
665
  const startCeremony = functions
625
666
  .region("europe-west1")
626
667
  .runWith({
627
- memory: "512MB"
668
+ memory: "1GB"
628
669
  })
629
670
  .pubsub.schedule(`every 30 minutes`)
630
671
  .onRun(async () => {
@@ -646,7 +687,7 @@ const startCeremony = functions
646
687
  const stopCeremony = functions
647
688
  .region("europe-west1")
648
689
  .runWith({
649
- memory: "512MB"
690
+ memory: "1GB"
650
691
  })
651
692
  .pubsub.schedule(`every 30 minutes`)
652
693
  .onRun(async () => {
@@ -668,7 +709,7 @@ const stopCeremony = functions
668
709
  const setupCeremony = functions
669
710
  .region("europe-west1")
670
711
  .runWith({
671
- memory: "512MB"
712
+ memory: "1GB"
672
713
  })
673
714
  .https.onCall(async (data, context) => {
674
715
  // Check if the user has the coordinator claim.
@@ -706,7 +747,9 @@ const setupCeremony = functions
706
747
  // The VM unique identifier (if any).
707
748
  let vmInstanceId = "";
708
749
  // Get a new circuit document.
709
- const circuitDoc = await firestore.collection(getCircuitsCollectionPath(ceremonyDoc.ref.id)).doc().get();
750
+ const ccp = getCircuitsCollectionPath(ceremonyDoc.ref.id);
751
+ printLog(`CircuitsCollectionPath = ${ccp}`, LogLevel.DEBUG);
752
+ const circuitDoc = await firestore.collection(ccp).doc().get();
710
753
  // Check if using the VM approach for contribution verification.
711
754
  if (circuit.verification.cfOrVm === "VM" /* CircuitContributionVerificationMechanism.VM */) {
712
755
  // VM command to be run at the startup.
@@ -743,12 +786,14 @@ const setupCeremony = functions
743
786
  }
744
787
  // Encode circuit data.
745
788
  const encodedCircuit = htmlEncodeCircuitData(circuit);
789
+ printLog(`writing circuit data...`, LogLevel.DEBUG);
746
790
  // Prepare tx to write circuit data.
747
791
  batch.create(circuitDoc.ref, {
748
792
  ...encodedCircuit,
749
793
  lastUpdated: getCurrentServerTimestampInMillis()
750
794
  });
751
795
  }
796
+ printLog(`Done handling circuits...`, LogLevel.DEBUG);
752
797
  // Send txs in a batch (to avoid race conditions).
753
798
  await batch.commit();
754
799
  printLog(`Setup completed for ceremony ${ceremonyDoc.id}`, LogLevel.DEBUG);
@@ -761,7 +806,7 @@ const setupCeremony = functions
761
806
  const initEmptyWaitingQueueForCircuit = functions
762
807
  .region("europe-west1")
763
808
  .runWith({
764
- memory: "512MB"
809
+ memory: "1GB"
765
810
  })
766
811
  .firestore.document(`/${commonTerms.collections.ceremonies.name}/{ceremony}/${commonTerms.collections.circuits.name}/{circuit}`)
767
812
  .onCreate(async (doc) => {
@@ -793,7 +838,7 @@ const initEmptyWaitingQueueForCircuit = functions
793
838
  const finalizeCeremony = functions
794
839
  .region("europe-west1")
795
840
  .runWith({
796
- memory: "512MB"
841
+ memory: "1GB"
797
842
  })
798
843
  .https.onCall(async (data, context) => {
799
844
  if (!context.auth || !context.auth.token.coordinator)
@@ -814,7 +859,7 @@ const finalizeCeremony = functions
814
859
  // Get ceremony circuits.
815
860
  const circuits = await getCeremonyCircuits(ceremonyId);
816
861
  // Get final contribution for each circuit.
817
- // nb. the `getFinalContributionDocument` checks the existance of the final contribution document (if not present, throws).
862
+ // nb. the `getFinalContributionDocument` checks the existence of the final contribution document (if not present, throws).
818
863
  // Therefore, we just need to call the method without taking any data to verify the pre-condition of having already computed
819
864
  // the final contributions for each ceremony circuit.
820
865
  for await (const circuit of circuits)
@@ -867,9 +912,9 @@ dotenv.config();
867
912
  * @dev true when the participant can participate (1.A, 3.B, 1.D); otherwise false.
868
913
  */
869
914
  const checkParticipantForCeremony = functions
870
- .region('europe-west1')
915
+ .region("europe-west1")
871
916
  .runWith({
872
- memory: "512MB"
917
+ memory: "1GB"
873
918
  })
874
919
  .https.onCall(async (data, context) => {
875
920
  if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
@@ -938,7 +983,7 @@ const checkParticipantForCeremony = functions
938
983
  participantDoc.ref.update({
939
984
  status: "EXHUMED" /* ParticipantStatus.EXHUMED */,
940
985
  contributions,
941
- tempContributionData: tempContributionData ? tempContributionData : FieldValue.delete(),
986
+ tempContributionData: tempContributionData || FieldValue.delete(),
942
987
  contributionStep: "DOWNLOADING" /* ParticipantContributionStep.DOWNLOADING */,
943
988
  contributionStartedAt: 0,
944
989
  verificationStartedAt: FieldValue.delete(),
@@ -971,9 +1016,9 @@ const checkParticipantForCeremony = functions
971
1016
  * 2) the participant has just finished the contribution for a circuit (contributionProgress != 0 && status = CONTRIBUTED && contributionStep = COMPLETED).
972
1017
  */
973
1018
  const progressToNextCircuitForContribution = functions
974
- .region('europe-west1')
1019
+ .region("europe-west1")
975
1020
  .runWith({
976
- memory: "512MB"
1021
+ memory: "1GB"
977
1022
  })
978
1023
  .https.onCall(async (data, context) => {
979
1024
  if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
@@ -1018,9 +1063,9 @@ const progressToNextCircuitForContribution = functions
1018
1063
  * 5) Completed contribution computation and verification.
1019
1064
  */
1020
1065
  const progressToNextContributionStep = functions
1021
- .region('europe-west1')
1066
+ .region("europe-west1")
1022
1067
  .runWith({
1023
- memory: "512MB"
1068
+ memory: "1GB"
1024
1069
  })
1025
1070
  .https.onCall(async (data, context) => {
1026
1071
  if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
@@ -1069,9 +1114,9 @@ const progressToNextContributionStep = functions
1069
1114
  * @dev enable the current contributor to resume a contribution from where it had left off.
1070
1115
  */
1071
1116
  const permanentlyStoreCurrentContributionTimeAndHash = functions
1072
- .region('europe-west1')
1117
+ .region("europe-west1")
1073
1118
  .runWith({
1074
- memory: "512MB"
1119
+ memory: "1GB"
1075
1120
  })
1076
1121
  .https.onCall(async (data, context) => {
1077
1122
  if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
@@ -1111,9 +1156,9 @@ const permanentlyStoreCurrentContributionTimeAndHash = functions
1111
1156
  * @dev enable the current contributor to resume a multi-part upload from where it had left off.
1112
1157
  */
1113
1158
  const temporaryStoreCurrentContributionMultiPartUploadId = functions
1114
- .region('europe-west1')
1159
+ .region("europe-west1")
1115
1160
  .runWith({
1116
- memory: "512MB"
1161
+ memory: "1GB"
1117
1162
  })
1118
1163
  .https.onCall(async (data, context) => {
1119
1164
  if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
@@ -1149,9 +1194,9 @@ const temporaryStoreCurrentContributionMultiPartUploadId = functions
1149
1194
  * @dev enable the current contributor to resume a multi-part upload from where it had left off.
1150
1195
  */
1151
1196
  const temporaryStoreCurrentContributionUploadedChunkData = functions
1152
- .region('europe-west1')
1197
+ .region("europe-west1")
1153
1198
  .runWith({
1154
- memory: "512MB"
1199
+ memory: "1GB"
1155
1200
  })
1156
1201
  .https.onCall(async (data, context) => {
1157
1202
  if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
@@ -1191,9 +1236,9 @@ const temporaryStoreCurrentContributionUploadedChunkData = functions
1191
1236
  * contributed to every selected ceremony circuits (= DONE).
1192
1237
  */
1193
1238
  const checkAndPrepareCoordinatorForFinalization = functions
1194
- .region('europe-west1')
1239
+ .region("europe-west1")
1195
1240
  .runWith({
1196
- memory: "512MB"
1241
+ memory: "1GB"
1197
1242
  })
1198
1243
  .https.onCall(async (data, context) => {
1199
1244
  if (!context.auth || !context.auth.token.coordinator)
@@ -1270,7 +1315,7 @@ const coordinate = async (participant, circuit, isSingleParticipantCoordination,
1270
1315
  if (isSingleParticipantCoordination) {
1271
1316
  // Scenario (A).
1272
1317
  if (emptyWaitingQueue) {
1273
- printLog(`Coordinate - executing scenario A - emptyWaitingQueue`, LogLevel.DEBUG);
1318
+ printLog(`Coordinate - executing scenario A - emptyWaitingQueue`, LogLevel.INFO);
1274
1319
  // Update.
1275
1320
  newCurrentContributorId = participant.id;
1276
1321
  newParticipantStatus = "CONTRIBUTING" /* ParticipantStatus.CONTRIBUTING */;
@@ -1279,14 +1324,14 @@ const coordinate = async (participant, circuit, isSingleParticipantCoordination,
1279
1324
  }
1280
1325
  // Scenario (A).
1281
1326
  else if (participantResumingAfterTimeoutExpiration) {
1282
- printLog(`Coordinate - executing scenario A - single - participantResumingAfterTimeoutExpiration`, LogLevel.DEBUG);
1327
+ printLog(`Coordinate - executing scenario A - single - participantResumingAfterTimeoutExpiration`, LogLevel.INFO);
1283
1328
  newParticipantStatus = "CONTRIBUTING" /* ParticipantStatus.CONTRIBUTING */;
1284
1329
  newContributionStep = "DOWNLOADING" /* ParticipantContributionStep.DOWNLOADING */;
1285
1330
  newCurrentContributorId = participant.id;
1286
1331
  }
1287
1332
  // Scenario (B).
1288
1333
  else if (participantIsNotCurrentContributor) {
1289
- printLog(`Coordinate - executing scenario B - single - participantIsNotCurrentContributor`, LogLevel.DEBUG);
1334
+ printLog(`Coordinate - executing scenario B - single - participantIsNotCurrentContributor`, LogLevel.INFO);
1290
1335
  newCurrentContributorId = currentContributor;
1291
1336
  newParticipantStatus = "WAITING" /* ParticipantStatus.WAITING */;
1292
1337
  newContributors.push(participant.id);
@@ -1305,7 +1350,7 @@ const coordinate = async (participant, circuit, isSingleParticipantCoordination,
1305
1350
  });
1306
1351
  }
1307
1352
  else if (participantIsCurrentContributor && participantCompletedOneOrAllContributions && !!ceremonyId) {
1308
- printLog(`Coordinate - executing scenario C - multi - participantIsCurrentContributor && participantCompletedOneOrAllContributions`, LogLevel.DEBUG);
1353
+ printLog(`Coordinate - executing scenario C - multi - participantIsCurrentContributor && participantCompletedOneOrAllContributions`, LogLevel.INFO);
1309
1354
  newParticipantStatus = "CONTRIBUTING" /* ParticipantStatus.CONTRIBUTING */;
1310
1355
  newContributionStep = "DOWNLOADING" /* ParticipantContributionStep.DOWNLOADING */;
1311
1356
  // Remove from waiting queue of circuit X.
@@ -1323,7 +1368,7 @@ const coordinate = async (participant, circuit, isSingleParticipantCoordination,
1323
1368
  contributionStartedAt: getCurrentServerTimestampInMillis(),
1324
1369
  lastUpdated: getCurrentServerTimestampInMillis()
1325
1370
  });
1326
- printLog(`Participant ${newCurrentContributorId} is the new current contributor for circuit ${circuit.id}`, LogLevel.DEBUG);
1371
+ printLog(`Participant ${newCurrentContributorId} is the new current contributor for circuit ${circuit.id}`, LogLevel.INFO);
1327
1372
  }
1328
1373
  }
1329
1374
  // Prepare tx - must be done for all Scenarios.
@@ -1343,54 +1388,74 @@ const coordinate = async (participant, circuit, isSingleParticipantCoordination,
1343
1388
  * Wait until the command has completed its execution inside the VM.
1344
1389
  * @dev this method implements a custom interval to check 5 times after 1 minute if the command execution
1345
1390
  * has been completed or not by calling the `retrieveCommandStatus` method.
1346
- * @param {any} resolve the promise.
1347
- * @param {any} reject the promise.
1348
1391
  * @param {SSMClient} ssm the SSM client.
1349
1392
  * @param {string} vmInstanceId the unique identifier of the VM instance.
1350
1393
  * @param {string} commandId the unique identifier of the VM command.
1351
1394
  * @returns <Promise<void>> true when the command execution succeed; otherwise false.
1352
1395
  */
1353
- const waitForVMCommandExecution = (resolve, reject, ssm, vmInstanceId, commandId) => {
1354
- const interval = setInterval(async () => {
1396
+ const waitForVMCommandExecution = (ssm, vmInstanceId, commandId) => new Promise((resolve, reject) => {
1397
+ const poll = async () => {
1355
1398
  try {
1356
1399
  // Get command status.
1357
1400
  const cmdStatus = await retrieveCommandStatus(ssm, vmInstanceId, commandId);
1358
1401
  printLog(`Checking command ${commandId} status => ${cmdStatus}`, LogLevel.DEBUG);
1359
- if (cmdStatus === CommandInvocationStatus.SUCCESS) {
1360
- printLog(`Command ${commandId} successfully completed`, LogLevel.DEBUG);
1361
- // Resolve the promise.
1362
- resolve();
1363
- }
1364
- else if (cmdStatus === CommandInvocationStatus.FAILED) {
1365
- logAndThrowError(SPECIFIC_ERRORS.SE_VM_FAILED_COMMAND_EXECUTION);
1366
- reject();
1367
- }
1368
- else if (cmdStatus === CommandInvocationStatus.TIMED_OUT) {
1369
- logAndThrowError(SPECIFIC_ERRORS.SE_VM_TIMEDOUT_COMMAND_EXECUTION);
1370
- reject();
1371
- }
1372
- else if (cmdStatus === CommandInvocationStatus.CANCELLED) {
1373
- logAndThrowError(SPECIFIC_ERRORS.SE_VM_CANCELLED_COMMAND_EXECUTION);
1374
- reject();
1402
+ let error;
1403
+ switch (cmdStatus) {
1404
+ case CommandInvocationStatus.CANCELLING:
1405
+ case CommandInvocationStatus.CANCELLED: {
1406
+ error = SPECIFIC_ERRORS.SE_VM_CANCELLED_COMMAND_EXECUTION;
1407
+ break;
1408
+ }
1409
+ case CommandInvocationStatus.DELAYED: {
1410
+ error = SPECIFIC_ERRORS.SE_VM_DELAYED_COMMAND_EXECUTION;
1411
+ break;
1412
+ }
1413
+ case CommandInvocationStatus.FAILED: {
1414
+ error = SPECIFIC_ERRORS.SE_VM_FAILED_COMMAND_EXECUTION;
1415
+ break;
1416
+ }
1417
+ case CommandInvocationStatus.TIMED_OUT: {
1418
+ error = SPECIFIC_ERRORS.SE_VM_TIMEDOUT_COMMAND_EXECUTION;
1419
+ break;
1420
+ }
1421
+ case CommandInvocationStatus.IN_PROGRESS:
1422
+ case CommandInvocationStatus.PENDING: {
1423
+ // wait a minute and poll again
1424
+ setTimeout(poll, 60000);
1425
+ return;
1426
+ }
1427
+ case CommandInvocationStatus.SUCCESS: {
1428
+ printLog(`Command ${commandId} successfully completed`, LogLevel.DEBUG);
1429
+ // Resolve the promise.
1430
+ resolve();
1431
+ return;
1432
+ }
1433
+ default: {
1434
+ logAndThrowError(SPECIFIC_ERRORS.SE_VM_UNKNOWN_COMMAND_STATUS);
1435
+ }
1375
1436
  }
1376
- else if (cmdStatus === CommandInvocationStatus.DELAYED) {
1377
- logAndThrowError(SPECIFIC_ERRORS.SE_VM_DELAYED_COMMAND_EXECUTION);
1378
- reject();
1437
+ if (error) {
1438
+ logAndThrowError(error);
1379
1439
  }
1380
1440
  }
1381
1441
  catch (error) {
1382
1442
  printLog(`Invalid command ${commandId} execution`, LogLevel.DEBUG);
1443
+ const ec2 = await createEC2Client();
1444
+ // if it errors out, let's just log it as a warning so the coordinator is aware
1445
+ try {
1446
+ await stopEC2Instance(ec2, vmInstanceId);
1447
+ }
1448
+ catch (stopError) {
1449
+ printLog(`Error while stopping VM instance ${vmInstanceId} - Error ${stopError}`, LogLevel.WARN);
1450
+ }
1383
1451
  if (!error.toString().includes(commandId))
1384
1452
  logAndThrowError(COMMON_ERRORS.CM_INVALID_COMMAND_EXECUTION);
1385
1453
  // Reject the promise.
1386
1454
  reject();
1387
1455
  }
1388
- finally {
1389
- // Clear the interval.
1390
- clearInterval(interval);
1391
- }
1392
- }, 60000); // 1 minute.
1393
- };
1456
+ };
1457
+ setTimeout(poll, 60000);
1458
+ });
1394
1459
  /**
1395
1460
  * This method is used to coordinate the waiting queues of ceremony circuits.
1396
1461
  * @dev this cloud function is triggered whenever an update of a document related to a participant of a ceremony occurs.
@@ -1411,9 +1476,9 @@ const waitForVMCommandExecution = (resolve, reject, ssm, vmInstanceId, commandId
1411
1476
  * - Just completed a contribution or all contributions for each circuit. If yes, coordinate (multi-participant scenario).
1412
1477
  */
1413
1478
  const coordinateCeremonyParticipant = functionsV1
1414
- .region('europe-west1')
1479
+ .region("europe-west1")
1415
1480
  .runWith({
1416
- memory: "512MB"
1481
+ memory: "1GB"
1417
1482
  })
1418
1483
  .firestore.document(`${commonTerms.collections.ceremonies.name}/{ceremonyId}/${commonTerms.collections.participants.name}/{participantId}`)
1419
1484
  .onUpdate(async (participantChanges) => {
@@ -1429,8 +1494,8 @@ const coordinateCeremonyParticipant = functionsV1
1429
1494
  // Extract data.
1430
1495
  const { contributionProgress: prevContributionProgress, status: prevStatus, contributionStep: prevContributionStep } = exParticipant.data();
1431
1496
  const { contributionProgress: changedContributionProgress, status: changedStatus, contributionStep: changedContributionStep } = changedParticipant.data();
1432
- printLog(`Coordinate participant ${exParticipant.id} for ceremony ${ceremonyId}`, LogLevel.DEBUG);
1433
- printLog(`Participant status: ${prevStatus} => ${changedStatus} - Participant contribution step: ${prevContributionStep} => ${changedContributionStep}`, LogLevel.DEBUG);
1497
+ printLog(`Coordinate participant ${exParticipant.id} for ceremony ${ceremonyId}`, LogLevel.INFO);
1498
+ printLog(`Participant status: ${prevStatus} => ${changedStatus} - Participant contribution step: ${prevContributionStep} => ${changedContributionStep}`, LogLevel.INFO);
1434
1499
  // Define pre-conditions.
1435
1500
  const participantReadyToContribute = changedStatus === "READY" /* ParticipantStatus.READY */;
1436
1501
  const participantReadyForFirstContribution = participantReadyToContribute && prevContributionProgress === 0;
@@ -1440,8 +1505,8 @@ const coordinateCeremonyParticipant = functionsV1
1440
1505
  prevContributionProgress !== 0;
1441
1506
  const participantCompletedEveryCircuitContribution = changedStatus === "DONE" /* ParticipantStatus.DONE */ && prevStatus !== "DONE" /* ParticipantStatus.DONE */;
1442
1507
  const participantCompletedContribution = prevContributionProgress === changedContributionProgress &&
1443
- prevStatus === "CONTRIBUTING" /* ParticipantStatus.CONTRIBUTING */ &&
1444
- prevContributionStep === "VERIFYING" /* ParticipantContributionStep.VERIFYING */ &&
1508
+ (prevStatus === "CONTRIBUTING" /* ParticipantStatus.CONTRIBUTING */ ||
1509
+ prevContributionStep === "VERIFYING" /* ParticipantContributionStep.VERIFYING */) &&
1445
1510
  changedStatus === "CONTRIBUTED" /* ParticipantStatus.CONTRIBUTED */ &&
1446
1511
  changedContributionStep === "COMPLETED" /* ParticipantContributionStep.COMPLETED */;
1447
1512
  // Step (2).
@@ -1449,7 +1514,7 @@ const coordinateCeremonyParticipant = functionsV1
1449
1514
  participantResumingContributionAfterTimeout ||
1450
1515
  participantReadyForNextContribution) {
1451
1516
  // Step (2.A).
1452
- printLog(`Participant is ready for first contribution (${participantReadyForFirstContribution}) or for the next contribution (${participantReadyForNextContribution}) or is resuming after a timeout expiration (${participantResumingContributionAfterTimeout})`, LogLevel.DEBUG);
1517
+ printLog(`Participant is ready for first contribution (${participantReadyForFirstContribution}) or for the next contribution (${participantReadyForNextContribution}) or is resuming after a timeout expiration (${participantResumingContributionAfterTimeout})`, LogLevel.INFO);
1453
1518
  // Get the circuit.
1454
1519
  const circuit = await getCircuitDocumentByPosition(ceremonyId, changedContributionProgress);
1455
1520
  // Coordinate.
@@ -1458,7 +1523,7 @@ const coordinateCeremonyParticipant = functionsV1
1458
1523
  }
1459
1524
  else if (participantCompletedContribution || participantCompletedEveryCircuitContribution) {
1460
1525
  // Step (2.B).
1461
- printLog(`Participant completed a contribution (${participantCompletedContribution}) or every contribution for each circuit (${participantCompletedEveryCircuitContribution})`, LogLevel.DEBUG);
1526
+ printLog(`Participant completed a contribution (${participantCompletedContribution}) or every contribution for each circuit (${participantCompletedEveryCircuitContribution})`, LogLevel.INFO);
1462
1527
  // Get the circuit.
1463
1528
  const circuit = await getCircuitDocumentByPosition(ceremonyId, prevContributionProgress);
1464
1529
  // Coordinate.
@@ -1482,11 +1547,9 @@ const checkIfVMRunning = async (ec2, vmInstanceId, attempts = 5) => {
1482
1547
  const isVMRunning = await checkIfRunning(ec2, vmInstanceId);
1483
1548
  if (!isVMRunning) {
1484
1549
  printLog(`VM not running, ${attempts - 1} attempts remaining. Retrying in 1 minute...`, LogLevel.DEBUG);
1485
- return await checkIfVMRunning(ec2, vmInstanceId, attempts - 1);
1486
- }
1487
- else {
1488
- return true;
1550
+ return checkIfVMRunning(ec2, vmInstanceId, attempts - 1);
1489
1551
  }
1552
+ return true;
1490
1553
  };
1491
1554
  /**
1492
1555
  * Verify the contribution of a participant computed while contributing to a specific circuit of a ceremony.
@@ -1514,267 +1577,296 @@ const checkIfVMRunning = async (ec2, vmInstanceId, attempts = 5) => {
1514
1577
  * 1.A.4.C.1) If true, update circuit waiting for queue and average timings accordingly to contribution verification results;
1515
1578
  * 2) Send all updates atomically to the Firestore database.
1516
1579
  */
1517
- const verifycontribution = functionsV2.https.onCall({ memory: "16GiB", timeoutSeconds: 3600, region: 'europe-west1' }, async (request) => {
1518
- if (!request.auth || (!request.auth.token.participant && !request.auth.token.coordinator))
1519
- logAndThrowError(SPECIFIC_ERRORS.SE_AUTH_NO_CURRENT_AUTH_USER);
1520
- if (!request.data.ceremonyId ||
1521
- !request.data.circuitId ||
1522
- !request.data.contributorOrCoordinatorIdentifier ||
1523
- !request.data.bucketName)
1524
- logAndThrowError(COMMON_ERRORS.CM_MISSING_OR_WRONG_INPUT_DATA);
1525
- if (!process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_NAME ||
1526
- !process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_VERSION ||
1527
- !process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_COMMIT_HASH)
1528
- logAndThrowError(COMMON_ERRORS.CM_WRONG_CONFIGURATION);
1529
- // Step (0).
1530
- // Prepare and start timer.
1531
- const verifyContributionTimer = new Timer({ label: commonTerms.cloudFunctionsNames.verifyContribution });
1532
- verifyContributionTimer.start();
1533
- // Get DB.
1534
- const firestore = admin.firestore();
1535
- // Prepare batch of txs.
1536
- const batch = firestore.batch();
1537
- // Extract data.
1538
- const { ceremonyId, circuitId, contributorOrCoordinatorIdentifier, bucketName } = request.data;
1539
- const userId = request.auth?.uid;
1540
- // Look for the ceremony, circuit and participant document.
1541
- const ceremonyDoc = await getDocumentById(commonTerms.collections.ceremonies.name, ceremonyId);
1542
- const circuitDoc = await getDocumentById(getCircuitsCollectionPath(ceremonyId), circuitId);
1543
- const participantDoc = await getDocumentById(getParticipantsCollectionPath(ceremonyId), userId);
1544
- if (!ceremonyDoc.data() || !circuitDoc.data() || !participantDoc.data())
1545
- logAndThrowError(COMMON_ERRORS.CM_INEXISTENT_DOCUMENT_DATA);
1546
- // Extract documents data.
1547
- const { state } = ceremonyDoc.data();
1548
- const { status, contributions, verificationStartedAt, contributionStartedAt } = participantDoc.data();
1549
- const { waitingQueue, prefix, avgTimings, verification, files } = circuitDoc.data();
1550
- const { completedContributions, failedContributions } = waitingQueue;
1551
- const { contributionComputation: avgContributionComputationTime, fullContribution: avgFullContributionTime, verifyCloudFunction: avgVerifyCloudFunctionTime } = avgTimings;
1552
- const { cfOrVm, vm } = verification;
1553
- // we might not have it if the circuit is not using VM.
1554
- let vmInstanceId = "";
1555
- if (vm)
1556
- vmInstanceId = vm.vmInstanceId;
1557
- // Define pre-conditions.
1558
- const isFinalizing = state === "CLOSED" /* CeremonyState.CLOSED */ && request.auth && request.auth.token.coordinator; // true only when the coordinator verifies the final contributions.
1559
- const isContributing = status === "CONTRIBUTING" /* ParticipantStatus.CONTRIBUTING */;
1560
- const isUsingVM = cfOrVm === "VM" /* CircuitContributionVerificationMechanism.VM */ && !!vmInstanceId;
1561
- // Prepare state.
1562
- let isContributionValid = false;
1563
- let verifyCloudFunctionExecutionTime = 0; // time spent while executing the verify contribution cloud function.
1564
- let verifyCloudFunctionTime = 0; // time spent while executing the core business logic of this cloud function.
1565
- let fullContributionTime = 0; // time spent while doing non-verification contributions tasks (download, compute, upload).
1566
- let contributionComputationTime = 0; // time spent while computing the contribution.
1567
- let lastZkeyBlake2bHash = ""; // the Blake2B hash of the last zKey.
1568
- let verificationTranscriptTemporaryLocalPath = ""; // the local temporary path for the verification transcript.
1569
- let transcriptBlake2bHash = ""; // the Blake2B hash of the verification transcript.
1570
- let commandId = ""; // the unique identifier of the VM command.
1571
- // Derive necessary data.
1572
- const lastZkeyIndex = formatZkeyIndex(completedContributions + 1);
1573
- const verificationTranscriptCompleteFilename = `${prefix}_${isFinalizing
1574
- ? `${contributorOrCoordinatorIdentifier}_${finalContributionIndex}_verification_transcript.log`
1575
- : `${lastZkeyIndex}_${contributorOrCoordinatorIdentifier}_verification_transcript.log`}`;
1576
- const lastZkeyFilename = `${prefix}_${isFinalizing ? finalContributionIndex : lastZkeyIndex}.zkey`;
1577
- // Prepare state for VM verification (if needed).
1578
- const ec2 = await createEC2Client();
1579
- const ssm = await createSSMClient();
1580
- // Step (1.A.1).
1581
- // Get storage paths.
1582
- const verificationTranscriptStoragePathAndFilename = getTranscriptStorageFilePath(prefix, verificationTranscriptCompleteFilename);
1583
- // the zKey storage path is required to be sent to the VM api
1584
- const lastZkeyStoragePath = getZkeyStorageFilePath(prefix, `${prefix}_${isFinalizing ? finalContributionIndex : lastZkeyIndex}.zkey`);
1585
- const verificationTaskTimer = new Timer({ label: `${ceremonyId}-${circuitId}-${participantDoc.id}` });
1586
- const completeVerification = async () => {
1587
- // Stop verification task timer.
1588
- printLog("Completing verification", LogLevel.DEBUG);
1589
- verificationTaskTimer.stop();
1590
- verifyCloudFunctionExecutionTime = verificationTaskTimer.ms();
1591
- if (isUsingVM) {
1592
- // Create temporary path.
1593
- verificationTranscriptTemporaryLocalPath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}.log`);
1594
- await sleep(1000); // wait 1s for file creation.
1595
- // Download from bucket.
1596
- // nb. the transcript MUST be uploaded from the VM by verification commands.
1597
- await downloadArtifactFromS3Bucket(bucketName, verificationTranscriptStoragePathAndFilename, verificationTranscriptTemporaryLocalPath);
1598
- // Read the verification trascript and validate data by checking for core info ("ZKey Ok!").
1599
- const content = fs.readFileSync(verificationTranscriptTemporaryLocalPath, "utf-8");
1600
- if (content.includes("ZKey Ok!"))
1601
- isContributionValid = true;
1602
- // If the contribution is valid, then format and store the trascript.
1603
- if (isContributionValid) {
1604
- // eslint-disable-next-line no-control-regex
1605
- const updated = content.replace(/\x1b[[0-9;]*m/g, "");
1606
- fs.writeFileSync(verificationTranscriptTemporaryLocalPath, updated);
1580
+ const verifycontribution = functionsV2.https.onCall({ memory: "32GiB", timeoutSeconds: 3600, region: "europe-west1", cpu: 8 }, async (request) => {
1581
+ try {
1582
+ if (!request.auth || (!request.auth.token.participant && !request.auth.token.coordinator))
1583
+ logAndThrowError(SPECIFIC_ERRORS.SE_AUTH_NO_CURRENT_AUTH_USER);
1584
+ if (!request.data.ceremonyId ||
1585
+ !request.data.circuitId ||
1586
+ !request.data.contributorOrCoordinatorIdentifier ||
1587
+ !request.data.bucketName)
1588
+ logAndThrowError(COMMON_ERRORS.CM_MISSING_OR_WRONG_INPUT_DATA);
1589
+ if (!process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_NAME ||
1590
+ !process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_VERSION ||
1591
+ !process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_COMMIT_HASH)
1592
+ logAndThrowError(COMMON_ERRORS.CM_WRONG_CONFIGURATION);
1593
+ const BUCKET_NAME_REGEX = /^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$/;
1594
+ if (!BUCKET_NAME_REGEX.test(request.data.bucketName))
1595
+ logAndThrowError(SPECIFIC_ERRORS.WRONG_BUCKET_NAME);
1596
+ // Step (0).
1597
+ // Prepare and start timer.
1598
+ const verifyContributionTimer = new Timer({ label: commonTerms.cloudFunctionsNames.verifyContribution });
1599
+ verifyContributionTimer.start();
1600
+ // Get DB.
1601
+ const firestore = admin.firestore();
1602
+ // Prepare batch of txs.
1603
+ const batch = firestore.batch();
1604
+ // Extract data.
1605
+ const { ceremonyId, circuitId, contributorOrCoordinatorIdentifier, bucketName } = request.data;
1606
+ const userId = request.auth?.uid;
1607
+ // Look for the ceremony, circuit and participant document.
1608
+ const ceremonyDoc = await getDocumentById(commonTerms.collections.ceremonies.name, ceremonyId);
1609
+ const circuitDoc = await getDocumentById(getCircuitsCollectionPath(ceremonyId), circuitId);
1610
+ const participantDoc = await getDocumentById(getParticipantsCollectionPath(ceremonyId), userId);
1611
+ if (!ceremonyDoc.data() || !circuitDoc.data() || !participantDoc.data())
1612
+ logAndThrowError(COMMON_ERRORS.CM_INEXISTENT_DOCUMENT_DATA);
1613
+ // Extract documents data.
1614
+ const { state } = ceremonyDoc.data();
1615
+ const { status, contributions, verificationStartedAt, contributionStartedAt } = participantDoc.data();
1616
+ const { waitingQueue, prefix, avgTimings, verification, files } = circuitDoc.data();
1617
+ const { completedContributions, failedContributions } = waitingQueue;
1618
+ const { contributionComputation: avgContributionComputationTime, fullContribution: avgFullContributionTime, verifyCloudFunction: avgVerifyCloudFunctionTime } = avgTimings;
1619
+ const { cfOrVm, vm } = verification;
1620
+ // we might not have it if the circuit is not using VM.
1621
+ let vmInstanceId = "";
1622
+ if (vm)
1623
+ vmInstanceId = vm.vmInstanceId;
1624
+ // Define pre-conditions.
1625
+ const isFinalizing = state === "CLOSED" /* CeremonyState.CLOSED */ && request.auth && request.auth.token.coordinator; // true only when the coordinator verifies the final contributions.
1626
+ const isContributing = status === "CONTRIBUTING" /* ParticipantStatus.CONTRIBUTING */;
1627
+ const isUsingVM = cfOrVm === "VM" /* CircuitContributionVerificationMechanism.VM */ && !!vmInstanceId;
1628
+ // Prepare state.
1629
+ let isContributionValid = false;
1630
+ let verifyCloudFunctionExecutionTime = 0; // time spent while executing the verify contribution cloud function.
1631
+ let verifyCloudFunctionTime = 0; // time spent while executing the core business logic of this cloud function.
1632
+ let fullContributionTime = 0; // time spent while doing non-verification contributions tasks (download, compute, upload).
1633
+ let contributionComputationTime = 0; // time spent while computing the contribution.
1634
+ let lastZkeyBlake2bHash = ""; // the Blake2B hash of the last zKey.
1635
+ let verificationTranscriptTemporaryLocalPath = ""; // the local temporary path for the verification transcript.
1636
+ let transcriptBlake2bHash = ""; // the Blake2B hash of the verification transcript.
1637
+ let commandId = ""; // the unique identifier of the VM command.
1638
+ // Derive necessary data.
1639
+ const lastZkeyIndex = formatZkeyIndex(completedContributions + 1);
1640
+ const verificationTranscriptCompleteFilename = `${prefix}_${isFinalizing
1641
+ ? `${contributorOrCoordinatorIdentifier}_${finalContributionIndex}_verification_transcript.log`
1642
+ : `${lastZkeyIndex}_${contributorOrCoordinatorIdentifier}_verification_transcript.log`}`;
1643
+ const lastZkeyFilename = `${prefix}_${isFinalizing ? finalContributionIndex : lastZkeyIndex}.zkey`;
1644
+ // Prepare state for VM verification (if needed).
1645
+ const ec2 = await createEC2Client();
1646
+ const ssm = await createSSMClient();
1647
+ // Step (1.A.1).
1648
+ // Get storage paths.
1649
+ const verificationTranscriptStoragePathAndFilename = getTranscriptStorageFilePath(prefix, verificationTranscriptCompleteFilename);
1650
+ // the zKey storage path is required to be sent to the VM api
1651
+ const lastZkeyStoragePath = getZkeyStorageFilePath(prefix, `${prefix}_${isFinalizing ? finalContributionIndex : lastZkeyIndex}.zkey`);
1652
+ const verificationTaskTimer = new Timer({ label: `${ceremonyId}-${circuitId}-${participantDoc.id}` });
1653
+ const dumpLog = async (path) => {
1654
+ printLog(`transcript >>>>>>`, LogLevel.DEBUG);
1655
+ try {
1656
+ const data = await fs.promises.readFile(path, "utf8");
1657
+ printLog(data, LogLevel.DEBUG);
1607
1658
  }
1608
- }
1609
- printLog(`The contribution has been verified - Result ${isContributionValid}`, LogLevel.DEBUG);
1610
- // Create a new contribution document.
1611
- const contributionDoc = await firestore
1612
- .collection(getContributionsCollectionPath(ceremonyId, circuitId))
1613
- .doc()
1614
- .get();
1615
- // Step (1.A.4).
1616
- if (isContributionValid) {
1617
- // Sleep ~3 seconds to wait for verification transcription.
1618
- await sleep(3000);
1619
- // Step (1.A.4.A.1).
1659
+ catch (readError) {
1660
+ printLog(readError, LogLevel.ERROR);
1661
+ }
1662
+ };
1663
+ const completeVerification = async () => {
1664
+ // Stop verification task timer.
1665
+ printLog("Completing verification", LogLevel.DEBUG);
1666
+ verificationTaskTimer.stop();
1667
+ verifyCloudFunctionExecutionTime = verificationTaskTimer.ms();
1620
1668
  if (isUsingVM) {
1621
- // Retrieve the contribution hash from the command output.
1622
- lastZkeyBlake2bHash = await retrieveCommandOutput(ssm, vmInstanceId, commandId);
1623
- const hashRegex = /[a-fA-F0-9]{64}/;
1624
- const match = lastZkeyBlake2bHash.match(hashRegex);
1625
- lastZkeyBlake2bHash = match.at(0);
1626
- // re upload the formatted verification transcript
1627
- await uploadFileToBucket(bucketName, verificationTranscriptStoragePathAndFilename, verificationTranscriptTemporaryLocalPath, true);
1669
+ // Create temporary path.
1670
+ verificationTranscriptTemporaryLocalPath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}.log`);
1671
+ await sleep(1000); // wait 1s for file creation.
1672
+ // Download from bucket.
1673
+ // nb. the transcript MUST be uploaded from the VM by verification commands.
1674
+ await downloadArtifactFromS3Bucket(bucketName, verificationTranscriptStoragePathAndFilename, verificationTranscriptTemporaryLocalPath);
1675
+ // Read the verification trascript and validate data by checking for core info ("ZKey Ok!").
1676
+ const content = fs.readFileSync(verificationTranscriptTemporaryLocalPath, "utf-8");
1677
+ if (content.includes("ZKey Ok!"))
1678
+ isContributionValid = true;
1679
+ // If the contribution is valid, then format and store the trascript.
1680
+ if (isContributionValid) {
1681
+ // eslint-disable-next-line no-control-regex
1682
+ const updated = content.replace(/\x1b[[0-9;]*m/g, "");
1683
+ fs.writeFileSync(verificationTranscriptTemporaryLocalPath, updated);
1684
+ }
1685
+ }
1686
+ printLog(`The contribution has been verified - Result ${isContributionValid}`, LogLevel.DEBUG);
1687
+ // Create a new contribution document.
1688
+ const contributionDoc = await firestore
1689
+ .collection(getContributionsCollectionPath(ceremonyId, circuitId))
1690
+ .doc()
1691
+ .get();
1692
+ // Step (1.A.4).
1693
+ if (isContributionValid) {
1694
+ // Sleep ~3 seconds to wait for verification transcription.
1695
+ await sleep(3000);
1696
+ // Step (1.A.4.A.1).
1697
+ if (isUsingVM) {
1698
+ // Retrieve the contribution hash from the command output.
1699
+ lastZkeyBlake2bHash = await retrieveCommandOutput(ssm, vmInstanceId, commandId);
1700
+ const hashRegex = /[a-fA-F0-9]{64}/;
1701
+ const match = lastZkeyBlake2bHash.match(hashRegex);
1702
+ lastZkeyBlake2bHash = match.at(0);
1703
+ // re upload the formatted verification transcript
1704
+ await uploadFileToBucket(bucketName, verificationTranscriptStoragePathAndFilename, verificationTranscriptTemporaryLocalPath, true);
1705
+ }
1706
+ else {
1707
+ // Upload verification transcript.
1708
+ /// nb. do not use multi-part upload here due to small file size.
1709
+ await uploadFileToBucket(bucketName, verificationTranscriptStoragePathAndFilename, verificationTranscriptTemporaryLocalPath, true);
1710
+ }
1711
+ // Compute verification transcript hash.
1712
+ transcriptBlake2bHash = await blake512FromPath(verificationTranscriptTemporaryLocalPath);
1713
+ // Free resources by unlinking transcript temporary file.
1714
+ fs.unlinkSync(verificationTranscriptTemporaryLocalPath);
1715
+ // Filter participant contributions to find the data related to the one verified.
1716
+ const participantContributions = contributions.filter((contribution) => !!contribution.hash && !!contribution.computationTime && !contribution.doc);
1717
+ /// @dev (there must be only one contribution with an empty 'doc' field).
1718
+ if (participantContributions.length !== 1)
1719
+ logAndThrowError(SPECIFIC_ERRORS.SE_VERIFICATION_NO_PARTICIPANT_CONTRIBUTION_DATA);
1720
+ // Get contribution computation time.
1721
+ contributionComputationTime = contributions.at(0).computationTime;
1722
+ // Step (1.A.4.A.2).
1723
+ batch.create(contributionDoc.ref, {
1724
+ participantId: participantDoc.id,
1725
+ contributionComputationTime,
1726
+ verificationComputationTime: verifyCloudFunctionExecutionTime,
1727
+ zkeyIndex: isFinalizing ? finalContributionIndex : lastZkeyIndex,
1728
+ files: {
1729
+ transcriptFilename: verificationTranscriptCompleteFilename,
1730
+ lastZkeyFilename,
1731
+ transcriptStoragePath: verificationTranscriptStoragePathAndFilename,
1732
+ lastZkeyStoragePath,
1733
+ transcriptBlake2bHash,
1734
+ lastZkeyBlake2bHash
1735
+ },
1736
+ verificationSoftware: {
1737
+ name: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_NAME),
1738
+ version: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_VERSION),
1739
+ commitHash: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_COMMIT_HASH)
1740
+ },
1741
+ valid: isContributionValid,
1742
+ lastUpdated: getCurrentServerTimestampInMillis()
1743
+ });
1744
+ verifyContributionTimer.stop();
1745
+ verifyCloudFunctionTime = verifyContributionTimer.ms();
1628
1746
  }
1629
1747
  else {
1630
- // Upload verification transcript.
1631
- /// nb. do not use multi-part upload here due to small file size.
1632
- await uploadFileToBucket(bucketName, verificationTranscriptStoragePathAndFilename, verificationTranscriptTemporaryLocalPath, true);
1748
+ // Step (1.A.4.B).
1749
+ // Free-up storage by deleting invalid contribution.
1750
+ await deleteObject(bucketName, lastZkeyStoragePath);
1751
+ // Step (1.A.4.B.1).
1752
+ batch.create(contributionDoc.ref, {
1753
+ participantId: participantDoc.id,
1754
+ verificationComputationTime: verifyCloudFunctionExecutionTime,
1755
+ zkeyIndex: isFinalizing ? finalContributionIndex : lastZkeyIndex,
1756
+ verificationSoftware: {
1757
+ name: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_NAME),
1758
+ version: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_VERSION),
1759
+ commitHash: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_COMMIT_HASH)
1760
+ },
1761
+ valid: isContributionValid,
1762
+ lastUpdated: getCurrentServerTimestampInMillis()
1763
+ });
1764
+ }
1765
+ // Stop VM instance
1766
+ if (isUsingVM) {
1767
+ // using try and catch as the VM stopping function can throw
1768
+ // however we want to continue without stopping as the
1769
+ // verification was valid, and inform the coordinator
1770
+ try {
1771
+ await stopEC2Instance(ec2, vmInstanceId);
1772
+ }
1773
+ catch (error) {
1774
+ printLog(`Error while stopping VM instance ${vmInstanceId} - Error ${error}`, LogLevel.WARN);
1775
+ }
1776
+ }
1777
+ // Step (1.A.4.C)
1778
+ if (!isFinalizing) {
1779
+ // Step (1.A.4.C.1)
1780
+ // Compute new average contribution/verification time.
1781
+ fullContributionTime = Number(verificationStartedAt) - Number(contributionStartedAt);
1782
+ const newAvgContributionComputationTime = avgContributionComputationTime > 0
1783
+ ? (avgContributionComputationTime + contributionComputationTime) / 2
1784
+ : contributionComputationTime;
1785
+ const newAvgFullContributionTime = avgFullContributionTime > 0
1786
+ ? (avgFullContributionTime + fullContributionTime) / 2
1787
+ : fullContributionTime;
1788
+ const newAvgVerifyCloudFunctionTime = avgVerifyCloudFunctionTime > 0
1789
+ ? (avgVerifyCloudFunctionTime + verifyCloudFunctionTime) / 2
1790
+ : verifyCloudFunctionTime;
1791
+ // Prepare tx to update circuit average contribution/verification time.
1792
+ const updatedCircuitDoc = await getDocumentById(getCircuitsCollectionPath(ceremonyId), circuitId);
1793
+ const { waitingQueue: updatedWaitingQueue } = updatedCircuitDoc.data();
1794
+ /// @dev this must happen only for valid contributions.
1795
+ batch.update(circuitDoc.ref, {
1796
+ avgTimings: {
1797
+ contributionComputation: isContributionValid
1798
+ ? newAvgContributionComputationTime
1799
+ : avgContributionComputationTime,
1800
+ fullContribution: isContributionValid
1801
+ ? newAvgFullContributionTime
1802
+ : avgFullContributionTime,
1803
+ verifyCloudFunction: isContributionValid
1804
+ ? newAvgVerifyCloudFunctionTime
1805
+ : avgVerifyCloudFunctionTime
1806
+ },
1807
+ waitingQueue: {
1808
+ ...updatedWaitingQueue,
1809
+ completedContributions: isContributionValid
1810
+ ? completedContributions + 1
1811
+ : completedContributions,
1812
+ failedContributions: isContributionValid ? failedContributions : failedContributions + 1
1813
+ },
1814
+ lastUpdated: getCurrentServerTimestampInMillis()
1815
+ });
1816
+ }
1817
+ // Step (2).
1818
+ await batch.commit();
1819
+ printLog(`The contribution #${isFinalizing ? finalContributionIndex : lastZkeyIndex} of circuit ${circuitId} (ceremony ${ceremonyId}) has been verified as ${isContributionValid ? "valid" : "invalid"} for the participant ${participantDoc.id}`, LogLevel.INFO);
1820
+ };
1821
+ // Step (1).
1822
+ if (isContributing || isFinalizing) {
1823
+ // Prepare timer.
1824
+ verificationTaskTimer.start();
1825
+ // Step (1.A.3.0).
1826
+ if (isUsingVM) {
1827
+ printLog(`Starting the VM mechanism`, LogLevel.DEBUG);
1828
+ // Prepare for VM execution.
1829
+ let isVMRunning = false; // true when the VM is up, otherwise false.
1830
+ // Step (1.A.3.1).
1831
+ await startEC2Instance(ec2, vmInstanceId);
1832
+ await sleep(60000); // nb. wait for VM startup (1 mins + retry).
1833
+ // Check if the startup is running.
1834
+ isVMRunning = await checkIfVMRunning(ec2, vmInstanceId);
1835
+ printLog(`VM running: ${isVMRunning}`, LogLevel.DEBUG);
1836
+ // Step (1.A.3.2).
1837
+ // Prepare.
1838
+ const verificationCommand = vmContributionVerificationCommand(bucketName, lastZkeyStoragePath, verificationTranscriptStoragePathAndFilename);
1839
+ // Run.
1840
+ commandId = await runCommandUsingSSM(ssm, vmInstanceId, verificationCommand);
1841
+ printLog(`Starting the execution of command ${commandId}`, LogLevel.DEBUG);
1842
+ // Step (1.A.3.3).
1843
+ return await waitForVMCommandExecution(ssm, vmInstanceId, commandId)
1844
+ .then(async () => {
1845
+ // Command execution successfully completed.
1846
+ printLog(`Command ${commandId} execution has been successfully completed`, LogLevel.DEBUG);
1847
+ await completeVerification();
1848
+ })
1849
+ .catch((error) => {
1850
+ // Command execution aborted.
1851
+ printLog(`Command ${commandId} execution has been aborted - Error ${error}`, LogLevel.WARN);
1852
+ logAndThrowError(COMMON_ERRORS.CM_INVALID_COMMAND_EXECUTION);
1853
+ });
1633
1854
  }
1634
- // Compute verification transcript hash.
1635
- transcriptBlake2bHash = await blake512FromPath(verificationTranscriptTemporaryLocalPath);
1636
- // Free resources by unlinking transcript temporary file.
1637
- fs.unlinkSync(verificationTranscriptTemporaryLocalPath);
1638
- // Filter participant contributions to find the data related to the one verified.
1639
- const participantContributions = contributions.filter((contribution) => !!contribution.hash && !!contribution.computationTime && !contribution.doc);
1640
- /// @dev (there must be only one contribution with an empty 'doc' field).
1641
- if (participantContributions.length !== 1)
1642
- logAndThrowError(SPECIFIC_ERRORS.SE_VERIFICATION_NO_PARTICIPANT_CONTRIBUTION_DATA);
1643
- // Get contribution computation time.
1644
- contributionComputationTime = contributions.at(0).computationTime;
1645
- // Step (1.A.4.A.2).
1646
- batch.create(contributionDoc.ref, {
1647
- participantId: participantDoc.id,
1648
- contributionComputationTime,
1649
- verificationComputationTime: verifyCloudFunctionExecutionTime,
1650
- zkeyIndex: isFinalizing ? finalContributionIndex : lastZkeyIndex,
1651
- files: {
1652
- transcriptFilename: verificationTranscriptCompleteFilename,
1653
- lastZkeyFilename,
1654
- transcriptStoragePath: verificationTranscriptStoragePathAndFilename,
1655
- lastZkeyStoragePath,
1656
- transcriptBlake2bHash,
1657
- lastZkeyBlake2bHash
1658
- },
1659
- verificationSoftware: {
1660
- name: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_NAME),
1661
- version: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_VERSION),
1662
- commitHash: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_COMMIT_HASH)
1663
- },
1664
- valid: isContributionValid,
1665
- lastUpdated: getCurrentServerTimestampInMillis()
1666
- });
1667
- verifyContributionTimer.stop();
1668
- verifyCloudFunctionTime = verifyContributionTimer.ms();
1669
- }
1670
- else {
1671
- // Step (1.A.4.B).
1672
- // Free-up storage by deleting invalid contribution.
1673
- await deleteObject(bucketName, lastZkeyStoragePath);
1674
- // Step (1.A.4.B.1).
1675
- batch.create(contributionDoc.ref, {
1676
- participantId: participantDoc.id,
1677
- verificationComputationTime: verifyCloudFunctionExecutionTime,
1678
- zkeyIndex: isFinalizing ? finalContributionIndex : lastZkeyIndex,
1679
- verificationSoftware: {
1680
- name: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_NAME),
1681
- version: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_VERSION),
1682
- commitHash: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_COMMIT_HASH)
1683
- },
1684
- valid: isContributionValid,
1685
- lastUpdated: getCurrentServerTimestampInMillis()
1686
- });
1687
- }
1688
- // Stop VM instance
1689
- if (isUsingVM)
1690
- await stopEC2Instance(ec2, vmInstanceId);
1691
- // Step (1.A.4.C)
1692
- if (!isFinalizing) {
1693
- // Step (1.A.4.C.1)
1694
- // Compute new average contribution/verification time.
1695
- fullContributionTime = Number(verificationStartedAt) - Number(contributionStartedAt);
1696
- const newAvgContributionComputationTime = avgContributionComputationTime > 0
1697
- ? (avgContributionComputationTime + contributionComputationTime) / 2
1698
- : contributionComputationTime;
1699
- const newAvgFullContributionTime = avgFullContributionTime > 0
1700
- ? (avgFullContributionTime + fullContributionTime) / 2
1701
- : fullContributionTime;
1702
- const newAvgVerifyCloudFunctionTime = avgVerifyCloudFunctionTime > 0
1703
- ? (avgVerifyCloudFunctionTime + verifyCloudFunctionTime) / 2
1704
- : verifyCloudFunctionTime;
1705
- // Prepare tx to update circuit average contribution/verification time.
1706
- const updatedCircuitDoc = await getDocumentById(getCircuitsCollectionPath(ceremonyId), circuitId);
1707
- const { waitingQueue: updatedWaitingQueue } = updatedCircuitDoc.data();
1708
- /// @dev this must happen only for valid contributions.
1709
- batch.update(circuitDoc.ref, {
1710
- avgTimings: {
1711
- contributionComputation: isContributionValid
1712
- ? newAvgContributionComputationTime
1713
- : avgContributionComputationTime,
1714
- fullContribution: isContributionValid ? newAvgFullContributionTime : avgFullContributionTime,
1715
- verifyCloudFunction: isContributionValid
1716
- ? newAvgVerifyCloudFunctionTime
1717
- : avgVerifyCloudFunctionTime
1718
- },
1719
- waitingQueue: {
1720
- ...updatedWaitingQueue,
1721
- completedContributions: isContributionValid
1722
- ? completedContributions + 1
1723
- : completedContributions,
1724
- failedContributions: isContributionValid ? failedContributions : failedContributions + 1
1725
- },
1726
- lastUpdated: getCurrentServerTimestampInMillis()
1727
- });
1728
- }
1729
- // Step (2).
1730
- await batch.commit();
1731
- printLog(`The contribution #${isFinalizing ? finalContributionIndex : lastZkeyIndex} of circuit ${circuitId} (ceremony ${ceremonyId}) has been verified as ${isContributionValid ? "valid" : "invalid"} for the participant ${participantDoc.id}`, LogLevel.DEBUG);
1732
- };
1733
- // Step (1).
1734
- if (isContributing || isFinalizing) {
1735
- // Prepare timer.
1736
- verificationTaskTimer.start();
1737
- // Step (1.A.3.0).
1738
- if (isUsingVM) {
1739
- printLog(`Starting the VM mechanism`, LogLevel.DEBUG);
1740
- // Prepare for VM execution.
1741
- let isVMRunning = false; // true when the VM is up, otherwise false.
1742
- // Step (1.A.3.1).
1743
- await startEC2Instance(ec2, vmInstanceId);
1744
- await sleep(60000); // nb. wait for VM startup (1 mins + retry).
1745
- // Check if the startup is running.
1746
- isVMRunning = await checkIfVMRunning(ec2, vmInstanceId);
1747
- printLog(`VM running: ${isVMRunning}`, LogLevel.DEBUG);
1748
- // Step (1.A.3.2).
1749
- // Prepare.
1750
- const verificationCommand = vmContributionVerificationCommand(bucketName, lastZkeyStoragePath, verificationTranscriptStoragePathAndFilename);
1751
- // Run.
1752
- commandId = await runCommandUsingSSM(ssm, vmInstanceId, verificationCommand);
1753
- printLog(`Starting the execution of command ${commandId}`, LogLevel.DEBUG);
1754
- // Step (1.A.3.3).
1755
- return new Promise((resolve, reject) => waitForVMCommandExecution(resolve, reject, ssm, vmInstanceId, commandId))
1756
- .then(async () => {
1757
- // Command execution successfully completed.
1758
- printLog(`Command ${commandId} execution has been successfully completed`, LogLevel.DEBUG);
1759
- await completeVerification();
1760
- })
1761
- .catch((error) => {
1762
- // Command execution aborted.
1763
- printLog(`Command ${commandId} execution has been aborted - Error ${error}`, LogLevel.DEBUG);
1764
- logAndThrowError(COMMON_ERRORS.CM_INVALID_COMMAND_EXECUTION);
1765
- });
1766
- }
1767
- else {
1768
1855
  // CF approach.
1769
1856
  printLog(`CF mechanism`, LogLevel.DEBUG);
1770
1857
  const potStoragePath = getPotStorageFilePath(files.potFilename);
1771
1858
  const firstZkeyStoragePath = getZkeyStorageFilePath(prefix, `${prefix}_${genesisZkeyIndex}.zkey`);
1859
+ printLog(`pot file: ${potStoragePath}`, LogLevel.DEBUG);
1860
+ printLog(`zkey file: ${firstZkeyStoragePath}`, LogLevel.DEBUG);
1772
1861
  // Prepare temporary file paths.
1773
1862
  // (nb. these are needed to download the necessary artifacts for verification from AWS S3).
1774
1863
  verificationTranscriptTemporaryLocalPath = createTemporaryLocalPath(verificationTranscriptCompleteFilename);
1775
1864
  const potTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}.pot`);
1776
1865
  const firstZkeyTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}_genesis.zkey`);
1777
1866
  const lastZkeyTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}_last.zkey`);
1867
+ printLog(`pot file: ${potTempFilePath}`, LogLevel.DEBUG);
1868
+ printLog(`firstZkey file: ${firstZkeyTempFilePath}`, LogLevel.DEBUG);
1869
+ printLog(`last zkey file: ${lastZkeyTempFilePath}`, LogLevel.DEBUG);
1778
1870
  // Create and populate transcript.
1779
1871
  const transcriptLogger = createCustomLoggerForFile(verificationTranscriptTemporaryLocalPath);
1780
1872
  transcriptLogger.info(`${isFinalizing ? `Final verification` : `Verification`} transcript for ${prefix} circuit Phase 2 contribution.\n${isFinalizing ? `Coordinator ` : `Contributor # ${Number(lastZkeyIndex)}`} (${contributorOrCoordinatorIdentifier})\n`);
@@ -1784,6 +1876,7 @@ const verifycontribution = functionsV2.https.onCall({ memory: "16GiB", timeoutSe
1784
1876
  await downloadArtifactFromS3Bucket(bucketName, lastZkeyStoragePath, lastZkeyTempFilePath);
1785
1877
  // Step (1.A.4).
1786
1878
  isContributionValid = await zKey.verifyFromInit(firstZkeyTempFilePath, potTempFilePath, lastZkeyTempFilePath, transcriptLogger);
1879
+ await dumpLog(verificationTranscriptTemporaryLocalPath);
1787
1880
  // Compute contribution hash.
1788
1881
  lastZkeyBlake2bHash = await blake512FromPath(lastZkeyTempFilePath);
1789
1882
  // Free resources by unlinking temporary folders.
@@ -1798,6 +1891,11 @@ const verifycontribution = functionsV2.https.onCall({ memory: "16GiB", timeoutSe
1798
1891
  }
1799
1892
  await completeVerification();
1800
1893
  }
1894
+ return null;
1895
+ }
1896
+ catch (error) {
1897
+ logAndThrowError(makeError("unknown", error));
1898
+ return null;
1801
1899
  }
1802
1900
  });
1803
1901
  /**
@@ -1806,9 +1904,9 @@ const verifycontribution = functionsV2.https.onCall({ memory: "16GiB", timeoutSe
1806
1904
  * this does not happen if the participant is actually the coordinator who is finalizing the ceremony.
1807
1905
  */
1808
1906
  const refreshParticipantAfterContributionVerification = functionsV1
1809
- .region('europe-west1')
1907
+ .region("europe-west1")
1810
1908
  .runWith({
1811
- memory: "512MB"
1909
+ memory: "1GB"
1812
1910
  })
1813
1911
  .firestore.document(`/${commonTerms.collections.ceremonies.name}/{ceremony}/${commonTerms.collections.circuits.name}/{circuit}/${commonTerms.collections.contributions.name}/{contributions}`)
1814
1912
  .onCreate(async (createdContribution) => {
@@ -1859,7 +1957,7 @@ const refreshParticipantAfterContributionVerification = functionsV1
1859
1957
  lastUpdated: getCurrentServerTimestampInMillis()
1860
1958
  });
1861
1959
  await batch.commit();
1862
- printLog(`Participant ${participantId} refreshed after contribution ${createdContribution.id} - The participant was finalizing the ceremony ${isFinalizing}`, LogLevel.DEBUG);
1960
+ printLog(`Participant ${participantId} refreshed after contribution ${createdContribution.id} - The participant was finalizing the ceremony? ${isFinalizing}`, LogLevel.INFO);
1863
1961
  });
1864
1962
  /**
1865
1963
  * Finalize the ceremony circuit.
@@ -1867,9 +1965,9 @@ const refreshParticipantAfterContributionVerification = functionsV1
1867
1965
  * and verification key extracted from the circuit final contribution (as part of the ceremony finalization process).
1868
1966
  */
1869
1967
  const finalizeCircuit = functionsV1
1870
- .region('europe-west1')
1968
+ .region("europe-west1")
1871
1969
  .runWith({
1872
- memory: "512MB"
1970
+ memory: "1GB"
1873
1971
  })
1874
1972
  .https.onCall(async (data, context) => {
1875
1973
  if (!context.auth || !context.auth.token.coordinator)
@@ -2013,7 +2111,7 @@ const checkIfBucketIsDedicatedToCeremony = async (bucketName) => {
2013
2111
  const createBucket = functions
2014
2112
  .region("europe-west1")
2015
2113
  .runWith({
2016
- memory: "512MB"
2114
+ memory: "1GB"
2017
2115
  })
2018
2116
  .https.onCall(async (data, context) => {
2019
2117
  // Check if the user has the coordinator claim.
@@ -2024,6 +2122,7 @@ const createBucket = functions
2024
2122
  // Connect to S3 client.
2025
2123
  const S3 = await getS3Client();
2026
2124
  try {
2125
+ printLog(`Creating AWS S3 bucket ${data.bucketName} ...`, LogLevel.LOG);
2027
2126
  // Try to get information about the bucket.
2028
2127
  await S3.send(new HeadBucketCommand({ Bucket: data.bucketName }));
2029
2128
  // If the command succeeded, the bucket exists, throw an error.
@@ -2064,8 +2163,10 @@ const createBucket = functions
2064
2163
  CORSConfiguration: {
2065
2164
  CORSRules: [
2066
2165
  {
2067
- AllowedMethods: ["GET"],
2068
- AllowedOrigins: ["*"]
2166
+ AllowedMethods: ["GET", "PUT"],
2167
+ AllowedOrigins: ["*"],
2168
+ ExposeHeaders: ["ETag", "Content-Length"],
2169
+ AllowedHeaders: ["*"]
2069
2170
  }
2070
2171
  ]
2071
2172
  }
@@ -2101,7 +2202,7 @@ const createBucket = functions
2101
2202
  const checkIfObjectExist = functions
2102
2203
  .region("europe-west1")
2103
2204
  .runWith({
2104
- memory: "512MB"
2205
+ memory: "1GB"
2105
2206
  })
2106
2207
  .https.onCall(async (data, context) => {
2107
2208
  // Check if the user has the coordinator claim.
@@ -2147,7 +2248,7 @@ const checkIfObjectExist = functions
2147
2248
  const generateGetObjectPreSignedUrl = functions
2148
2249
  .region("europe-west1")
2149
2250
  .runWith({
2150
- memory: "512MB"
2251
+ memory: "1GB"
2151
2252
  })
2152
2253
  .https.onCall(async (data, context) => {
2153
2254
  if (!context.auth)
@@ -2187,7 +2288,7 @@ const generateGetObjectPreSignedUrl = functions
2187
2288
  const startMultiPartUpload = functions
2188
2289
  .region("europe-west1")
2189
2290
  .runWith({
2190
- memory: "512MB"
2291
+ memory: "2GB"
2191
2292
  })
2192
2293
  .https.onCall(async (data, context) => {
2193
2294
  if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
@@ -2242,7 +2343,8 @@ const startMultiPartUpload = functions
2242
2343
  const generatePreSignedUrlsParts = functions
2243
2344
  .region("europe-west1")
2244
2345
  .runWith({
2245
- memory: "512MB"
2346
+ memory: "1GB",
2347
+ timeoutSeconds: 300
2246
2348
  })
2247
2349
  .https.onCall(async (data, context) => {
2248
2350
  if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
@@ -2302,7 +2404,7 @@ const generatePreSignedUrlsParts = functions
2302
2404
  const completeMultiPartUpload = functions
2303
2405
  .region("europe-west1")
2304
2406
  .runWith({
2305
- memory: "512MB"
2407
+ memory: "2GB"
2306
2408
  })
2307
2409
  .https.onCall(async (data, context) => {
2308
2410
  if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
@@ -2351,6 +2453,216 @@ const completeMultiPartUpload = functions
2351
2453
  }
2352
2454
  });
2353
2455
 
2456
+ dotenv.config();
2457
+ const { BANDADA_API_URL, BANDADA_GROUP_ID } = process.env;
2458
+ const bandadaApi = new ApiSdk(BANDADA_API_URL);
2459
+ const bandadaValidateProof = functions
2460
+ .region("europe-west1")
2461
+ .runWith({
2462
+ memory: "1GB"
2463
+ })
2464
+ .https.onCall(async (data) => {
2465
+ const VKEY_DATA = {
2466
+ protocol: "groth16",
2467
+ curve: "bn128",
2468
+ nPublic: 3,
2469
+ vk_alpha_1: [
2470
+ "20491192805390485299153009773594534940189261866228447918068658471970481763042",
2471
+ "9383485363053290200918347156157836566562967994039712273449902621266178545958",
2472
+ "1"
2473
+ ],
2474
+ vk_beta_2: [
2475
+ [
2476
+ "6375614351688725206403948262868962793625744043794305715222011528459656738731",
2477
+ "4252822878758300859123897981450591353533073413197771768651442665752259397132"
2478
+ ],
2479
+ [
2480
+ "10505242626370262277552901082094356697409835680220590971873171140371331206856",
2481
+ "21847035105528745403288232691147584728191162732299865338377159692350059136679"
2482
+ ],
2483
+ ["1", "0"]
2484
+ ],
2485
+ vk_gamma_2: [
2486
+ [
2487
+ "10857046999023057135944570762232829481370756359578518086990519993285655852781",
2488
+ "11559732032986387107991004021392285783925812861821192530917403151452391805634"
2489
+ ],
2490
+ [
2491
+ "8495653923123431417604973247489272438418190587263600148770280649306958101930",
2492
+ "4082367875863433681332203403145435568316851327593401208105741076214120093531"
2493
+ ],
2494
+ ["1", "0"]
2495
+ ],
2496
+ vk_delta_2: [
2497
+ [
2498
+ "3697618915467790705869942236922063775466274665053173890632463796679068973252",
2499
+ "14948341351907992175709156460547989243732741534604949238422596319735704165658"
2500
+ ],
2501
+ [
2502
+ "3028459181652799888716942141752307629938889957960373621898607910203491239368",
2503
+ "11380736494786911280692284374675752681598754560757720296073023058533044108340"
2504
+ ],
2505
+ ["1", "0"]
2506
+ ],
2507
+ vk_alphabeta_12: [
2508
+ [
2509
+ [
2510
+ "2029413683389138792403550203267699914886160938906632433982220835551125967885",
2511
+ "21072700047562757817161031222997517981543347628379360635925549008442030252106"
2512
+ ],
2513
+ [
2514
+ "5940354580057074848093997050200682056184807770593307860589430076672439820312",
2515
+ "12156638873931618554171829126792193045421052652279363021382169897324752428276"
2516
+ ],
2517
+ [
2518
+ "7898200236362823042373859371574133993780991612861777490112507062703164551277",
2519
+ "7074218545237549455313236346927434013100842096812539264420499035217050630853"
2520
+ ]
2521
+ ],
2522
+ [
2523
+ [
2524
+ "7077479683546002997211712695946002074877511277312570035766170199895071832130",
2525
+ "10093483419865920389913245021038182291233451549023025229112148274109565435465"
2526
+ ],
2527
+ [
2528
+ "4595479056700221319381530156280926371456704509942304414423590385166031118820",
2529
+ "19831328484489333784475432780421641293929726139240675179672856274388269393268"
2530
+ ],
2531
+ [
2532
+ "11934129596455521040620786944827826205713621633706285934057045369193958244500",
2533
+ "8037395052364110730298837004334506829870972346962140206007064471173334027475"
2534
+ ]
2535
+ ]
2536
+ ],
2537
+ IC: [
2538
+ [
2539
+ "12951059800758687233303204819298121944551181861362200875212570257618182506154",
2540
+ "5751958719396509176593242305268064754837298673622815112953832050159760501392",
2541
+ "1"
2542
+ ],
2543
+ [
2544
+ "9561588427935871983444704959674198910445823619407211599507208879011862515257",
2545
+ "14576201570478094842467636169770180675293504492823217349086195663150934064643",
2546
+ "1"
2547
+ ],
2548
+ [
2549
+ "4811967233483727873912563574622036989372099129165459921963463310078093941559",
2550
+ "1874883809855039536107616044787862082553628089593740724610117059083415551067",
2551
+ "1"
2552
+ ],
2553
+ [
2554
+ "12252730267779308452229639835051322390696643456253768618882001876621526827161",
2555
+ "7899194018737016222260328309937800777948677569409898603827268776967707173231",
2556
+ "1"
2557
+ ]
2558
+ ]
2559
+ };
2560
+ if (!BANDADA_GROUP_ID)
2561
+ throw new Error("BANDADA_GROUP_ID is not defined in .env");
2562
+ const { proof, publicSignals } = data;
2563
+ const isCorrect = groth16.verify(VKEY_DATA, publicSignals, proof);
2564
+ if (!isCorrect)
2565
+ return {
2566
+ valid: false,
2567
+ message: "Invalid proof",
2568
+ token: ""
2569
+ };
2570
+ const commitment = data.publicSignals[1];
2571
+ const isMember = await bandadaApi.isGroupMember(BANDADA_GROUP_ID, commitment);
2572
+ if (!isMember)
2573
+ return {
2574
+ valid: false,
2575
+ message: "Not a member of the group",
2576
+ token: ""
2577
+ };
2578
+ const auth = getAuth();
2579
+ try {
2580
+ await admin.auth().createUser({
2581
+ uid: commitment
2582
+ });
2583
+ }
2584
+ catch (error) {
2585
+ // if user already exist then just pass
2586
+ if (error.code !== "auth/uid-already-exists") {
2587
+ throw new Error(error);
2588
+ }
2589
+ }
2590
+ const token = await auth.createCustomToken(commitment);
2591
+ return {
2592
+ valid: true,
2593
+ message: "Valid proof and group member",
2594
+ token
2595
+ };
2596
+ });
2597
+
2598
+ dotenv.config();
2599
+ const checkNonceOfSIWEAddress = functions
2600
+ .region("europe-west1")
2601
+ .runWith({ memory: "1GB" })
2602
+ .https.onCall(async (data) => {
2603
+ try {
2604
+ const { auth0Token } = data;
2605
+ const result = (await fetch(`${process.env.AUTH0_APPLICATION_URL}/userinfo`, {
2606
+ method: "GET",
2607
+ headers: {
2608
+ "content-type": "application/json",
2609
+ authorization: `Bearer ${auth0Token}`
2610
+ }
2611
+ }).then((_res) => _res.json()));
2612
+ if (!result.sub) {
2613
+ return {
2614
+ valid: false,
2615
+ message: "No user detected. Please check device flow token"
2616
+ };
2617
+ }
2618
+ const auth = getAuth();
2619
+ // check nonce
2620
+ const parts = result.sub.split("|");
2621
+ const address = decodeURIComponent(parts[2]).split(":")[2];
2622
+ const minimumNonce = Number(process.env.ETH_MINIMUM_NONCE);
2623
+ const nonceBlockHeight = "latest"; // process.env.ETH_NONCE_BLOCK_HEIGHT
2624
+ // look up nonce for address @block
2625
+ let nonceOk = true;
2626
+ if (minimumNonce > 0) {
2627
+ const provider = setEthProvider();
2628
+ console.log(`got provider - block # ${await provider.getBlockNumber()}`);
2629
+ const nonce = await provider.getTransactionCount(address, nonceBlockHeight);
2630
+ console.log(`nonce ${nonce}`);
2631
+ nonceOk = nonce >= minimumNonce;
2632
+ }
2633
+ console.log(`checking nonce ${nonceOk}`);
2634
+ if (!nonceOk) {
2635
+ return {
2636
+ valid: false,
2637
+ message: "Eth address does not meet the nonce requirements"
2638
+ };
2639
+ }
2640
+ try {
2641
+ await admin.auth().createUser({
2642
+ displayName: address,
2643
+ uid: address
2644
+ });
2645
+ }
2646
+ catch (error) {
2647
+ // if user already exist then just pass
2648
+ if (error.code !== "auth/uid-already-exists") {
2649
+ throw new Error(error);
2650
+ }
2651
+ }
2652
+ const token = await auth.createCustomToken(address);
2653
+ return {
2654
+ valid: true,
2655
+ token
2656
+ };
2657
+ }
2658
+ catch (error) {
2659
+ return {
2660
+ valid: false,
2661
+ message: `Something went wrong ${error}`
2662
+ };
2663
+ }
2664
+ });
2665
+
2354
2666
  dotenv.config();
2355
2667
  /**
2356
2668
  * Check and remove the current contributor if it doesn't complete the contribution on the specified amount of time.
@@ -2373,7 +2685,7 @@ dotenv.config();
2373
2685
  const checkAndRemoveBlockingContributor = functions
2374
2686
  .region("europe-west1")
2375
2687
  .runWith({
2376
- memory: "512MB"
2688
+ memory: "1GB"
2377
2689
  })
2378
2690
  .pubsub.schedule("every 1 minutes")
2379
2691
  .onRun(async () => {
@@ -2392,7 +2704,7 @@ const checkAndRemoveBlockingContributor = functions
2392
2704
  // Get ceremony circuits.
2393
2705
  const circuits = await getCeremonyCircuits(ceremony.id);
2394
2706
  // Extract ceremony data.
2395
- const { timeoutMechanismType, penalty } = ceremony.data();
2707
+ const { timeoutType: timeoutMechanismType, penalty } = ceremony.data();
2396
2708
  for (const circuit of circuits) {
2397
2709
  if (!circuit.data())
2398
2710
  // Do not use `logAndThrowError` method to avoid the function to exit before checking every ceremony.
@@ -2405,7 +2717,7 @@ const checkAndRemoveBlockingContributor = functions
2405
2717
  // Case (A).
2406
2718
  if (!currentContributor)
2407
2719
  // Do not use `logAndThrowError` method to avoid the function to exit before checking every ceremony.
2408
- printLog(`No current contributor for circuit ${circuit.id} - ceremony ${ceremony.id}`, LogLevel.WARN);
2720
+ printLog(`No current contributor for circuit ${circuit.id} - ceremony ${ceremony.id}`, LogLevel.DEBUG);
2409
2721
  else if (avgFullContribution === 0 &&
2410
2722
  avgContributionComputation === 0 &&
2411
2723
  avgVerifyCloudFunction === 0 &&
@@ -2431,7 +2743,7 @@ const checkAndRemoveBlockingContributor = functions
2431
2743
  ? Number(contributionStartedAt) +
2432
2744
  Number(avgFullContribution) +
2433
2745
  Number(timeoutDynamicThreshold)
2434
- : Number(contributionStartedAt) + Number(fixedTimeWindow) * 60000; // * 60000 = convert minutes to millis.
2746
+ : (Number(contributionStartedAt) + Number(fixedTimeWindow)) * 60000; // * 60000 = convert minutes to millis.
2435
2747
  // Case (D).
2436
2748
  const timeoutExpirationDateInMsForVerificationCloudFunction = contributionStep === "VERIFYING" /* ParticipantContributionStep.VERIFYING */ &&
2437
2749
  !!verificationStartedAt
@@ -2442,17 +2754,18 @@ const checkAndRemoveBlockingContributor = functions
2442
2754
  if (timeoutExpirationDateInMsForBlockingContributor < currentServerTimestamp &&
2443
2755
  (contributionStep === "DOWNLOADING" /* ParticipantContributionStep.DOWNLOADING */ ||
2444
2756
  contributionStep === "COMPUTING" /* ParticipantContributionStep.COMPUTING */ ||
2445
- contributionStep === "UPLOADING" /* ParticipantContributionStep.UPLOADING */))
2757
+ contributionStep === "UPLOADING" /* ParticipantContributionStep.UPLOADING */ ||
2758
+ contributionStep === "COMPLETED" /* ParticipantContributionStep.COMPLETED */))
2446
2759
  timeoutType = "BLOCKING_CONTRIBUTION" /* TimeoutType.BLOCKING_CONTRIBUTION */;
2447
2760
  if (timeoutExpirationDateInMsForVerificationCloudFunction > 0 &&
2448
2761
  timeoutExpirationDateInMsForVerificationCloudFunction < currentServerTimestamp &&
2449
2762
  contributionStep === "VERIFYING" /* ParticipantContributionStep.VERIFYING */)
2450
2763
  timeoutType = "BLOCKING_CLOUD_FUNCTION" /* TimeoutType.BLOCKING_CLOUD_FUNCTION */;
2451
- printLog(`${timeoutType} detected for circuit ${circuit.id} - ceremony ${ceremony.id}`, LogLevel.DEBUG);
2452
2764
  if (!timeoutType)
2453
2765
  // Do not use `logAndThrowError` method to avoid the function to exit before checking every ceremony.
2454
- printLog(`No timeout for circuit ${circuit.id} - ceremony ${ceremony.id}`, LogLevel.WARN);
2766
+ printLog(`No timeout for circuit ${circuit.id} - ceremony ${ceremony.id}`, LogLevel.DEBUG);
2455
2767
  else {
2768
+ printLog(`${timeoutType} detected for circuit ${circuit.id} - ceremony ${ceremony.id}`, LogLevel.WARN);
2456
2769
  // Case (E).
2457
2770
  let nextCurrentContributorId = "";
2458
2771
  // Prepare Firestore batch of txs.
@@ -2503,7 +2816,7 @@ const checkAndRemoveBlockingContributor = functions
2503
2816
  });
2504
2817
  // Send atomic update for Firestore.
2505
2818
  await batch.commit();
2506
- printLog(`The contributor ${participant.id} has been identified as potential blocking contributor. A timeout of type ${timeoutType} has been triggered w/ a penalty of ${timeoutPenaltyInMs} ms`, LogLevel.DEBUG);
2819
+ printLog(`The contributor ${participant.id} has been identified as potential blocking contributor. A timeout of type ${timeoutType} has been triggered w/ a penalty of ${timeoutPenaltyInMs} ms`, LogLevel.WARN);
2507
2820
  }
2508
2821
  }
2509
2822
  }
@@ -2519,7 +2832,7 @@ const checkAndRemoveBlockingContributor = functions
2519
2832
  const resumeContributionAfterTimeoutExpiration = functions
2520
2833
  .region("europe-west1")
2521
2834
  .runWith({
2522
- memory: "512MB"
2835
+ memory: "1GB"
2523
2836
  })
2524
2837
  .https.onCall(async (data, context) => {
2525
2838
  if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
@@ -2552,4 +2865,4 @@ const resumeContributionAfterTimeoutExpiration = functions
2552
2865
 
2553
2866
  admin.initializeApp();
2554
2867
 
2555
- export { checkAndPrepareCoordinatorForFinalization, checkAndRemoveBlockingContributor, checkIfObjectExist, checkParticipantForCeremony, completeMultiPartUpload, coordinateCeremonyParticipant, createBucket, finalizeCeremony, finalizeCircuit, generateGetObjectPreSignedUrl, generatePreSignedUrlsParts, initEmptyWaitingQueueForCircuit, permanentlyStoreCurrentContributionTimeAndHash, processSignUpWithCustomClaims, progressToNextCircuitForContribution, progressToNextContributionStep, refreshParticipantAfterContributionVerification, registerAuthUser, resumeContributionAfterTimeoutExpiration, setupCeremony, startCeremony, startMultiPartUpload, stopCeremony, temporaryStoreCurrentContributionMultiPartUploadId, temporaryStoreCurrentContributionUploadedChunkData, verifycontribution };
2868
+ export { bandadaValidateProof, checkAndPrepareCoordinatorForFinalization, checkAndRemoveBlockingContributor, checkIfObjectExist, checkNonceOfSIWEAddress, checkParticipantForCeremony, completeMultiPartUpload, coordinateCeremonyParticipant, createBucket, finalizeCeremony, finalizeCircuit, generateGetObjectPreSignedUrl, generatePreSignedUrlsParts, initEmptyWaitingQueueForCircuit, permanentlyStoreCurrentContributionTimeAndHash, processSignUpWithCustomClaims, progressToNextCircuitForContribution, progressToNextContributionStep, refreshParticipantAfterContributionVerification, registerAuthUser, resumeContributionAfterTimeoutExpiration, setupCeremony, startCeremony, startMultiPartUpload, stopCeremony, temporaryStoreCurrentContributionMultiPartUploadId, temporaryStoreCurrentContributionUploadedChunkData, verifycontribution };