@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
@@ -27,10 +27,13 @@ var path = require('path');
27
27
  var os = require('os');
28
28
  var clientSsm = require('@aws-sdk/client-ssm');
29
29
  var clientEc2 = require('@aws-sdk/client-ec2');
30
+ var ethers = require('ethers');
30
31
  var functionsV1 = require('firebase-functions/v1');
31
32
  var functionsV2 = require('firebase-functions/v2');
32
33
  var timerNode = require('timer-node');
33
34
  var snarkjs = require('snarkjs');
35
+ var apiSdk = require('@bandada/api-sdk');
36
+ var auth = require('firebase-admin/auth');
34
37
 
35
38
  function _interopNamespaceDefault(e) {
36
39
  var n = Object.create(null);
@@ -72,7 +75,7 @@ var LogLevel;
72
75
  * @notice the set of Firebase Functions status codes. The codes are the same at the
73
76
  * ones exposed by {@link https://github.com/grpc/grpc/blob/master/doc/statuscodes.md | gRPC}.
74
77
  * @param errorCode <FunctionsErrorCode> - the set of possible error codes.
75
- * @param message <string> - the error messge.
78
+ * @param message <string> - the error message.
76
79
  * @param [details] <string> - the details of the error (optional).
77
80
  * @returns <HttpsError>
78
81
  */
@@ -144,7 +147,9 @@ const SPECIFIC_ERRORS = {
144
147
  SE_VM_FAILED_COMMAND_EXECUTION: makeError("failed-precondition", "VM command execution failed", "Please, contact the coordinator if this error persists."),
145
148
  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."),
146
149
  SE_VM_CANCELLED_COMMAND_EXECUTION: makeError("cancelled", "VM command execution has been cancelled", "Please, contact the coordinator if this error persists."),
147
- 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.")
150
+ 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."),
151
+ 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."),
152
+ 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.")
148
153
  };
149
154
  /**
150
155
  * A set of common errors.
@@ -163,6 +168,8 @@ const COMMON_ERRORS = {
163
168
  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.")
164
169
  };
165
170
 
171
+ dotenv.config();
172
+ let provider;
166
173
  /**
167
174
  * Return a configured and connected instance of the AWS S3 client.
168
175
  * @dev this method check and utilize the environment variables to configure the connection
@@ -185,6 +192,36 @@ const getS3Client = async () => {
185
192
  region: process.env.AWS_REGION
186
193
  });
187
194
  };
195
+ /**
196
+ * Returns a Prvider, connected via a configured JSON URL or else
197
+ * the ethers.js default provider, using configured API keys.
198
+ * @returns <ethers.providers.Provider> An Eth node provider
199
+ */
200
+ const setEthProvider = () => {
201
+ if (provider)
202
+ return provider;
203
+ console.log(`setting new provider`);
204
+ // Use JSON URL if defined
205
+ // if ((hardhat as any).ethers) {
206
+ // console.log(`using hardhat.ethers provider`)
207
+ // provider = (hardhat as any).ethers.provider
208
+ // } else
209
+ if (process.env.ETH_PROVIDER_JSON_URL) {
210
+ console.log(`JSON URL provider at ${process.env.ETH_PROVIDER_JSON_URL}`);
211
+ provider = new ethers.providers.JsonRpcProvider({
212
+ url: process.env.ETH_PROVIDER_JSON_URL,
213
+ skipFetchSetup: true
214
+ });
215
+ }
216
+ else {
217
+ // Otherwise, connect the default provider with ALchemy, Infura, or both
218
+ provider = ethers.providers.getDefaultProvider("homestead", {
219
+ alchemy: process.env.ETH_PROVIDER_ALCHEMY_API_KEY,
220
+ infura: process.env.ETH_PROVIDER_INFURA_API_KEY
221
+ });
222
+ }
223
+ return provider;
224
+ };
188
225
 
189
226
  dotenv.config();
190
227
  /**
@@ -287,7 +324,7 @@ const queryOpenedCeremonies = async () => {
287
324
  const getCircuitDocumentByPosition = async (ceremonyId, sequencePosition) => {
288
325
  // Query for all ceremony circuits.
289
326
  const circuits = await getCeremonyCircuits(ceremonyId);
290
- // Apply a filter using the sequence postion.
327
+ // Apply a filter using the sequence position.
291
328
  const matchedCircuits = circuits.filter((circuit) => circuit.data().sequencePosition === sequencePosition);
292
329
  if (matchedCircuits.length !== 1)
293
330
  logAndThrowError(COMMON_ERRORS.CM_NO_CIRCUIT_FOR_GIVEN_SEQUENCE_POSITION);
@@ -328,7 +365,7 @@ const downloadArtifactFromS3Bucket = async (bucketName, objectKey, localFilePath
328
365
  const writeStream = node_fs.createWriteStream(localFilePath);
329
366
  const streamPipeline = node_util.promisify(node_stream.pipeline);
330
367
  await streamPipeline(response.body, writeStream);
331
- writeStream.on('finish', () => {
368
+ writeStream.on("finish", () => {
332
369
  writeStream.end();
333
370
  });
334
371
  };
@@ -452,12 +489,14 @@ const htmlEncodeCircuitData = (circuitDocument) => ({
452
489
  const getGitHubVariables = () => {
453
490
  if (!process.env.GITHUB_MINIMUM_FOLLOWERS ||
454
491
  !process.env.GITHUB_MINIMUM_FOLLOWING ||
455
- !process.env.GITHUB_MINIMUM_PUBLIC_REPOS)
492
+ !process.env.GITHUB_MINIMUM_PUBLIC_REPOS ||
493
+ !process.env.GITHUB_MINIMUM_AGE)
456
494
  logAndThrowError(COMMON_ERRORS.CM_WRONG_CONFIGURATION);
457
495
  return {
458
496
  minimumFollowers: Number(process.env.GITHUB_MINIMUM_FOLLOWERS),
459
497
  minimumFollowing: Number(process.env.GITHUB_MINIMUM_FOLLOWING),
460
- minimumPublicRepos: Number(process.env.GITHUB_MINIMUM_PUBLIC_REPOS)
498
+ minimumPublicRepos: Number(process.env.GITHUB_MINIMUM_PUBLIC_REPOS),
499
+ minimumAge: Number(process.env.GITHUB_MINIMUM_AGE)
461
500
  };
462
501
  };
463
502
  /**
@@ -467,7 +506,7 @@ const getGitHubVariables = () => {
467
506
  const getAWSVariables = () => {
468
507
  if (!process.env.AWS_ACCESS_KEY_ID ||
469
508
  !process.env.AWS_SECRET_ACCESS_KEY ||
470
- !process.env.AWS_ROLE_ARN ||
509
+ !process.env.AWS_INSTANCE_PROFILE_ARN ||
471
510
  !process.env.AWS_AMI_ID ||
472
511
  !process.env.AWS_SNS_TOPIC_ARN)
473
512
  logAndThrowError(COMMON_ERRORS.CM_WRONG_CONFIGURATION);
@@ -475,7 +514,7 @@ const getAWSVariables = () => {
475
514
  accessKeyId: process.env.AWS_ACCESS_KEY_ID,
476
515
  secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
477
516
  region: process.env.AWS_REGION || "eu-central-1",
478
- roleArn: process.env.AWS_ROLE_ARN,
517
+ instanceProfileArn: process.env.AWS_INSTANCE_PROFILE_ARN,
479
518
  amiId: process.env.AWS_AMI_ID,
480
519
  snsTopic: process.env.AWS_SNS_TOPIC_ARN
481
520
  };
@@ -521,7 +560,7 @@ dotenv.config();
521
560
  const registerAuthUser = functions__namespace
522
561
  .region("europe-west1")
523
562
  .runWith({
524
- memory: "512MB"
563
+ memory: "1GB"
525
564
  })
526
565
  .auth.user()
527
566
  .onCreate(async (user) => {
@@ -553,16 +592,18 @@ const registerAuthUser = functions__namespace
553
592
  email === process.env.CUSTOM_CLAIMS_COORDINATOR_EMAIL_ADDRESS_OR_DOMAIN)) {
554
593
  const auth = admin.auth();
555
594
  // if provider == github.com let's use our functions to check the user's reputation
556
- if (user.providerData[0].providerId === "github.com") {
595
+ if (user.providerData.length > 0 && user.providerData[0].providerId === "github.com") {
557
596
  const vars = getGitHubVariables();
558
597
  // this return true or false
559
598
  try {
560
- const { reputable, avatarUrl: avatarURL } = await actions.githubReputation(user.providerData[0].uid, vars.minimumFollowing, vars.minimumFollowers, vars.minimumPublicRepos);
599
+ const { reputable, avatarUrl: avatarURL } = await actions.githubReputation(user.providerData[0].uid, vars.minimumFollowing, vars.minimumFollowers, vars.minimumPublicRepos, vars.minimumAge);
561
600
  if (!reputable) {
562
601
  // Delete user
563
602
  await auth.deleteUser(user.uid);
564
603
  // Throw error
565
- 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.`));
604
+ 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
605
+ ? user.uid
606
+ : 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.`));
566
607
  }
567
608
  // store locally
568
609
  avatarUrl = avatarURL;
@@ -577,13 +618,13 @@ const registerAuthUser = functions__namespace
577
618
  }
578
619
  // Set document (nb. we refer to providerData[0] because we use Github OAuth provider only).
579
620
  // In future releases we might want to loop through the providerData array as we support
580
- // more providers.
621
+ // more providers.
581
622
  await userRef.set({
582
623
  name: encodedDisplayName,
583
624
  encodedDisplayName,
584
625
  // Metadata.
585
626
  creationTime,
586
- lastSignInTime,
627
+ lastSignInTime: lastSignInTime || creationTime,
587
628
  // Optional.
588
629
  email: email || "",
589
630
  emailVerified: emailVerified || false,
@@ -593,7 +634,7 @@ const registerAuthUser = functions__namespace
593
634
  // we want to create a new collection for the users to store the avatars
594
635
  const avatarRef = firestore.collection(actions.commonTerms.collections.avatars.name).doc(uid);
595
636
  await avatarRef.set({
596
- avatarUrl: avatarUrl || "",
637
+ avatarUrl: avatarUrl || ""
597
638
  });
598
639
  printLog(`Authenticated user document with identifier ${uid} has been correctly stored`, LogLevel.DEBUG);
599
640
  printLog(`Authenticated user avatar with identifier ${uid} has been correctly stored`, LogLevel.DEBUG);
@@ -606,7 +647,7 @@ const registerAuthUser = functions__namespace
606
647
  const processSignUpWithCustomClaims = functions__namespace
607
648
  .region("europe-west1")
608
649
  .runWith({
609
- memory: "512MB"
650
+ memory: "1GB"
610
651
  })
611
652
  .auth.user()
612
653
  .onCreate(async (user) => {
@@ -647,7 +688,7 @@ dotenv.config();
647
688
  const startCeremony = functions__namespace
648
689
  .region("europe-west1")
649
690
  .runWith({
650
- memory: "512MB"
691
+ memory: "1GB"
651
692
  })
652
693
  .pubsub.schedule(`every 30 minutes`)
653
694
  .onRun(async () => {
@@ -669,7 +710,7 @@ const startCeremony = functions__namespace
669
710
  const stopCeremony = functions__namespace
670
711
  .region("europe-west1")
671
712
  .runWith({
672
- memory: "512MB"
713
+ memory: "1GB"
673
714
  })
674
715
  .pubsub.schedule(`every 30 minutes`)
675
716
  .onRun(async () => {
@@ -691,7 +732,7 @@ const stopCeremony = functions__namespace
691
732
  const setupCeremony = functions__namespace
692
733
  .region("europe-west1")
693
734
  .runWith({
694
- memory: "512MB"
735
+ memory: "1GB"
695
736
  })
696
737
  .https.onCall(async (data, context) => {
697
738
  // Check if the user has the coordinator claim.
@@ -729,7 +770,9 @@ const setupCeremony = functions__namespace
729
770
  // The VM unique identifier (if any).
730
771
  let vmInstanceId = "";
731
772
  // Get a new circuit document.
732
- const circuitDoc = await firestore.collection(actions.getCircuitsCollectionPath(ceremonyDoc.ref.id)).doc().get();
773
+ const ccp = actions.getCircuitsCollectionPath(ceremonyDoc.ref.id);
774
+ printLog(`CircuitsCollectionPath = ${ccp}`, LogLevel.DEBUG);
775
+ const circuitDoc = await firestore.collection(ccp).doc().get();
733
776
  // Check if using the VM approach for contribution verification.
734
777
  if (circuit.verification.cfOrVm === "VM" /* CircuitContributionVerificationMechanism.VM */) {
735
778
  // VM command to be run at the startup.
@@ -766,12 +809,14 @@ const setupCeremony = functions__namespace
766
809
  }
767
810
  // Encode circuit data.
768
811
  const encodedCircuit = htmlEncodeCircuitData(circuit);
812
+ printLog(`writing circuit data...`, LogLevel.DEBUG);
769
813
  // Prepare tx to write circuit data.
770
814
  batch.create(circuitDoc.ref, {
771
815
  ...encodedCircuit,
772
816
  lastUpdated: getCurrentServerTimestampInMillis()
773
817
  });
774
818
  }
819
+ printLog(`Done handling circuits...`, LogLevel.DEBUG);
775
820
  // Send txs in a batch (to avoid race conditions).
776
821
  await batch.commit();
777
822
  printLog(`Setup completed for ceremony ${ceremonyDoc.id}`, LogLevel.DEBUG);
@@ -784,7 +829,7 @@ const setupCeremony = functions__namespace
784
829
  const initEmptyWaitingQueueForCircuit = functions__namespace
785
830
  .region("europe-west1")
786
831
  .runWith({
787
- memory: "512MB"
832
+ memory: "1GB"
788
833
  })
789
834
  .firestore.document(`/${actions.commonTerms.collections.ceremonies.name}/{ceremony}/${actions.commonTerms.collections.circuits.name}/{circuit}`)
790
835
  .onCreate(async (doc) => {
@@ -816,7 +861,7 @@ const initEmptyWaitingQueueForCircuit = functions__namespace
816
861
  const finalizeCeremony = functions__namespace
817
862
  .region("europe-west1")
818
863
  .runWith({
819
- memory: "512MB"
864
+ memory: "1GB"
820
865
  })
821
866
  .https.onCall(async (data, context) => {
822
867
  if (!context.auth || !context.auth.token.coordinator)
@@ -837,7 +882,7 @@ const finalizeCeremony = functions__namespace
837
882
  // Get ceremony circuits.
838
883
  const circuits = await getCeremonyCircuits(ceremonyId);
839
884
  // Get final contribution for each circuit.
840
- // nb. the `getFinalContributionDocument` checks the existance of the final contribution document (if not present, throws).
885
+ // nb. the `getFinalContributionDocument` checks the existence of the final contribution document (if not present, throws).
841
886
  // Therefore, we just need to call the method without taking any data to verify the pre-condition of having already computed
842
887
  // the final contributions for each ceremony circuit.
843
888
  for await (const circuit of circuits)
@@ -890,9 +935,9 @@ dotenv.config();
890
935
  * @dev true when the participant can participate (1.A, 3.B, 1.D); otherwise false.
891
936
  */
892
937
  const checkParticipantForCeremony = functions__namespace
893
- .region('europe-west1')
938
+ .region("europe-west1")
894
939
  .runWith({
895
- memory: "512MB"
940
+ memory: "1GB"
896
941
  })
897
942
  .https.onCall(async (data, context) => {
898
943
  if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
@@ -961,7 +1006,7 @@ const checkParticipantForCeremony = functions__namespace
961
1006
  participantDoc.ref.update({
962
1007
  status: "EXHUMED" /* ParticipantStatus.EXHUMED */,
963
1008
  contributions,
964
- tempContributionData: tempContributionData ? tempContributionData : firestore.FieldValue.delete(),
1009
+ tempContributionData: tempContributionData || firestore.FieldValue.delete(),
965
1010
  contributionStep: "DOWNLOADING" /* ParticipantContributionStep.DOWNLOADING */,
966
1011
  contributionStartedAt: 0,
967
1012
  verificationStartedAt: firestore.FieldValue.delete(),
@@ -994,9 +1039,9 @@ const checkParticipantForCeremony = functions__namespace
994
1039
  * 2) the participant has just finished the contribution for a circuit (contributionProgress != 0 && status = CONTRIBUTED && contributionStep = COMPLETED).
995
1040
  */
996
1041
  const progressToNextCircuitForContribution = functions__namespace
997
- .region('europe-west1')
1042
+ .region("europe-west1")
998
1043
  .runWith({
999
- memory: "512MB"
1044
+ memory: "1GB"
1000
1045
  })
1001
1046
  .https.onCall(async (data, context) => {
1002
1047
  if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
@@ -1041,9 +1086,9 @@ const progressToNextCircuitForContribution = functions__namespace
1041
1086
  * 5) Completed contribution computation and verification.
1042
1087
  */
1043
1088
  const progressToNextContributionStep = functions__namespace
1044
- .region('europe-west1')
1089
+ .region("europe-west1")
1045
1090
  .runWith({
1046
- memory: "512MB"
1091
+ memory: "1GB"
1047
1092
  })
1048
1093
  .https.onCall(async (data, context) => {
1049
1094
  if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
@@ -1092,9 +1137,9 @@ const progressToNextContributionStep = functions__namespace
1092
1137
  * @dev enable the current contributor to resume a contribution from where it had left off.
1093
1138
  */
1094
1139
  const permanentlyStoreCurrentContributionTimeAndHash = functions__namespace
1095
- .region('europe-west1')
1140
+ .region("europe-west1")
1096
1141
  .runWith({
1097
- memory: "512MB"
1142
+ memory: "1GB"
1098
1143
  })
1099
1144
  .https.onCall(async (data, context) => {
1100
1145
  if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
@@ -1134,9 +1179,9 @@ const permanentlyStoreCurrentContributionTimeAndHash = functions__namespace
1134
1179
  * @dev enable the current contributor to resume a multi-part upload from where it had left off.
1135
1180
  */
1136
1181
  const temporaryStoreCurrentContributionMultiPartUploadId = functions__namespace
1137
- .region('europe-west1')
1182
+ .region("europe-west1")
1138
1183
  .runWith({
1139
- memory: "512MB"
1184
+ memory: "1GB"
1140
1185
  })
1141
1186
  .https.onCall(async (data, context) => {
1142
1187
  if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
@@ -1172,9 +1217,9 @@ const temporaryStoreCurrentContributionMultiPartUploadId = functions__namespace
1172
1217
  * @dev enable the current contributor to resume a multi-part upload from where it had left off.
1173
1218
  */
1174
1219
  const temporaryStoreCurrentContributionUploadedChunkData = functions__namespace
1175
- .region('europe-west1')
1220
+ .region("europe-west1")
1176
1221
  .runWith({
1177
- memory: "512MB"
1222
+ memory: "1GB"
1178
1223
  })
1179
1224
  .https.onCall(async (data, context) => {
1180
1225
  if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
@@ -1214,9 +1259,9 @@ const temporaryStoreCurrentContributionUploadedChunkData = functions__namespace
1214
1259
  * contributed to every selected ceremony circuits (= DONE).
1215
1260
  */
1216
1261
  const checkAndPrepareCoordinatorForFinalization = functions__namespace
1217
- .region('europe-west1')
1262
+ .region("europe-west1")
1218
1263
  .runWith({
1219
- memory: "512MB"
1264
+ memory: "1GB"
1220
1265
  })
1221
1266
  .https.onCall(async (data, context) => {
1222
1267
  if (!context.auth || !context.auth.token.coordinator)
@@ -1293,7 +1338,7 @@ const coordinate = async (participant, circuit, isSingleParticipantCoordination,
1293
1338
  if (isSingleParticipantCoordination) {
1294
1339
  // Scenario (A).
1295
1340
  if (emptyWaitingQueue) {
1296
- printLog(`Coordinate - executing scenario A - emptyWaitingQueue`, LogLevel.DEBUG);
1341
+ printLog(`Coordinate - executing scenario A - emptyWaitingQueue`, LogLevel.INFO);
1297
1342
  // Update.
1298
1343
  newCurrentContributorId = participant.id;
1299
1344
  newParticipantStatus = "CONTRIBUTING" /* ParticipantStatus.CONTRIBUTING */;
@@ -1302,14 +1347,14 @@ const coordinate = async (participant, circuit, isSingleParticipantCoordination,
1302
1347
  }
1303
1348
  // Scenario (A).
1304
1349
  else if (participantResumingAfterTimeoutExpiration) {
1305
- printLog(`Coordinate - executing scenario A - single - participantResumingAfterTimeoutExpiration`, LogLevel.DEBUG);
1350
+ printLog(`Coordinate - executing scenario A - single - participantResumingAfterTimeoutExpiration`, LogLevel.INFO);
1306
1351
  newParticipantStatus = "CONTRIBUTING" /* ParticipantStatus.CONTRIBUTING */;
1307
1352
  newContributionStep = "DOWNLOADING" /* ParticipantContributionStep.DOWNLOADING */;
1308
1353
  newCurrentContributorId = participant.id;
1309
1354
  }
1310
1355
  // Scenario (B).
1311
1356
  else if (participantIsNotCurrentContributor) {
1312
- printLog(`Coordinate - executing scenario B - single - participantIsNotCurrentContributor`, LogLevel.DEBUG);
1357
+ printLog(`Coordinate - executing scenario B - single - participantIsNotCurrentContributor`, LogLevel.INFO);
1313
1358
  newCurrentContributorId = currentContributor;
1314
1359
  newParticipantStatus = "WAITING" /* ParticipantStatus.WAITING */;
1315
1360
  newContributors.push(participant.id);
@@ -1328,7 +1373,7 @@ const coordinate = async (participant, circuit, isSingleParticipantCoordination,
1328
1373
  });
1329
1374
  }
1330
1375
  else if (participantIsCurrentContributor && participantCompletedOneOrAllContributions && !!ceremonyId) {
1331
- printLog(`Coordinate - executing scenario C - multi - participantIsCurrentContributor && participantCompletedOneOrAllContributions`, LogLevel.DEBUG);
1376
+ printLog(`Coordinate - executing scenario C - multi - participantIsCurrentContributor && participantCompletedOneOrAllContributions`, LogLevel.INFO);
1332
1377
  newParticipantStatus = "CONTRIBUTING" /* ParticipantStatus.CONTRIBUTING */;
1333
1378
  newContributionStep = "DOWNLOADING" /* ParticipantContributionStep.DOWNLOADING */;
1334
1379
  // Remove from waiting queue of circuit X.
@@ -1346,7 +1391,7 @@ const coordinate = async (participant, circuit, isSingleParticipantCoordination,
1346
1391
  contributionStartedAt: getCurrentServerTimestampInMillis(),
1347
1392
  lastUpdated: getCurrentServerTimestampInMillis()
1348
1393
  });
1349
- printLog(`Participant ${newCurrentContributorId} is the new current contributor for circuit ${circuit.id}`, LogLevel.DEBUG);
1394
+ printLog(`Participant ${newCurrentContributorId} is the new current contributor for circuit ${circuit.id}`, LogLevel.INFO);
1350
1395
  }
1351
1396
  }
1352
1397
  // Prepare tx - must be done for all Scenarios.
@@ -1366,54 +1411,74 @@ const coordinate = async (participant, circuit, isSingleParticipantCoordination,
1366
1411
  * Wait until the command has completed its execution inside the VM.
1367
1412
  * @dev this method implements a custom interval to check 5 times after 1 minute if the command execution
1368
1413
  * has been completed or not by calling the `retrieveCommandStatus` method.
1369
- * @param {any} resolve the promise.
1370
- * @param {any} reject the promise.
1371
1414
  * @param {SSMClient} ssm the SSM client.
1372
1415
  * @param {string} vmInstanceId the unique identifier of the VM instance.
1373
1416
  * @param {string} commandId the unique identifier of the VM command.
1374
1417
  * @returns <Promise<void>> true when the command execution succeed; otherwise false.
1375
1418
  */
1376
- const waitForVMCommandExecution = (resolve, reject, ssm, vmInstanceId, commandId) => {
1377
- const interval = setInterval(async () => {
1419
+ const waitForVMCommandExecution = (ssm, vmInstanceId, commandId) => new Promise((resolve, reject) => {
1420
+ const poll = async () => {
1378
1421
  try {
1379
1422
  // Get command status.
1380
1423
  const cmdStatus = await actions.retrieveCommandStatus(ssm, vmInstanceId, commandId);
1381
1424
  printLog(`Checking command ${commandId} status => ${cmdStatus}`, LogLevel.DEBUG);
1382
- if (cmdStatus === clientSsm.CommandInvocationStatus.SUCCESS) {
1383
- printLog(`Command ${commandId} successfully completed`, LogLevel.DEBUG);
1384
- // Resolve the promise.
1385
- resolve();
1386
- }
1387
- else if (cmdStatus === clientSsm.CommandInvocationStatus.FAILED) {
1388
- logAndThrowError(SPECIFIC_ERRORS.SE_VM_FAILED_COMMAND_EXECUTION);
1389
- reject();
1390
- }
1391
- else if (cmdStatus === clientSsm.CommandInvocationStatus.TIMED_OUT) {
1392
- logAndThrowError(SPECIFIC_ERRORS.SE_VM_TIMEDOUT_COMMAND_EXECUTION);
1393
- reject();
1394
- }
1395
- else if (cmdStatus === clientSsm.CommandInvocationStatus.CANCELLED) {
1396
- logAndThrowError(SPECIFIC_ERRORS.SE_VM_CANCELLED_COMMAND_EXECUTION);
1397
- reject();
1425
+ let error;
1426
+ switch (cmdStatus) {
1427
+ case clientSsm.CommandInvocationStatus.CANCELLING:
1428
+ case clientSsm.CommandInvocationStatus.CANCELLED: {
1429
+ error = SPECIFIC_ERRORS.SE_VM_CANCELLED_COMMAND_EXECUTION;
1430
+ break;
1431
+ }
1432
+ case clientSsm.CommandInvocationStatus.DELAYED: {
1433
+ error = SPECIFIC_ERRORS.SE_VM_DELAYED_COMMAND_EXECUTION;
1434
+ break;
1435
+ }
1436
+ case clientSsm.CommandInvocationStatus.FAILED: {
1437
+ error = SPECIFIC_ERRORS.SE_VM_FAILED_COMMAND_EXECUTION;
1438
+ break;
1439
+ }
1440
+ case clientSsm.CommandInvocationStatus.TIMED_OUT: {
1441
+ error = SPECIFIC_ERRORS.SE_VM_TIMEDOUT_COMMAND_EXECUTION;
1442
+ break;
1443
+ }
1444
+ case clientSsm.CommandInvocationStatus.IN_PROGRESS:
1445
+ case clientSsm.CommandInvocationStatus.PENDING: {
1446
+ // wait a minute and poll again
1447
+ setTimeout(poll, 60000);
1448
+ return;
1449
+ }
1450
+ case clientSsm.CommandInvocationStatus.SUCCESS: {
1451
+ printLog(`Command ${commandId} successfully completed`, LogLevel.DEBUG);
1452
+ // Resolve the promise.
1453
+ resolve();
1454
+ return;
1455
+ }
1456
+ default: {
1457
+ logAndThrowError(SPECIFIC_ERRORS.SE_VM_UNKNOWN_COMMAND_STATUS);
1458
+ }
1398
1459
  }
1399
- else if (cmdStatus === clientSsm.CommandInvocationStatus.DELAYED) {
1400
- logAndThrowError(SPECIFIC_ERRORS.SE_VM_DELAYED_COMMAND_EXECUTION);
1401
- reject();
1460
+ if (error) {
1461
+ logAndThrowError(error);
1402
1462
  }
1403
1463
  }
1404
1464
  catch (error) {
1405
1465
  printLog(`Invalid command ${commandId} execution`, LogLevel.DEBUG);
1466
+ const ec2 = await createEC2Client();
1467
+ // if it errors out, let's just log it as a warning so the coordinator is aware
1468
+ try {
1469
+ await actions.stopEC2Instance(ec2, vmInstanceId);
1470
+ }
1471
+ catch (stopError) {
1472
+ printLog(`Error while stopping VM instance ${vmInstanceId} - Error ${stopError}`, LogLevel.WARN);
1473
+ }
1406
1474
  if (!error.toString().includes(commandId))
1407
1475
  logAndThrowError(COMMON_ERRORS.CM_INVALID_COMMAND_EXECUTION);
1408
1476
  // Reject the promise.
1409
1477
  reject();
1410
1478
  }
1411
- finally {
1412
- // Clear the interval.
1413
- clearInterval(interval);
1414
- }
1415
- }, 60000); // 1 minute.
1416
- };
1479
+ };
1480
+ setTimeout(poll, 60000);
1481
+ });
1417
1482
  /**
1418
1483
  * This method is used to coordinate the waiting queues of ceremony circuits.
1419
1484
  * @dev this cloud function is triggered whenever an update of a document related to a participant of a ceremony occurs.
@@ -1434,9 +1499,9 @@ const waitForVMCommandExecution = (resolve, reject, ssm, vmInstanceId, commandId
1434
1499
  * - Just completed a contribution or all contributions for each circuit. If yes, coordinate (multi-participant scenario).
1435
1500
  */
1436
1501
  const coordinateCeremonyParticipant = functionsV1__namespace
1437
- .region('europe-west1')
1502
+ .region("europe-west1")
1438
1503
  .runWith({
1439
- memory: "512MB"
1504
+ memory: "1GB"
1440
1505
  })
1441
1506
  .firestore.document(`${actions.commonTerms.collections.ceremonies.name}/{ceremonyId}/${actions.commonTerms.collections.participants.name}/{participantId}`)
1442
1507
  .onUpdate(async (participantChanges) => {
@@ -1452,8 +1517,8 @@ const coordinateCeremonyParticipant = functionsV1__namespace
1452
1517
  // Extract data.
1453
1518
  const { contributionProgress: prevContributionProgress, status: prevStatus, contributionStep: prevContributionStep } = exParticipant.data();
1454
1519
  const { contributionProgress: changedContributionProgress, status: changedStatus, contributionStep: changedContributionStep } = changedParticipant.data();
1455
- printLog(`Coordinate participant ${exParticipant.id} for ceremony ${ceremonyId}`, LogLevel.DEBUG);
1456
- printLog(`Participant status: ${prevStatus} => ${changedStatus} - Participant contribution step: ${prevContributionStep} => ${changedContributionStep}`, LogLevel.DEBUG);
1520
+ printLog(`Coordinate participant ${exParticipant.id} for ceremony ${ceremonyId}`, LogLevel.INFO);
1521
+ printLog(`Participant status: ${prevStatus} => ${changedStatus} - Participant contribution step: ${prevContributionStep} => ${changedContributionStep}`, LogLevel.INFO);
1457
1522
  // Define pre-conditions.
1458
1523
  const participantReadyToContribute = changedStatus === "READY" /* ParticipantStatus.READY */;
1459
1524
  const participantReadyForFirstContribution = participantReadyToContribute && prevContributionProgress === 0;
@@ -1463,8 +1528,8 @@ const coordinateCeremonyParticipant = functionsV1__namespace
1463
1528
  prevContributionProgress !== 0;
1464
1529
  const participantCompletedEveryCircuitContribution = changedStatus === "DONE" /* ParticipantStatus.DONE */ && prevStatus !== "DONE" /* ParticipantStatus.DONE */;
1465
1530
  const participantCompletedContribution = prevContributionProgress === changedContributionProgress &&
1466
- prevStatus === "CONTRIBUTING" /* ParticipantStatus.CONTRIBUTING */ &&
1467
- prevContributionStep === "VERIFYING" /* ParticipantContributionStep.VERIFYING */ &&
1531
+ (prevStatus === "CONTRIBUTING" /* ParticipantStatus.CONTRIBUTING */ ||
1532
+ prevContributionStep === "VERIFYING" /* ParticipantContributionStep.VERIFYING */) &&
1468
1533
  changedStatus === "CONTRIBUTED" /* ParticipantStatus.CONTRIBUTED */ &&
1469
1534
  changedContributionStep === "COMPLETED" /* ParticipantContributionStep.COMPLETED */;
1470
1535
  // Step (2).
@@ -1472,7 +1537,7 @@ const coordinateCeremonyParticipant = functionsV1__namespace
1472
1537
  participantResumingContributionAfterTimeout ||
1473
1538
  participantReadyForNextContribution) {
1474
1539
  // Step (2.A).
1475
- printLog(`Participant is ready for first contribution (${participantReadyForFirstContribution}) or for the next contribution (${participantReadyForNextContribution}) or is resuming after a timeout expiration (${participantResumingContributionAfterTimeout})`, LogLevel.DEBUG);
1540
+ printLog(`Participant is ready for first contribution (${participantReadyForFirstContribution}) or for the next contribution (${participantReadyForNextContribution}) or is resuming after a timeout expiration (${participantResumingContributionAfterTimeout})`, LogLevel.INFO);
1476
1541
  // Get the circuit.
1477
1542
  const circuit = await getCircuitDocumentByPosition(ceremonyId, changedContributionProgress);
1478
1543
  // Coordinate.
@@ -1481,7 +1546,7 @@ const coordinateCeremonyParticipant = functionsV1__namespace
1481
1546
  }
1482
1547
  else if (participantCompletedContribution || participantCompletedEveryCircuitContribution) {
1483
1548
  // Step (2.B).
1484
- printLog(`Participant completed a contribution (${participantCompletedContribution}) or every contribution for each circuit (${participantCompletedEveryCircuitContribution})`, LogLevel.DEBUG);
1549
+ printLog(`Participant completed a contribution (${participantCompletedContribution}) or every contribution for each circuit (${participantCompletedEveryCircuitContribution})`, LogLevel.INFO);
1485
1550
  // Get the circuit.
1486
1551
  const circuit = await getCircuitDocumentByPosition(ceremonyId, prevContributionProgress);
1487
1552
  // Coordinate.
@@ -1505,11 +1570,9 @@ const checkIfVMRunning = async (ec2, vmInstanceId, attempts = 5) => {
1505
1570
  const isVMRunning = await actions.checkIfRunning(ec2, vmInstanceId);
1506
1571
  if (!isVMRunning) {
1507
1572
  printLog(`VM not running, ${attempts - 1} attempts remaining. Retrying in 1 minute...`, LogLevel.DEBUG);
1508
- return await checkIfVMRunning(ec2, vmInstanceId, attempts - 1);
1509
- }
1510
- else {
1511
- return true;
1573
+ return checkIfVMRunning(ec2, vmInstanceId, attempts - 1);
1512
1574
  }
1575
+ return true;
1513
1576
  };
1514
1577
  /**
1515
1578
  * Verify the contribution of a participant computed while contributing to a specific circuit of a ceremony.
@@ -1537,267 +1600,296 @@ const checkIfVMRunning = async (ec2, vmInstanceId, attempts = 5) => {
1537
1600
  * 1.A.4.C.1) If true, update circuit waiting for queue and average timings accordingly to contribution verification results;
1538
1601
  * 2) Send all updates atomically to the Firestore database.
1539
1602
  */
1540
- const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB", timeoutSeconds: 3600, region: 'europe-west1' }, async (request) => {
1541
- if (!request.auth || (!request.auth.token.participant && !request.auth.token.coordinator))
1542
- logAndThrowError(SPECIFIC_ERRORS.SE_AUTH_NO_CURRENT_AUTH_USER);
1543
- if (!request.data.ceremonyId ||
1544
- !request.data.circuitId ||
1545
- !request.data.contributorOrCoordinatorIdentifier ||
1546
- !request.data.bucketName)
1547
- logAndThrowError(COMMON_ERRORS.CM_MISSING_OR_WRONG_INPUT_DATA);
1548
- if (!process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_NAME ||
1549
- !process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_VERSION ||
1550
- !process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_COMMIT_HASH)
1551
- logAndThrowError(COMMON_ERRORS.CM_WRONG_CONFIGURATION);
1552
- // Step (0).
1553
- // Prepare and start timer.
1554
- const verifyContributionTimer = new timerNode.Timer({ label: actions.commonTerms.cloudFunctionsNames.verifyContribution });
1555
- verifyContributionTimer.start();
1556
- // Get DB.
1557
- const firestore = admin.firestore();
1558
- // Prepare batch of txs.
1559
- const batch = firestore.batch();
1560
- // Extract data.
1561
- const { ceremonyId, circuitId, contributorOrCoordinatorIdentifier, bucketName } = request.data;
1562
- const userId = request.auth?.uid;
1563
- // Look for the ceremony, circuit and participant document.
1564
- const ceremonyDoc = await getDocumentById(actions.commonTerms.collections.ceremonies.name, ceremonyId);
1565
- const circuitDoc = await getDocumentById(actions.getCircuitsCollectionPath(ceremonyId), circuitId);
1566
- const participantDoc = await getDocumentById(actions.getParticipantsCollectionPath(ceremonyId), userId);
1567
- if (!ceremonyDoc.data() || !circuitDoc.data() || !participantDoc.data())
1568
- logAndThrowError(COMMON_ERRORS.CM_INEXISTENT_DOCUMENT_DATA);
1569
- // Extract documents data.
1570
- const { state } = ceremonyDoc.data();
1571
- const { status, contributions, verificationStartedAt, contributionStartedAt } = participantDoc.data();
1572
- const { waitingQueue, prefix, avgTimings, verification, files } = circuitDoc.data();
1573
- const { completedContributions, failedContributions } = waitingQueue;
1574
- const { contributionComputation: avgContributionComputationTime, fullContribution: avgFullContributionTime, verifyCloudFunction: avgVerifyCloudFunctionTime } = avgTimings;
1575
- const { cfOrVm, vm } = verification;
1576
- // we might not have it if the circuit is not using VM.
1577
- let vmInstanceId = "";
1578
- if (vm)
1579
- vmInstanceId = vm.vmInstanceId;
1580
- // Define pre-conditions.
1581
- const isFinalizing = state === "CLOSED" /* CeremonyState.CLOSED */ && request.auth && request.auth.token.coordinator; // true only when the coordinator verifies the final contributions.
1582
- const isContributing = status === "CONTRIBUTING" /* ParticipantStatus.CONTRIBUTING */;
1583
- const isUsingVM = cfOrVm === "VM" /* CircuitContributionVerificationMechanism.VM */ && !!vmInstanceId;
1584
- // Prepare state.
1585
- let isContributionValid = false;
1586
- let verifyCloudFunctionExecutionTime = 0; // time spent while executing the verify contribution cloud function.
1587
- let verifyCloudFunctionTime = 0; // time spent while executing the core business logic of this cloud function.
1588
- let fullContributionTime = 0; // time spent while doing non-verification contributions tasks (download, compute, upload).
1589
- let contributionComputationTime = 0; // time spent while computing the contribution.
1590
- let lastZkeyBlake2bHash = ""; // the Blake2B hash of the last zKey.
1591
- let verificationTranscriptTemporaryLocalPath = ""; // the local temporary path for the verification transcript.
1592
- let transcriptBlake2bHash = ""; // the Blake2B hash of the verification transcript.
1593
- let commandId = ""; // the unique identifier of the VM command.
1594
- // Derive necessary data.
1595
- const lastZkeyIndex = actions.formatZkeyIndex(completedContributions + 1);
1596
- const verificationTranscriptCompleteFilename = `${prefix}_${isFinalizing
1597
- ? `${contributorOrCoordinatorIdentifier}_${actions.finalContributionIndex}_verification_transcript.log`
1598
- : `${lastZkeyIndex}_${contributorOrCoordinatorIdentifier}_verification_transcript.log`}`;
1599
- const lastZkeyFilename = `${prefix}_${isFinalizing ? actions.finalContributionIndex : lastZkeyIndex}.zkey`;
1600
- // Prepare state for VM verification (if needed).
1601
- const ec2 = await createEC2Client();
1602
- const ssm = await createSSMClient();
1603
- // Step (1.A.1).
1604
- // Get storage paths.
1605
- const verificationTranscriptStoragePathAndFilename = actions.getTranscriptStorageFilePath(prefix, verificationTranscriptCompleteFilename);
1606
- // the zKey storage path is required to be sent to the VM api
1607
- const lastZkeyStoragePath = actions.getZkeyStorageFilePath(prefix, `${prefix}_${isFinalizing ? actions.finalContributionIndex : lastZkeyIndex}.zkey`);
1608
- const verificationTaskTimer = new timerNode.Timer({ label: `${ceremonyId}-${circuitId}-${participantDoc.id}` });
1609
- const completeVerification = async () => {
1610
- // Stop verification task timer.
1611
- printLog("Completing verification", LogLevel.DEBUG);
1612
- verificationTaskTimer.stop();
1613
- verifyCloudFunctionExecutionTime = verificationTaskTimer.ms();
1614
- if (isUsingVM) {
1615
- // Create temporary path.
1616
- verificationTranscriptTemporaryLocalPath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}.log`);
1617
- await sleep(1000); // wait 1s for file creation.
1618
- // Download from bucket.
1619
- // nb. the transcript MUST be uploaded from the VM by verification commands.
1620
- await downloadArtifactFromS3Bucket(bucketName, verificationTranscriptStoragePathAndFilename, verificationTranscriptTemporaryLocalPath);
1621
- // Read the verification trascript and validate data by checking for core info ("ZKey Ok!").
1622
- const content = fs.readFileSync(verificationTranscriptTemporaryLocalPath, "utf-8");
1623
- if (content.includes("ZKey Ok!"))
1624
- isContributionValid = true;
1625
- // If the contribution is valid, then format and store the trascript.
1626
- if (isContributionValid) {
1627
- // eslint-disable-next-line no-control-regex
1628
- const updated = content.replace(/\x1b[[0-9;]*m/g, "");
1629
- fs.writeFileSync(verificationTranscriptTemporaryLocalPath, updated);
1603
+ const verifycontribution = functionsV2__namespace.https.onCall({ memory: "32GiB", timeoutSeconds: 3600, region: "europe-west1", cpu: 8 }, async (request) => {
1604
+ try {
1605
+ if (!request.auth || (!request.auth.token.participant && !request.auth.token.coordinator))
1606
+ logAndThrowError(SPECIFIC_ERRORS.SE_AUTH_NO_CURRENT_AUTH_USER);
1607
+ if (!request.data.ceremonyId ||
1608
+ !request.data.circuitId ||
1609
+ !request.data.contributorOrCoordinatorIdentifier ||
1610
+ !request.data.bucketName)
1611
+ logAndThrowError(COMMON_ERRORS.CM_MISSING_OR_WRONG_INPUT_DATA);
1612
+ if (!process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_NAME ||
1613
+ !process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_VERSION ||
1614
+ !process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_COMMIT_HASH)
1615
+ logAndThrowError(COMMON_ERRORS.CM_WRONG_CONFIGURATION);
1616
+ const BUCKET_NAME_REGEX = /^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$/;
1617
+ if (!BUCKET_NAME_REGEX.test(request.data.bucketName))
1618
+ logAndThrowError(SPECIFIC_ERRORS.WRONG_BUCKET_NAME);
1619
+ // Step (0).
1620
+ // Prepare and start timer.
1621
+ const verifyContributionTimer = new timerNode.Timer({ label: actions.commonTerms.cloudFunctionsNames.verifyContribution });
1622
+ verifyContributionTimer.start();
1623
+ // Get DB.
1624
+ const firestore = admin.firestore();
1625
+ // Prepare batch of txs.
1626
+ const batch = firestore.batch();
1627
+ // Extract data.
1628
+ const { ceremonyId, circuitId, contributorOrCoordinatorIdentifier, bucketName } = request.data;
1629
+ const userId = request.auth?.uid;
1630
+ // Look for the ceremony, circuit and participant document.
1631
+ const ceremonyDoc = await getDocumentById(actions.commonTerms.collections.ceremonies.name, ceremonyId);
1632
+ const circuitDoc = await getDocumentById(actions.getCircuitsCollectionPath(ceremonyId), circuitId);
1633
+ const participantDoc = await getDocumentById(actions.getParticipantsCollectionPath(ceremonyId), userId);
1634
+ if (!ceremonyDoc.data() || !circuitDoc.data() || !participantDoc.data())
1635
+ logAndThrowError(COMMON_ERRORS.CM_INEXISTENT_DOCUMENT_DATA);
1636
+ // Extract documents data.
1637
+ const { state } = ceremonyDoc.data();
1638
+ const { status, contributions, verificationStartedAt, contributionStartedAt } = participantDoc.data();
1639
+ const { waitingQueue, prefix, avgTimings, verification, files } = circuitDoc.data();
1640
+ const { completedContributions, failedContributions } = waitingQueue;
1641
+ const { contributionComputation: avgContributionComputationTime, fullContribution: avgFullContributionTime, verifyCloudFunction: avgVerifyCloudFunctionTime } = avgTimings;
1642
+ const { cfOrVm, vm } = verification;
1643
+ // we might not have it if the circuit is not using VM.
1644
+ let vmInstanceId = "";
1645
+ if (vm)
1646
+ vmInstanceId = vm.vmInstanceId;
1647
+ // Define pre-conditions.
1648
+ const isFinalizing = state === "CLOSED" /* CeremonyState.CLOSED */ && request.auth && request.auth.token.coordinator; // true only when the coordinator verifies the final contributions.
1649
+ const isContributing = status === "CONTRIBUTING" /* ParticipantStatus.CONTRIBUTING */;
1650
+ const isUsingVM = cfOrVm === "VM" /* CircuitContributionVerificationMechanism.VM */ && !!vmInstanceId;
1651
+ // Prepare state.
1652
+ let isContributionValid = false;
1653
+ let verifyCloudFunctionExecutionTime = 0; // time spent while executing the verify contribution cloud function.
1654
+ let verifyCloudFunctionTime = 0; // time spent while executing the core business logic of this cloud function.
1655
+ let fullContributionTime = 0; // time spent while doing non-verification contributions tasks (download, compute, upload).
1656
+ let contributionComputationTime = 0; // time spent while computing the contribution.
1657
+ let lastZkeyBlake2bHash = ""; // the Blake2B hash of the last zKey.
1658
+ let verificationTranscriptTemporaryLocalPath = ""; // the local temporary path for the verification transcript.
1659
+ let transcriptBlake2bHash = ""; // the Blake2B hash of the verification transcript.
1660
+ let commandId = ""; // the unique identifier of the VM command.
1661
+ // Derive necessary data.
1662
+ const lastZkeyIndex = actions.formatZkeyIndex(completedContributions + 1);
1663
+ const verificationTranscriptCompleteFilename = `${prefix}_${isFinalizing
1664
+ ? `${contributorOrCoordinatorIdentifier}_${actions.finalContributionIndex}_verification_transcript.log`
1665
+ : `${lastZkeyIndex}_${contributorOrCoordinatorIdentifier}_verification_transcript.log`}`;
1666
+ const lastZkeyFilename = `${prefix}_${isFinalizing ? actions.finalContributionIndex : lastZkeyIndex}.zkey`;
1667
+ // Prepare state for VM verification (if needed).
1668
+ const ec2 = await createEC2Client();
1669
+ const ssm = await createSSMClient();
1670
+ // Step (1.A.1).
1671
+ // Get storage paths.
1672
+ const verificationTranscriptStoragePathAndFilename = actions.getTranscriptStorageFilePath(prefix, verificationTranscriptCompleteFilename);
1673
+ // the zKey storage path is required to be sent to the VM api
1674
+ const lastZkeyStoragePath = actions.getZkeyStorageFilePath(prefix, `${prefix}_${isFinalizing ? actions.finalContributionIndex : lastZkeyIndex}.zkey`);
1675
+ const verificationTaskTimer = new timerNode.Timer({ label: `${ceremonyId}-${circuitId}-${participantDoc.id}` });
1676
+ const dumpLog = async (path) => {
1677
+ printLog(`transcript >>>>>>`, LogLevel.DEBUG);
1678
+ try {
1679
+ const data = await fs.promises.readFile(path, "utf8");
1680
+ printLog(data, LogLevel.DEBUG);
1630
1681
  }
1631
- }
1632
- printLog(`The contribution has been verified - Result ${isContributionValid}`, LogLevel.DEBUG);
1633
- // Create a new contribution document.
1634
- const contributionDoc = await firestore
1635
- .collection(actions.getContributionsCollectionPath(ceremonyId, circuitId))
1636
- .doc()
1637
- .get();
1638
- // Step (1.A.4).
1639
- if (isContributionValid) {
1640
- // Sleep ~3 seconds to wait for verification transcription.
1641
- await sleep(3000);
1642
- // Step (1.A.4.A.1).
1682
+ catch (readError) {
1683
+ printLog(readError, LogLevel.ERROR);
1684
+ }
1685
+ };
1686
+ const completeVerification = async () => {
1687
+ // Stop verification task timer.
1688
+ printLog("Completing verification", LogLevel.DEBUG);
1689
+ verificationTaskTimer.stop();
1690
+ verifyCloudFunctionExecutionTime = verificationTaskTimer.ms();
1643
1691
  if (isUsingVM) {
1644
- // Retrieve the contribution hash from the command output.
1645
- lastZkeyBlake2bHash = await actions.retrieveCommandOutput(ssm, vmInstanceId, commandId);
1646
- const hashRegex = /[a-fA-F0-9]{64}/;
1647
- const match = lastZkeyBlake2bHash.match(hashRegex);
1648
- lastZkeyBlake2bHash = match.at(0);
1649
- // re upload the formatted verification transcript
1650
- await uploadFileToBucket(bucketName, verificationTranscriptStoragePathAndFilename, verificationTranscriptTemporaryLocalPath, true);
1692
+ // Create temporary path.
1693
+ verificationTranscriptTemporaryLocalPath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}.log`);
1694
+ await sleep(1000); // wait 1s for file creation.
1695
+ // Download from bucket.
1696
+ // nb. the transcript MUST be uploaded from the VM by verification commands.
1697
+ await downloadArtifactFromS3Bucket(bucketName, verificationTranscriptStoragePathAndFilename, verificationTranscriptTemporaryLocalPath);
1698
+ // Read the verification trascript and validate data by checking for core info ("ZKey Ok!").
1699
+ const content = fs.readFileSync(verificationTranscriptTemporaryLocalPath, "utf-8");
1700
+ if (content.includes("ZKey Ok!"))
1701
+ isContributionValid = true;
1702
+ // If the contribution is valid, then format and store the trascript.
1703
+ if (isContributionValid) {
1704
+ // eslint-disable-next-line no-control-regex
1705
+ const updated = content.replace(/\x1b[[0-9;]*m/g, "");
1706
+ fs.writeFileSync(verificationTranscriptTemporaryLocalPath, updated);
1707
+ }
1708
+ }
1709
+ printLog(`The contribution has been verified - Result ${isContributionValid}`, LogLevel.DEBUG);
1710
+ // Create a new contribution document.
1711
+ const contributionDoc = await firestore
1712
+ .collection(actions.getContributionsCollectionPath(ceremonyId, circuitId))
1713
+ .doc()
1714
+ .get();
1715
+ // Step (1.A.4).
1716
+ if (isContributionValid) {
1717
+ // Sleep ~3 seconds to wait for verification transcription.
1718
+ await sleep(3000);
1719
+ // Step (1.A.4.A.1).
1720
+ if (isUsingVM) {
1721
+ // Retrieve the contribution hash from the command output.
1722
+ lastZkeyBlake2bHash = await actions.retrieveCommandOutput(ssm, vmInstanceId, commandId);
1723
+ const hashRegex = /[a-fA-F0-9]{64}/;
1724
+ const match = lastZkeyBlake2bHash.match(hashRegex);
1725
+ lastZkeyBlake2bHash = match.at(0);
1726
+ // re upload the formatted verification transcript
1727
+ await uploadFileToBucket(bucketName, verificationTranscriptStoragePathAndFilename, verificationTranscriptTemporaryLocalPath, true);
1728
+ }
1729
+ else {
1730
+ // Upload verification transcript.
1731
+ /// nb. do not use multi-part upload here due to small file size.
1732
+ await uploadFileToBucket(bucketName, verificationTranscriptStoragePathAndFilename, verificationTranscriptTemporaryLocalPath, true);
1733
+ }
1734
+ // Compute verification transcript hash.
1735
+ transcriptBlake2bHash = await actions.blake512FromPath(verificationTranscriptTemporaryLocalPath);
1736
+ // Free resources by unlinking transcript temporary file.
1737
+ fs.unlinkSync(verificationTranscriptTemporaryLocalPath);
1738
+ // Filter participant contributions to find the data related to the one verified.
1739
+ const participantContributions = contributions.filter((contribution) => !!contribution.hash && !!contribution.computationTime && !contribution.doc);
1740
+ /// @dev (there must be only one contribution with an empty 'doc' field).
1741
+ if (participantContributions.length !== 1)
1742
+ logAndThrowError(SPECIFIC_ERRORS.SE_VERIFICATION_NO_PARTICIPANT_CONTRIBUTION_DATA);
1743
+ // Get contribution computation time.
1744
+ contributionComputationTime = contributions.at(0).computationTime;
1745
+ // Step (1.A.4.A.2).
1746
+ batch.create(contributionDoc.ref, {
1747
+ participantId: participantDoc.id,
1748
+ contributionComputationTime,
1749
+ verificationComputationTime: verifyCloudFunctionExecutionTime,
1750
+ zkeyIndex: isFinalizing ? actions.finalContributionIndex : lastZkeyIndex,
1751
+ files: {
1752
+ transcriptFilename: verificationTranscriptCompleteFilename,
1753
+ lastZkeyFilename,
1754
+ transcriptStoragePath: verificationTranscriptStoragePathAndFilename,
1755
+ lastZkeyStoragePath,
1756
+ transcriptBlake2bHash,
1757
+ lastZkeyBlake2bHash
1758
+ },
1759
+ verificationSoftware: {
1760
+ name: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_NAME),
1761
+ version: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_VERSION),
1762
+ commitHash: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_COMMIT_HASH)
1763
+ },
1764
+ valid: isContributionValid,
1765
+ lastUpdated: getCurrentServerTimestampInMillis()
1766
+ });
1767
+ verifyContributionTimer.stop();
1768
+ verifyCloudFunctionTime = verifyContributionTimer.ms();
1651
1769
  }
1652
1770
  else {
1653
- // Upload verification transcript.
1654
- /// nb. do not use multi-part upload here due to small file size.
1655
- await uploadFileToBucket(bucketName, verificationTranscriptStoragePathAndFilename, verificationTranscriptTemporaryLocalPath, true);
1771
+ // Step (1.A.4.B).
1772
+ // Free-up storage by deleting invalid contribution.
1773
+ await deleteObject(bucketName, lastZkeyStoragePath);
1774
+ // Step (1.A.4.B.1).
1775
+ batch.create(contributionDoc.ref, {
1776
+ participantId: participantDoc.id,
1777
+ verificationComputationTime: verifyCloudFunctionExecutionTime,
1778
+ zkeyIndex: isFinalizing ? actions.finalContributionIndex : lastZkeyIndex,
1779
+ verificationSoftware: {
1780
+ name: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_NAME),
1781
+ version: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_VERSION),
1782
+ commitHash: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_COMMIT_HASH)
1783
+ },
1784
+ valid: isContributionValid,
1785
+ lastUpdated: getCurrentServerTimestampInMillis()
1786
+ });
1787
+ }
1788
+ // Stop VM instance
1789
+ if (isUsingVM) {
1790
+ // using try and catch as the VM stopping function can throw
1791
+ // however we want to continue without stopping as the
1792
+ // verification was valid, and inform the coordinator
1793
+ try {
1794
+ await actions.stopEC2Instance(ec2, vmInstanceId);
1795
+ }
1796
+ catch (error) {
1797
+ printLog(`Error while stopping VM instance ${vmInstanceId} - Error ${error}`, LogLevel.WARN);
1798
+ }
1799
+ }
1800
+ // Step (1.A.4.C)
1801
+ if (!isFinalizing) {
1802
+ // Step (1.A.4.C.1)
1803
+ // Compute new average contribution/verification time.
1804
+ fullContributionTime = Number(verificationStartedAt) - Number(contributionStartedAt);
1805
+ const newAvgContributionComputationTime = avgContributionComputationTime > 0
1806
+ ? (avgContributionComputationTime + contributionComputationTime) / 2
1807
+ : contributionComputationTime;
1808
+ const newAvgFullContributionTime = avgFullContributionTime > 0
1809
+ ? (avgFullContributionTime + fullContributionTime) / 2
1810
+ : fullContributionTime;
1811
+ const newAvgVerifyCloudFunctionTime = avgVerifyCloudFunctionTime > 0
1812
+ ? (avgVerifyCloudFunctionTime + verifyCloudFunctionTime) / 2
1813
+ : verifyCloudFunctionTime;
1814
+ // Prepare tx to update circuit average contribution/verification time.
1815
+ const updatedCircuitDoc = await getDocumentById(actions.getCircuitsCollectionPath(ceremonyId), circuitId);
1816
+ const { waitingQueue: updatedWaitingQueue } = updatedCircuitDoc.data();
1817
+ /// @dev this must happen only for valid contributions.
1818
+ batch.update(circuitDoc.ref, {
1819
+ avgTimings: {
1820
+ contributionComputation: isContributionValid
1821
+ ? newAvgContributionComputationTime
1822
+ : avgContributionComputationTime,
1823
+ fullContribution: isContributionValid
1824
+ ? newAvgFullContributionTime
1825
+ : avgFullContributionTime,
1826
+ verifyCloudFunction: isContributionValid
1827
+ ? newAvgVerifyCloudFunctionTime
1828
+ : avgVerifyCloudFunctionTime
1829
+ },
1830
+ waitingQueue: {
1831
+ ...updatedWaitingQueue,
1832
+ completedContributions: isContributionValid
1833
+ ? completedContributions + 1
1834
+ : completedContributions,
1835
+ failedContributions: isContributionValid ? failedContributions : failedContributions + 1
1836
+ },
1837
+ lastUpdated: getCurrentServerTimestampInMillis()
1838
+ });
1839
+ }
1840
+ // Step (2).
1841
+ await batch.commit();
1842
+ printLog(`The contribution #${isFinalizing ? actions.finalContributionIndex : lastZkeyIndex} of circuit ${circuitId} (ceremony ${ceremonyId}) has been verified as ${isContributionValid ? "valid" : "invalid"} for the participant ${participantDoc.id}`, LogLevel.INFO);
1843
+ };
1844
+ // Step (1).
1845
+ if (isContributing || isFinalizing) {
1846
+ // Prepare timer.
1847
+ verificationTaskTimer.start();
1848
+ // Step (1.A.3.0).
1849
+ if (isUsingVM) {
1850
+ printLog(`Starting the VM mechanism`, LogLevel.DEBUG);
1851
+ // Prepare for VM execution.
1852
+ let isVMRunning = false; // true when the VM is up, otherwise false.
1853
+ // Step (1.A.3.1).
1854
+ await actions.startEC2Instance(ec2, vmInstanceId);
1855
+ await sleep(60000); // nb. wait for VM startup (1 mins + retry).
1856
+ // Check if the startup is running.
1857
+ isVMRunning = await checkIfVMRunning(ec2, vmInstanceId);
1858
+ printLog(`VM running: ${isVMRunning}`, LogLevel.DEBUG);
1859
+ // Step (1.A.3.2).
1860
+ // Prepare.
1861
+ const verificationCommand = actions.vmContributionVerificationCommand(bucketName, lastZkeyStoragePath, verificationTranscriptStoragePathAndFilename);
1862
+ // Run.
1863
+ commandId = await actions.runCommandUsingSSM(ssm, vmInstanceId, verificationCommand);
1864
+ printLog(`Starting the execution of command ${commandId}`, LogLevel.DEBUG);
1865
+ // Step (1.A.3.3).
1866
+ return await waitForVMCommandExecution(ssm, vmInstanceId, commandId)
1867
+ .then(async () => {
1868
+ // Command execution successfully completed.
1869
+ printLog(`Command ${commandId} execution has been successfully completed`, LogLevel.DEBUG);
1870
+ await completeVerification();
1871
+ })
1872
+ .catch((error) => {
1873
+ // Command execution aborted.
1874
+ printLog(`Command ${commandId} execution has been aborted - Error ${error}`, LogLevel.WARN);
1875
+ logAndThrowError(COMMON_ERRORS.CM_INVALID_COMMAND_EXECUTION);
1876
+ });
1656
1877
  }
1657
- // Compute verification transcript hash.
1658
- transcriptBlake2bHash = await actions.blake512FromPath(verificationTranscriptTemporaryLocalPath);
1659
- // Free resources by unlinking transcript temporary file.
1660
- fs.unlinkSync(verificationTranscriptTemporaryLocalPath);
1661
- // Filter participant contributions to find the data related to the one verified.
1662
- const participantContributions = contributions.filter((contribution) => !!contribution.hash && !!contribution.computationTime && !contribution.doc);
1663
- /// @dev (there must be only one contribution with an empty 'doc' field).
1664
- if (participantContributions.length !== 1)
1665
- logAndThrowError(SPECIFIC_ERRORS.SE_VERIFICATION_NO_PARTICIPANT_CONTRIBUTION_DATA);
1666
- // Get contribution computation time.
1667
- contributionComputationTime = contributions.at(0).computationTime;
1668
- // Step (1.A.4.A.2).
1669
- batch.create(contributionDoc.ref, {
1670
- participantId: participantDoc.id,
1671
- contributionComputationTime,
1672
- verificationComputationTime: verifyCloudFunctionExecutionTime,
1673
- zkeyIndex: isFinalizing ? actions.finalContributionIndex : lastZkeyIndex,
1674
- files: {
1675
- transcriptFilename: verificationTranscriptCompleteFilename,
1676
- lastZkeyFilename,
1677
- transcriptStoragePath: verificationTranscriptStoragePathAndFilename,
1678
- lastZkeyStoragePath,
1679
- transcriptBlake2bHash,
1680
- lastZkeyBlake2bHash
1681
- },
1682
- verificationSoftware: {
1683
- name: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_NAME),
1684
- version: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_VERSION),
1685
- commitHash: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_COMMIT_HASH)
1686
- },
1687
- valid: isContributionValid,
1688
- lastUpdated: getCurrentServerTimestampInMillis()
1689
- });
1690
- verifyContributionTimer.stop();
1691
- verifyCloudFunctionTime = verifyContributionTimer.ms();
1692
- }
1693
- else {
1694
- // Step (1.A.4.B).
1695
- // Free-up storage by deleting invalid contribution.
1696
- await deleteObject(bucketName, lastZkeyStoragePath);
1697
- // Step (1.A.4.B.1).
1698
- batch.create(contributionDoc.ref, {
1699
- participantId: participantDoc.id,
1700
- verificationComputationTime: verifyCloudFunctionExecutionTime,
1701
- zkeyIndex: isFinalizing ? actions.finalContributionIndex : lastZkeyIndex,
1702
- verificationSoftware: {
1703
- name: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_NAME),
1704
- version: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_VERSION),
1705
- commitHash: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_COMMIT_HASH)
1706
- },
1707
- valid: isContributionValid,
1708
- lastUpdated: getCurrentServerTimestampInMillis()
1709
- });
1710
- }
1711
- // Stop VM instance
1712
- if (isUsingVM)
1713
- await actions.stopEC2Instance(ec2, vmInstanceId);
1714
- // Step (1.A.4.C)
1715
- if (!isFinalizing) {
1716
- // Step (1.A.4.C.1)
1717
- // Compute new average contribution/verification time.
1718
- fullContributionTime = Number(verificationStartedAt) - Number(contributionStartedAt);
1719
- const newAvgContributionComputationTime = avgContributionComputationTime > 0
1720
- ? (avgContributionComputationTime + contributionComputationTime) / 2
1721
- : contributionComputationTime;
1722
- const newAvgFullContributionTime = avgFullContributionTime > 0
1723
- ? (avgFullContributionTime + fullContributionTime) / 2
1724
- : fullContributionTime;
1725
- const newAvgVerifyCloudFunctionTime = avgVerifyCloudFunctionTime > 0
1726
- ? (avgVerifyCloudFunctionTime + verifyCloudFunctionTime) / 2
1727
- : verifyCloudFunctionTime;
1728
- // Prepare tx to update circuit average contribution/verification time.
1729
- const updatedCircuitDoc = await getDocumentById(actions.getCircuitsCollectionPath(ceremonyId), circuitId);
1730
- const { waitingQueue: updatedWaitingQueue } = updatedCircuitDoc.data();
1731
- /// @dev this must happen only for valid contributions.
1732
- batch.update(circuitDoc.ref, {
1733
- avgTimings: {
1734
- contributionComputation: isContributionValid
1735
- ? newAvgContributionComputationTime
1736
- : avgContributionComputationTime,
1737
- fullContribution: isContributionValid ? newAvgFullContributionTime : avgFullContributionTime,
1738
- verifyCloudFunction: isContributionValid
1739
- ? newAvgVerifyCloudFunctionTime
1740
- : avgVerifyCloudFunctionTime
1741
- },
1742
- waitingQueue: {
1743
- ...updatedWaitingQueue,
1744
- completedContributions: isContributionValid
1745
- ? completedContributions + 1
1746
- : completedContributions,
1747
- failedContributions: isContributionValid ? failedContributions : failedContributions + 1
1748
- },
1749
- lastUpdated: getCurrentServerTimestampInMillis()
1750
- });
1751
- }
1752
- // Step (2).
1753
- await batch.commit();
1754
- printLog(`The contribution #${isFinalizing ? actions.finalContributionIndex : lastZkeyIndex} of circuit ${circuitId} (ceremony ${ceremonyId}) has been verified as ${isContributionValid ? "valid" : "invalid"} for the participant ${participantDoc.id}`, LogLevel.DEBUG);
1755
- };
1756
- // Step (1).
1757
- if (isContributing || isFinalizing) {
1758
- // Prepare timer.
1759
- verificationTaskTimer.start();
1760
- // Step (1.A.3.0).
1761
- if (isUsingVM) {
1762
- printLog(`Starting the VM mechanism`, LogLevel.DEBUG);
1763
- // Prepare for VM execution.
1764
- let isVMRunning = false; // true when the VM is up, otherwise false.
1765
- // Step (1.A.3.1).
1766
- await actions.startEC2Instance(ec2, vmInstanceId);
1767
- await sleep(60000); // nb. wait for VM startup (1 mins + retry).
1768
- // Check if the startup is running.
1769
- isVMRunning = await checkIfVMRunning(ec2, vmInstanceId);
1770
- printLog(`VM running: ${isVMRunning}`, LogLevel.DEBUG);
1771
- // Step (1.A.3.2).
1772
- // Prepare.
1773
- const verificationCommand = actions.vmContributionVerificationCommand(bucketName, lastZkeyStoragePath, verificationTranscriptStoragePathAndFilename);
1774
- // Run.
1775
- commandId = await actions.runCommandUsingSSM(ssm, vmInstanceId, verificationCommand);
1776
- printLog(`Starting the execution of command ${commandId}`, LogLevel.DEBUG);
1777
- // Step (1.A.3.3).
1778
- return new Promise((resolve, reject) => waitForVMCommandExecution(resolve, reject, ssm, vmInstanceId, commandId))
1779
- .then(async () => {
1780
- // Command execution successfully completed.
1781
- printLog(`Command ${commandId} execution has been successfully completed`, LogLevel.DEBUG);
1782
- await completeVerification();
1783
- })
1784
- .catch((error) => {
1785
- // Command execution aborted.
1786
- printLog(`Command ${commandId} execution has been aborted - Error ${error}`, LogLevel.DEBUG);
1787
- logAndThrowError(COMMON_ERRORS.CM_INVALID_COMMAND_EXECUTION);
1788
- });
1789
- }
1790
- else {
1791
1878
  // CF approach.
1792
1879
  printLog(`CF mechanism`, LogLevel.DEBUG);
1793
1880
  const potStoragePath = actions.getPotStorageFilePath(files.potFilename);
1794
1881
  const firstZkeyStoragePath = actions.getZkeyStorageFilePath(prefix, `${prefix}_${actions.genesisZkeyIndex}.zkey`);
1882
+ printLog(`pot file: ${potStoragePath}`, LogLevel.DEBUG);
1883
+ printLog(`zkey file: ${firstZkeyStoragePath}`, LogLevel.DEBUG);
1795
1884
  // Prepare temporary file paths.
1796
1885
  // (nb. these are needed to download the necessary artifacts for verification from AWS S3).
1797
1886
  verificationTranscriptTemporaryLocalPath = createTemporaryLocalPath(verificationTranscriptCompleteFilename);
1798
1887
  const potTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}.pot`);
1799
1888
  const firstZkeyTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}_genesis.zkey`);
1800
1889
  const lastZkeyTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}_last.zkey`);
1890
+ printLog(`pot file: ${potTempFilePath}`, LogLevel.DEBUG);
1891
+ printLog(`firstZkey file: ${firstZkeyTempFilePath}`, LogLevel.DEBUG);
1892
+ printLog(`last zkey file: ${lastZkeyTempFilePath}`, LogLevel.DEBUG);
1801
1893
  // Create and populate transcript.
1802
1894
  const transcriptLogger = actions.createCustomLoggerForFile(verificationTranscriptTemporaryLocalPath);
1803
1895
  transcriptLogger.info(`${isFinalizing ? `Final verification` : `Verification`} transcript for ${prefix} circuit Phase 2 contribution.\n${isFinalizing ? `Coordinator ` : `Contributor # ${Number(lastZkeyIndex)}`} (${contributorOrCoordinatorIdentifier})\n`);
@@ -1807,6 +1899,7 @@ const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB"
1807
1899
  await downloadArtifactFromS3Bucket(bucketName, lastZkeyStoragePath, lastZkeyTempFilePath);
1808
1900
  // Step (1.A.4).
1809
1901
  isContributionValid = await snarkjs.zKey.verifyFromInit(firstZkeyTempFilePath, potTempFilePath, lastZkeyTempFilePath, transcriptLogger);
1902
+ await dumpLog(verificationTranscriptTemporaryLocalPath);
1810
1903
  // Compute contribution hash.
1811
1904
  lastZkeyBlake2bHash = await actions.blake512FromPath(lastZkeyTempFilePath);
1812
1905
  // Free resources by unlinking temporary folders.
@@ -1821,6 +1914,11 @@ const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB"
1821
1914
  }
1822
1915
  await completeVerification();
1823
1916
  }
1917
+ return null;
1918
+ }
1919
+ catch (error) {
1920
+ logAndThrowError(makeError("unknown", error));
1921
+ return null;
1824
1922
  }
1825
1923
  });
1826
1924
  /**
@@ -1829,9 +1927,9 @@ const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB"
1829
1927
  * this does not happen if the participant is actually the coordinator who is finalizing the ceremony.
1830
1928
  */
1831
1929
  const refreshParticipantAfterContributionVerification = functionsV1__namespace
1832
- .region('europe-west1')
1930
+ .region("europe-west1")
1833
1931
  .runWith({
1834
- memory: "512MB"
1932
+ memory: "1GB"
1835
1933
  })
1836
1934
  .firestore.document(`/${actions.commonTerms.collections.ceremonies.name}/{ceremony}/${actions.commonTerms.collections.circuits.name}/{circuit}/${actions.commonTerms.collections.contributions.name}/{contributions}`)
1837
1935
  .onCreate(async (createdContribution) => {
@@ -1882,7 +1980,7 @@ const refreshParticipantAfterContributionVerification = functionsV1__namespace
1882
1980
  lastUpdated: getCurrentServerTimestampInMillis()
1883
1981
  });
1884
1982
  await batch.commit();
1885
- printLog(`Participant ${participantId} refreshed after contribution ${createdContribution.id} - The participant was finalizing the ceremony ${isFinalizing}`, LogLevel.DEBUG);
1983
+ printLog(`Participant ${participantId} refreshed after contribution ${createdContribution.id} - The participant was finalizing the ceremony? ${isFinalizing}`, LogLevel.INFO);
1886
1984
  });
1887
1985
  /**
1888
1986
  * Finalize the ceremony circuit.
@@ -1890,9 +1988,9 @@ const refreshParticipantAfterContributionVerification = functionsV1__namespace
1890
1988
  * and verification key extracted from the circuit final contribution (as part of the ceremony finalization process).
1891
1989
  */
1892
1990
  const finalizeCircuit = functionsV1__namespace
1893
- .region('europe-west1')
1991
+ .region("europe-west1")
1894
1992
  .runWith({
1895
- memory: "512MB"
1993
+ memory: "1GB"
1896
1994
  })
1897
1995
  .https.onCall(async (data, context) => {
1898
1996
  if (!context.auth || !context.auth.token.coordinator)
@@ -2036,7 +2134,7 @@ const checkIfBucketIsDedicatedToCeremony = async (bucketName) => {
2036
2134
  const createBucket = functions__namespace
2037
2135
  .region("europe-west1")
2038
2136
  .runWith({
2039
- memory: "512MB"
2137
+ memory: "1GB"
2040
2138
  })
2041
2139
  .https.onCall(async (data, context) => {
2042
2140
  // Check if the user has the coordinator claim.
@@ -2047,6 +2145,7 @@ const createBucket = functions__namespace
2047
2145
  // Connect to S3 client.
2048
2146
  const S3 = await getS3Client();
2049
2147
  try {
2148
+ printLog(`Creating AWS S3 bucket ${data.bucketName} ...`, LogLevel.LOG);
2050
2149
  // Try to get information about the bucket.
2051
2150
  await S3.send(new clientS3.HeadBucketCommand({ Bucket: data.bucketName }));
2052
2151
  // If the command succeeded, the bucket exists, throw an error.
@@ -2087,8 +2186,10 @@ const createBucket = functions__namespace
2087
2186
  CORSConfiguration: {
2088
2187
  CORSRules: [
2089
2188
  {
2090
- AllowedMethods: ["GET"],
2091
- AllowedOrigins: ["*"]
2189
+ AllowedMethods: ["GET", "PUT"],
2190
+ AllowedOrigins: ["*"],
2191
+ ExposeHeaders: ["ETag", "Content-Length"],
2192
+ AllowedHeaders: ["*"]
2092
2193
  }
2093
2194
  ]
2094
2195
  }
@@ -2124,7 +2225,7 @@ const createBucket = functions__namespace
2124
2225
  const checkIfObjectExist = functions__namespace
2125
2226
  .region("europe-west1")
2126
2227
  .runWith({
2127
- memory: "512MB"
2228
+ memory: "1GB"
2128
2229
  })
2129
2230
  .https.onCall(async (data, context) => {
2130
2231
  // Check if the user has the coordinator claim.
@@ -2170,7 +2271,7 @@ const checkIfObjectExist = functions__namespace
2170
2271
  const generateGetObjectPreSignedUrl = functions__namespace
2171
2272
  .region("europe-west1")
2172
2273
  .runWith({
2173
- memory: "512MB"
2274
+ memory: "1GB"
2174
2275
  })
2175
2276
  .https.onCall(async (data, context) => {
2176
2277
  if (!context.auth)
@@ -2210,7 +2311,7 @@ const generateGetObjectPreSignedUrl = functions__namespace
2210
2311
  const startMultiPartUpload = functions__namespace
2211
2312
  .region("europe-west1")
2212
2313
  .runWith({
2213
- memory: "512MB"
2314
+ memory: "2GB"
2214
2315
  })
2215
2316
  .https.onCall(async (data, context) => {
2216
2317
  if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
@@ -2265,7 +2366,8 @@ const startMultiPartUpload = functions__namespace
2265
2366
  const generatePreSignedUrlsParts = functions__namespace
2266
2367
  .region("europe-west1")
2267
2368
  .runWith({
2268
- memory: "512MB"
2369
+ memory: "1GB",
2370
+ timeoutSeconds: 300
2269
2371
  })
2270
2372
  .https.onCall(async (data, context) => {
2271
2373
  if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
@@ -2325,7 +2427,7 @@ const generatePreSignedUrlsParts = functions__namespace
2325
2427
  const completeMultiPartUpload = functions__namespace
2326
2428
  .region("europe-west1")
2327
2429
  .runWith({
2328
- memory: "512MB"
2430
+ memory: "2GB"
2329
2431
  })
2330
2432
  .https.onCall(async (data, context) => {
2331
2433
  if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
@@ -2374,6 +2476,216 @@ const completeMultiPartUpload = functions__namespace
2374
2476
  }
2375
2477
  });
2376
2478
 
2479
+ dotenv.config();
2480
+ const { BANDADA_API_URL, BANDADA_GROUP_ID } = process.env;
2481
+ const bandadaApi = new apiSdk.ApiSdk(BANDADA_API_URL);
2482
+ const bandadaValidateProof = functions__namespace
2483
+ .region("europe-west1")
2484
+ .runWith({
2485
+ memory: "1GB"
2486
+ })
2487
+ .https.onCall(async (data) => {
2488
+ const VKEY_DATA = {
2489
+ protocol: "groth16",
2490
+ curve: "bn128",
2491
+ nPublic: 3,
2492
+ vk_alpha_1: [
2493
+ "20491192805390485299153009773594534940189261866228447918068658471970481763042",
2494
+ "9383485363053290200918347156157836566562967994039712273449902621266178545958",
2495
+ "1"
2496
+ ],
2497
+ vk_beta_2: [
2498
+ [
2499
+ "6375614351688725206403948262868962793625744043794305715222011528459656738731",
2500
+ "4252822878758300859123897981450591353533073413197771768651442665752259397132"
2501
+ ],
2502
+ [
2503
+ "10505242626370262277552901082094356697409835680220590971873171140371331206856",
2504
+ "21847035105528745403288232691147584728191162732299865338377159692350059136679"
2505
+ ],
2506
+ ["1", "0"]
2507
+ ],
2508
+ vk_gamma_2: [
2509
+ [
2510
+ "10857046999023057135944570762232829481370756359578518086990519993285655852781",
2511
+ "11559732032986387107991004021392285783925812861821192530917403151452391805634"
2512
+ ],
2513
+ [
2514
+ "8495653923123431417604973247489272438418190587263600148770280649306958101930",
2515
+ "4082367875863433681332203403145435568316851327593401208105741076214120093531"
2516
+ ],
2517
+ ["1", "0"]
2518
+ ],
2519
+ vk_delta_2: [
2520
+ [
2521
+ "3697618915467790705869942236922063775466274665053173890632463796679068973252",
2522
+ "14948341351907992175709156460547989243732741534604949238422596319735704165658"
2523
+ ],
2524
+ [
2525
+ "3028459181652799888716942141752307629938889957960373621898607910203491239368",
2526
+ "11380736494786911280692284374675752681598754560757720296073023058533044108340"
2527
+ ],
2528
+ ["1", "0"]
2529
+ ],
2530
+ vk_alphabeta_12: [
2531
+ [
2532
+ [
2533
+ "2029413683389138792403550203267699914886160938906632433982220835551125967885",
2534
+ "21072700047562757817161031222997517981543347628379360635925549008442030252106"
2535
+ ],
2536
+ [
2537
+ "5940354580057074848093997050200682056184807770593307860589430076672439820312",
2538
+ "12156638873931618554171829126792193045421052652279363021382169897324752428276"
2539
+ ],
2540
+ [
2541
+ "7898200236362823042373859371574133993780991612861777490112507062703164551277",
2542
+ "7074218545237549455313236346927434013100842096812539264420499035217050630853"
2543
+ ]
2544
+ ],
2545
+ [
2546
+ [
2547
+ "7077479683546002997211712695946002074877511277312570035766170199895071832130",
2548
+ "10093483419865920389913245021038182291233451549023025229112148274109565435465"
2549
+ ],
2550
+ [
2551
+ "4595479056700221319381530156280926371456704509942304414423590385166031118820",
2552
+ "19831328484489333784475432780421641293929726139240675179672856274388269393268"
2553
+ ],
2554
+ [
2555
+ "11934129596455521040620786944827826205713621633706285934057045369193958244500",
2556
+ "8037395052364110730298837004334506829870972346962140206007064471173334027475"
2557
+ ]
2558
+ ]
2559
+ ],
2560
+ IC: [
2561
+ [
2562
+ "12951059800758687233303204819298121944551181861362200875212570257618182506154",
2563
+ "5751958719396509176593242305268064754837298673622815112953832050159760501392",
2564
+ "1"
2565
+ ],
2566
+ [
2567
+ "9561588427935871983444704959674198910445823619407211599507208879011862515257",
2568
+ "14576201570478094842467636169770180675293504492823217349086195663150934064643",
2569
+ "1"
2570
+ ],
2571
+ [
2572
+ "4811967233483727873912563574622036989372099129165459921963463310078093941559",
2573
+ "1874883809855039536107616044787862082553628089593740724610117059083415551067",
2574
+ "1"
2575
+ ],
2576
+ [
2577
+ "12252730267779308452229639835051322390696643456253768618882001876621526827161",
2578
+ "7899194018737016222260328309937800777948677569409898603827268776967707173231",
2579
+ "1"
2580
+ ]
2581
+ ]
2582
+ };
2583
+ if (!BANDADA_GROUP_ID)
2584
+ throw new Error("BANDADA_GROUP_ID is not defined in .env");
2585
+ const { proof, publicSignals } = data;
2586
+ const isCorrect = snarkjs.groth16.verify(VKEY_DATA, publicSignals, proof);
2587
+ if (!isCorrect)
2588
+ return {
2589
+ valid: false,
2590
+ message: "Invalid proof",
2591
+ token: ""
2592
+ };
2593
+ const commitment = data.publicSignals[1];
2594
+ const isMember = await bandadaApi.isGroupMember(BANDADA_GROUP_ID, commitment);
2595
+ if (!isMember)
2596
+ return {
2597
+ valid: false,
2598
+ message: "Not a member of the group",
2599
+ token: ""
2600
+ };
2601
+ const auth$1 = auth.getAuth();
2602
+ try {
2603
+ await admin.auth().createUser({
2604
+ uid: commitment
2605
+ });
2606
+ }
2607
+ catch (error) {
2608
+ // if user already exist then just pass
2609
+ if (error.code !== "auth/uid-already-exists") {
2610
+ throw new Error(error);
2611
+ }
2612
+ }
2613
+ const token = await auth$1.createCustomToken(commitment);
2614
+ return {
2615
+ valid: true,
2616
+ message: "Valid proof and group member",
2617
+ token
2618
+ };
2619
+ });
2620
+
2621
+ dotenv.config();
2622
+ const checkNonceOfSIWEAddress = functions__namespace
2623
+ .region("europe-west1")
2624
+ .runWith({ memory: "1GB" })
2625
+ .https.onCall(async (data) => {
2626
+ try {
2627
+ const { auth0Token } = data;
2628
+ const result = (await fetch(`${process.env.AUTH0_APPLICATION_URL}/userinfo`, {
2629
+ method: "GET",
2630
+ headers: {
2631
+ "content-type": "application/json",
2632
+ authorization: `Bearer ${auth0Token}`
2633
+ }
2634
+ }).then((_res) => _res.json()));
2635
+ if (!result.sub) {
2636
+ return {
2637
+ valid: false,
2638
+ message: "No user detected. Please check device flow token"
2639
+ };
2640
+ }
2641
+ const auth$1 = auth.getAuth();
2642
+ // check nonce
2643
+ const parts = result.sub.split("|");
2644
+ const address = decodeURIComponent(parts[2]).split(":")[2];
2645
+ const minimumNonce = Number(process.env.ETH_MINIMUM_NONCE);
2646
+ const nonceBlockHeight = "latest"; // process.env.ETH_NONCE_BLOCK_HEIGHT
2647
+ // look up nonce for address @block
2648
+ let nonceOk = true;
2649
+ if (minimumNonce > 0) {
2650
+ const provider = setEthProvider();
2651
+ console.log(`got provider - block # ${await provider.getBlockNumber()}`);
2652
+ const nonce = await provider.getTransactionCount(address, nonceBlockHeight);
2653
+ console.log(`nonce ${nonce}`);
2654
+ nonceOk = nonce >= minimumNonce;
2655
+ }
2656
+ console.log(`checking nonce ${nonceOk}`);
2657
+ if (!nonceOk) {
2658
+ return {
2659
+ valid: false,
2660
+ message: "Eth address does not meet the nonce requirements"
2661
+ };
2662
+ }
2663
+ try {
2664
+ await admin.auth().createUser({
2665
+ displayName: address,
2666
+ uid: address
2667
+ });
2668
+ }
2669
+ catch (error) {
2670
+ // if user already exist then just pass
2671
+ if (error.code !== "auth/uid-already-exists") {
2672
+ throw new Error(error);
2673
+ }
2674
+ }
2675
+ const token = await auth$1.createCustomToken(address);
2676
+ return {
2677
+ valid: true,
2678
+ token
2679
+ };
2680
+ }
2681
+ catch (error) {
2682
+ return {
2683
+ valid: false,
2684
+ message: `Something went wrong ${error}`
2685
+ };
2686
+ }
2687
+ });
2688
+
2377
2689
  dotenv.config();
2378
2690
  /**
2379
2691
  * Check and remove the current contributor if it doesn't complete the contribution on the specified amount of time.
@@ -2396,7 +2708,7 @@ dotenv.config();
2396
2708
  const checkAndRemoveBlockingContributor = functions__namespace
2397
2709
  .region("europe-west1")
2398
2710
  .runWith({
2399
- memory: "512MB"
2711
+ memory: "1GB"
2400
2712
  })
2401
2713
  .pubsub.schedule("every 1 minutes")
2402
2714
  .onRun(async () => {
@@ -2415,7 +2727,7 @@ const checkAndRemoveBlockingContributor = functions__namespace
2415
2727
  // Get ceremony circuits.
2416
2728
  const circuits = await getCeremonyCircuits(ceremony.id);
2417
2729
  // Extract ceremony data.
2418
- const { timeoutMechanismType, penalty } = ceremony.data();
2730
+ const { timeoutType: timeoutMechanismType, penalty } = ceremony.data();
2419
2731
  for (const circuit of circuits) {
2420
2732
  if (!circuit.data())
2421
2733
  // Do not use `logAndThrowError` method to avoid the function to exit before checking every ceremony.
@@ -2428,7 +2740,7 @@ const checkAndRemoveBlockingContributor = functions__namespace
2428
2740
  // Case (A).
2429
2741
  if (!currentContributor)
2430
2742
  // Do not use `logAndThrowError` method to avoid the function to exit before checking every ceremony.
2431
- printLog(`No current contributor for circuit ${circuit.id} - ceremony ${ceremony.id}`, LogLevel.WARN);
2743
+ printLog(`No current contributor for circuit ${circuit.id} - ceremony ${ceremony.id}`, LogLevel.DEBUG);
2432
2744
  else if (avgFullContribution === 0 &&
2433
2745
  avgContributionComputation === 0 &&
2434
2746
  avgVerifyCloudFunction === 0 &&
@@ -2454,7 +2766,7 @@ const checkAndRemoveBlockingContributor = functions__namespace
2454
2766
  ? Number(contributionStartedAt) +
2455
2767
  Number(avgFullContribution) +
2456
2768
  Number(timeoutDynamicThreshold)
2457
- : Number(contributionStartedAt) + Number(fixedTimeWindow) * 60000; // * 60000 = convert minutes to millis.
2769
+ : (Number(contributionStartedAt) + Number(fixedTimeWindow)) * 60000; // * 60000 = convert minutes to millis.
2458
2770
  // Case (D).
2459
2771
  const timeoutExpirationDateInMsForVerificationCloudFunction = contributionStep === "VERIFYING" /* ParticipantContributionStep.VERIFYING */ &&
2460
2772
  !!verificationStartedAt
@@ -2465,17 +2777,18 @@ const checkAndRemoveBlockingContributor = functions__namespace
2465
2777
  if (timeoutExpirationDateInMsForBlockingContributor < currentServerTimestamp &&
2466
2778
  (contributionStep === "DOWNLOADING" /* ParticipantContributionStep.DOWNLOADING */ ||
2467
2779
  contributionStep === "COMPUTING" /* ParticipantContributionStep.COMPUTING */ ||
2468
- contributionStep === "UPLOADING" /* ParticipantContributionStep.UPLOADING */))
2780
+ contributionStep === "UPLOADING" /* ParticipantContributionStep.UPLOADING */ ||
2781
+ contributionStep === "COMPLETED" /* ParticipantContributionStep.COMPLETED */))
2469
2782
  timeoutType = "BLOCKING_CONTRIBUTION" /* TimeoutType.BLOCKING_CONTRIBUTION */;
2470
2783
  if (timeoutExpirationDateInMsForVerificationCloudFunction > 0 &&
2471
2784
  timeoutExpirationDateInMsForVerificationCloudFunction < currentServerTimestamp &&
2472
2785
  contributionStep === "VERIFYING" /* ParticipantContributionStep.VERIFYING */)
2473
2786
  timeoutType = "BLOCKING_CLOUD_FUNCTION" /* TimeoutType.BLOCKING_CLOUD_FUNCTION */;
2474
- printLog(`${timeoutType} detected for circuit ${circuit.id} - ceremony ${ceremony.id}`, LogLevel.DEBUG);
2475
2787
  if (!timeoutType)
2476
2788
  // Do not use `logAndThrowError` method to avoid the function to exit before checking every ceremony.
2477
- printLog(`No timeout for circuit ${circuit.id} - ceremony ${ceremony.id}`, LogLevel.WARN);
2789
+ printLog(`No timeout for circuit ${circuit.id} - ceremony ${ceremony.id}`, LogLevel.DEBUG);
2478
2790
  else {
2791
+ printLog(`${timeoutType} detected for circuit ${circuit.id} - ceremony ${ceremony.id}`, LogLevel.WARN);
2479
2792
  // Case (E).
2480
2793
  let nextCurrentContributorId = "";
2481
2794
  // Prepare Firestore batch of txs.
@@ -2526,7 +2839,7 @@ const checkAndRemoveBlockingContributor = functions__namespace
2526
2839
  });
2527
2840
  // Send atomic update for Firestore.
2528
2841
  await batch.commit();
2529
- 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);
2842
+ 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);
2530
2843
  }
2531
2844
  }
2532
2845
  }
@@ -2542,7 +2855,7 @@ const checkAndRemoveBlockingContributor = functions__namespace
2542
2855
  const resumeContributionAfterTimeoutExpiration = functions__namespace
2543
2856
  .region("europe-west1")
2544
2857
  .runWith({
2545
- memory: "512MB"
2858
+ memory: "1GB"
2546
2859
  })
2547
2860
  .https.onCall(async (data, context) => {
2548
2861
  if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
@@ -2575,9 +2888,11 @@ const resumeContributionAfterTimeoutExpiration = functions__namespace
2575
2888
 
2576
2889
  admin.initializeApp();
2577
2890
 
2891
+ exports.bandadaValidateProof = bandadaValidateProof;
2578
2892
  exports.checkAndPrepareCoordinatorForFinalization = checkAndPrepareCoordinatorForFinalization;
2579
2893
  exports.checkAndRemoveBlockingContributor = checkAndRemoveBlockingContributor;
2580
2894
  exports.checkIfObjectExist = checkIfObjectExist;
2895
+ exports.checkNonceOfSIWEAddress = checkNonceOfSIWEAddress;
2581
2896
  exports.checkParticipantForCeremony = checkParticipantForCeremony;
2582
2897
  exports.completeMultiPartUpload = completeMultiPartUpload;
2583
2898
  exports.coordinateCeremonyParticipant = coordinateCeremonyParticipant;