@devtion/backend 0.0.0-3df1645 → 0.0.0-477457c

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +2 -2
  2. package/dist/src/functions/index.js +627 -345
  3. package/dist/src/functions/index.mjs +628 -348
  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 +2 -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 +11 -7
  25. package/src/functions/circuit.ts +414 -384
  26. package/src/functions/index.ts +2 -0
  27. package/src/functions/participant.ts +8 -8
  28. package/src/functions/siwe.ts +77 -0
  29. package/src/functions/storage.ts +7 -6
  30. package/src/functions/timeout.ts +14 -13
  31. package/src/functions/user.ts +6 -5
  32. package/src/lib/errors.ts +6 -1
  33. package/src/lib/services.ts +36 -0
  34. package/src/lib/utils.ts +8 -6
  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
2
  * @module @p0tion/backend
3
- * @version 1.0.9
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
  */
@@ -145,7 +148,8 @@ const SPECIFIC_ERRORS = {
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
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."),
148
- 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.")
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.")
149
153
  };
150
154
  /**
151
155
  * A set of common errors.
@@ -164,6 +168,8 @@ const COMMON_ERRORS = {
164
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.")
165
169
  };
166
170
 
171
+ dotenv.config();
172
+ let provider;
167
173
  /**
168
174
  * Return a configured and connected instance of the AWS S3 client.
169
175
  * @dev this method check and utilize the environment variables to configure the connection
@@ -186,6 +192,36 @@ const getS3Client = async () => {
186
192
  region: process.env.AWS_REGION
187
193
  });
188
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
+ };
189
225
 
190
226
  dotenv.config();
191
227
  /**
@@ -288,7 +324,7 @@ const queryOpenedCeremonies = async () => {
288
324
  const getCircuitDocumentByPosition = async (ceremonyId, sequencePosition) => {
289
325
  // Query for all ceremony circuits.
290
326
  const circuits = await getCeremonyCircuits(ceremonyId);
291
- // Apply a filter using the sequence postion.
327
+ // Apply a filter using the sequence position.
292
328
  const matchedCircuits = circuits.filter((circuit) => circuit.data().sequencePosition === sequencePosition);
293
329
  if (matchedCircuits.length !== 1)
294
330
  logAndThrowError(COMMON_ERRORS.CM_NO_CIRCUIT_FOR_GIVEN_SEQUENCE_POSITION);
@@ -453,12 +489,14 @@ const htmlEncodeCircuitData = (circuitDocument) => ({
453
489
  const getGitHubVariables = () => {
454
490
  if (!process.env.GITHUB_MINIMUM_FOLLOWERS ||
455
491
  !process.env.GITHUB_MINIMUM_FOLLOWING ||
456
- !process.env.GITHUB_MINIMUM_PUBLIC_REPOS)
492
+ !process.env.GITHUB_MINIMUM_PUBLIC_REPOS ||
493
+ !process.env.GITHUB_MINIMUM_AGE)
457
494
  logAndThrowError(COMMON_ERRORS.CM_WRONG_CONFIGURATION);
458
495
  return {
459
496
  minimumFollowers: Number(process.env.GITHUB_MINIMUM_FOLLOWERS),
460
497
  minimumFollowing: Number(process.env.GITHUB_MINIMUM_FOLLOWING),
461
- 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)
462
500
  };
463
501
  };
464
502
  /**
@@ -468,7 +506,7 @@ const getGitHubVariables = () => {
468
506
  const getAWSVariables = () => {
469
507
  if (!process.env.AWS_ACCESS_KEY_ID ||
470
508
  !process.env.AWS_SECRET_ACCESS_KEY ||
471
- !process.env.AWS_ROLE_ARN ||
509
+ !process.env.AWS_INSTANCE_PROFILE_ARN ||
472
510
  !process.env.AWS_AMI_ID ||
473
511
  !process.env.AWS_SNS_TOPIC_ARN)
474
512
  logAndThrowError(COMMON_ERRORS.CM_WRONG_CONFIGURATION);
@@ -476,7 +514,7 @@ const getAWSVariables = () => {
476
514
  accessKeyId: process.env.AWS_ACCESS_KEY_ID,
477
515
  secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
478
516
  region: process.env.AWS_REGION || "eu-central-1",
479
- roleArn: process.env.AWS_ROLE_ARN,
517
+ instanceProfileArn: process.env.AWS_INSTANCE_PROFILE_ARN,
480
518
  amiId: process.env.AWS_AMI_ID,
481
519
  snsTopic: process.env.AWS_SNS_TOPIC_ARN
482
520
  };
@@ -522,7 +560,7 @@ dotenv.config();
522
560
  const registerAuthUser = functions__namespace
523
561
  .region("europe-west1")
524
562
  .runWith({
525
- memory: "512MB"
563
+ memory: "1GB"
526
564
  })
527
565
  .auth.user()
528
566
  .onCreate(async (user) => {
@@ -554,11 +592,11 @@ const registerAuthUser = functions__namespace
554
592
  email === process.env.CUSTOM_CLAIMS_COORDINATOR_EMAIL_ADDRESS_OR_DOMAIN)) {
555
593
  const auth = admin.auth();
556
594
  // if provider == github.com let's use our functions to check the user's reputation
557
- if (user.providerData[0].providerId === "github.com") {
595
+ if (user.providerData.length > 0 && user.providerData[0].providerId === "github.com") {
558
596
  const vars = getGitHubVariables();
559
597
  // this return true or false
560
598
  try {
561
- 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);
562
600
  if (!reputable) {
563
601
  // Delete user
564
602
  await auth.deleteUser(user.uid);
@@ -586,7 +624,7 @@ const registerAuthUser = functions__namespace
586
624
  encodedDisplayName,
587
625
  // Metadata.
588
626
  creationTime,
589
- lastSignInTime,
627
+ lastSignInTime: lastSignInTime || creationTime,
590
628
  // Optional.
591
629
  email: email || "",
592
630
  emailVerified: emailVerified || false,
@@ -609,7 +647,7 @@ const registerAuthUser = functions__namespace
609
647
  const processSignUpWithCustomClaims = functions__namespace
610
648
  .region("europe-west1")
611
649
  .runWith({
612
- memory: "512MB"
650
+ memory: "1GB"
613
651
  })
614
652
  .auth.user()
615
653
  .onCreate(async (user) => {
@@ -650,7 +688,7 @@ dotenv.config();
650
688
  const startCeremony = functions__namespace
651
689
  .region("europe-west1")
652
690
  .runWith({
653
- memory: "512MB"
691
+ memory: "1GB"
654
692
  })
655
693
  .pubsub.schedule(`every 30 minutes`)
656
694
  .onRun(async () => {
@@ -672,7 +710,7 @@ const startCeremony = functions__namespace
672
710
  const stopCeremony = functions__namespace
673
711
  .region("europe-west1")
674
712
  .runWith({
675
- memory: "512MB"
713
+ memory: "1GB"
676
714
  })
677
715
  .pubsub.schedule(`every 30 minutes`)
678
716
  .onRun(async () => {
@@ -694,7 +732,7 @@ const stopCeremony = functions__namespace
694
732
  const setupCeremony = functions__namespace
695
733
  .region("europe-west1")
696
734
  .runWith({
697
- memory: "512MB"
735
+ memory: "1GB"
698
736
  })
699
737
  .https.onCall(async (data, context) => {
700
738
  // Check if the user has the coordinator claim.
@@ -732,7 +770,9 @@ const setupCeremony = functions__namespace
732
770
  // The VM unique identifier (if any).
733
771
  let vmInstanceId = "";
734
772
  // Get a new circuit document.
735
- 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();
736
776
  // Check if using the VM approach for contribution verification.
737
777
  if (circuit.verification.cfOrVm === "VM" /* CircuitContributionVerificationMechanism.VM */) {
738
778
  // VM command to be run at the startup.
@@ -769,12 +809,14 @@ const setupCeremony = functions__namespace
769
809
  }
770
810
  // Encode circuit data.
771
811
  const encodedCircuit = htmlEncodeCircuitData(circuit);
812
+ printLog(`writing circuit data...`, LogLevel.DEBUG);
772
813
  // Prepare tx to write circuit data.
773
814
  batch.create(circuitDoc.ref, {
774
815
  ...encodedCircuit,
775
816
  lastUpdated: getCurrentServerTimestampInMillis()
776
817
  });
777
818
  }
819
+ printLog(`Done handling circuits...`, LogLevel.DEBUG);
778
820
  // Send txs in a batch (to avoid race conditions).
779
821
  await batch.commit();
780
822
  printLog(`Setup completed for ceremony ${ceremonyDoc.id}`, LogLevel.DEBUG);
@@ -787,7 +829,7 @@ const setupCeremony = functions__namespace
787
829
  const initEmptyWaitingQueueForCircuit = functions__namespace
788
830
  .region("europe-west1")
789
831
  .runWith({
790
- memory: "512MB"
832
+ memory: "1GB"
791
833
  })
792
834
  .firestore.document(`/${actions.commonTerms.collections.ceremonies.name}/{ceremony}/${actions.commonTerms.collections.circuits.name}/{circuit}`)
793
835
  .onCreate(async (doc) => {
@@ -819,7 +861,7 @@ const initEmptyWaitingQueueForCircuit = functions__namespace
819
861
  const finalizeCeremony = functions__namespace
820
862
  .region("europe-west1")
821
863
  .runWith({
822
- memory: "512MB"
864
+ memory: "1GB"
823
865
  })
824
866
  .https.onCall(async (data, context) => {
825
867
  if (!context.auth || !context.auth.token.coordinator)
@@ -840,7 +882,7 @@ const finalizeCeremony = functions__namespace
840
882
  // Get ceremony circuits.
841
883
  const circuits = await getCeremonyCircuits(ceremonyId);
842
884
  // Get final contribution for each circuit.
843
- // 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).
844
886
  // Therefore, we just need to call the method without taking any data to verify the pre-condition of having already computed
845
887
  // the final contributions for each ceremony circuit.
846
888
  for await (const circuit of circuits)
@@ -895,7 +937,7 @@ dotenv.config();
895
937
  const checkParticipantForCeremony = functions__namespace
896
938
  .region("europe-west1")
897
939
  .runWith({
898
- memory: "512MB"
940
+ memory: "1GB"
899
941
  })
900
942
  .https.onCall(async (data, context) => {
901
943
  if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
@@ -964,7 +1006,7 @@ const checkParticipantForCeremony = functions__namespace
964
1006
  participantDoc.ref.update({
965
1007
  status: "EXHUMED" /* ParticipantStatus.EXHUMED */,
966
1008
  contributions,
967
- tempContributionData: tempContributionData ? tempContributionData : firestore.FieldValue.delete(),
1009
+ tempContributionData: tempContributionData || firestore.FieldValue.delete(),
968
1010
  contributionStep: "DOWNLOADING" /* ParticipantContributionStep.DOWNLOADING */,
969
1011
  contributionStartedAt: 0,
970
1012
  verificationStartedAt: firestore.FieldValue.delete(),
@@ -999,7 +1041,7 @@ const checkParticipantForCeremony = functions__namespace
999
1041
  const progressToNextCircuitForContribution = functions__namespace
1000
1042
  .region("europe-west1")
1001
1043
  .runWith({
1002
- memory: "512MB"
1044
+ memory: "1GB"
1003
1045
  })
1004
1046
  .https.onCall(async (data, context) => {
1005
1047
  if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
@@ -1046,7 +1088,7 @@ const progressToNextCircuitForContribution = functions__namespace
1046
1088
  const progressToNextContributionStep = functions__namespace
1047
1089
  .region("europe-west1")
1048
1090
  .runWith({
1049
- memory: "512MB"
1091
+ memory: "1GB"
1050
1092
  })
1051
1093
  .https.onCall(async (data, context) => {
1052
1094
  if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
@@ -1097,7 +1139,7 @@ const progressToNextContributionStep = functions__namespace
1097
1139
  const permanentlyStoreCurrentContributionTimeAndHash = functions__namespace
1098
1140
  .region("europe-west1")
1099
1141
  .runWith({
1100
- memory: "512MB"
1142
+ memory: "1GB"
1101
1143
  })
1102
1144
  .https.onCall(async (data, context) => {
1103
1145
  if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
@@ -1139,7 +1181,7 @@ const permanentlyStoreCurrentContributionTimeAndHash = functions__namespace
1139
1181
  const temporaryStoreCurrentContributionMultiPartUploadId = functions__namespace
1140
1182
  .region("europe-west1")
1141
1183
  .runWith({
1142
- memory: "512MB"
1184
+ memory: "1GB"
1143
1185
  })
1144
1186
  .https.onCall(async (data, context) => {
1145
1187
  if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
@@ -1177,7 +1219,7 @@ const temporaryStoreCurrentContributionMultiPartUploadId = functions__namespace
1177
1219
  const temporaryStoreCurrentContributionUploadedChunkData = functions__namespace
1178
1220
  .region("europe-west1")
1179
1221
  .runWith({
1180
- memory: "512MB"
1222
+ memory: "1GB"
1181
1223
  })
1182
1224
  .https.onCall(async (data, context) => {
1183
1225
  if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
@@ -1219,7 +1261,7 @@ const temporaryStoreCurrentContributionUploadedChunkData = functions__namespace
1219
1261
  const checkAndPrepareCoordinatorForFinalization = functions__namespace
1220
1262
  .region("europe-west1")
1221
1263
  .runWith({
1222
- memory: "512MB"
1264
+ memory: "1GB"
1223
1265
  })
1224
1266
  .https.onCall(async (data, context) => {
1225
1267
  if (!context.auth || !context.auth.token.coordinator)
@@ -1296,7 +1338,7 @@ const coordinate = async (participant, circuit, isSingleParticipantCoordination,
1296
1338
  if (isSingleParticipantCoordination) {
1297
1339
  // Scenario (A).
1298
1340
  if (emptyWaitingQueue) {
1299
- printLog(`Coordinate - executing scenario A - emptyWaitingQueue`, LogLevel.DEBUG);
1341
+ printLog(`Coordinate - executing scenario A - emptyWaitingQueue`, LogLevel.INFO);
1300
1342
  // Update.
1301
1343
  newCurrentContributorId = participant.id;
1302
1344
  newParticipantStatus = "CONTRIBUTING" /* ParticipantStatus.CONTRIBUTING */;
@@ -1305,14 +1347,14 @@ const coordinate = async (participant, circuit, isSingleParticipantCoordination,
1305
1347
  }
1306
1348
  // Scenario (A).
1307
1349
  else if (participantResumingAfterTimeoutExpiration) {
1308
- printLog(`Coordinate - executing scenario A - single - participantResumingAfterTimeoutExpiration`, LogLevel.DEBUG);
1350
+ printLog(`Coordinate - executing scenario A - single - participantResumingAfterTimeoutExpiration`, LogLevel.INFO);
1309
1351
  newParticipantStatus = "CONTRIBUTING" /* ParticipantStatus.CONTRIBUTING */;
1310
1352
  newContributionStep = "DOWNLOADING" /* ParticipantContributionStep.DOWNLOADING */;
1311
1353
  newCurrentContributorId = participant.id;
1312
1354
  }
1313
1355
  // Scenario (B).
1314
1356
  else if (participantIsNotCurrentContributor) {
1315
- printLog(`Coordinate - executing scenario B - single - participantIsNotCurrentContributor`, LogLevel.DEBUG);
1357
+ printLog(`Coordinate - executing scenario B - single - participantIsNotCurrentContributor`, LogLevel.INFO);
1316
1358
  newCurrentContributorId = currentContributor;
1317
1359
  newParticipantStatus = "WAITING" /* ParticipantStatus.WAITING */;
1318
1360
  newContributors.push(participant.id);
@@ -1331,7 +1373,7 @@ const coordinate = async (participant, circuit, isSingleParticipantCoordination,
1331
1373
  });
1332
1374
  }
1333
1375
  else if (participantIsCurrentContributor && participantCompletedOneOrAllContributions && !!ceremonyId) {
1334
- printLog(`Coordinate - executing scenario C - multi - participantIsCurrentContributor && participantCompletedOneOrAllContributions`, LogLevel.DEBUG);
1376
+ printLog(`Coordinate - executing scenario C - multi - participantIsCurrentContributor && participantCompletedOneOrAllContributions`, LogLevel.INFO);
1335
1377
  newParticipantStatus = "CONTRIBUTING" /* ParticipantStatus.CONTRIBUTING */;
1336
1378
  newContributionStep = "DOWNLOADING" /* ParticipantContributionStep.DOWNLOADING */;
1337
1379
  // Remove from waiting queue of circuit X.
@@ -1349,7 +1391,7 @@ const coordinate = async (participant, circuit, isSingleParticipantCoordination,
1349
1391
  contributionStartedAt: getCurrentServerTimestampInMillis(),
1350
1392
  lastUpdated: getCurrentServerTimestampInMillis()
1351
1393
  });
1352
- 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);
1353
1395
  }
1354
1396
  }
1355
1397
  // Prepare tx - must be done for all Scenarios.
@@ -1426,8 +1468,8 @@ const waitForVMCommandExecution = (ssm, vmInstanceId, commandId) => new Promise(
1426
1468
  try {
1427
1469
  await actions.stopEC2Instance(ec2, vmInstanceId);
1428
1470
  }
1429
- catch (error) {
1430
- printLog(`Error while stopping VM instance ${vmInstanceId} - Error ${error}`, LogLevel.WARN);
1471
+ catch (stopError) {
1472
+ printLog(`Error while stopping VM instance ${vmInstanceId} - Error ${stopError}`, LogLevel.WARN);
1431
1473
  }
1432
1474
  if (!error.toString().includes(commandId))
1433
1475
  logAndThrowError(COMMON_ERRORS.CM_INVALID_COMMAND_EXECUTION);
@@ -1459,7 +1501,7 @@ const waitForVMCommandExecution = (ssm, vmInstanceId, commandId) => new Promise(
1459
1501
  const coordinateCeremonyParticipant = functionsV1__namespace
1460
1502
  .region("europe-west1")
1461
1503
  .runWith({
1462
- memory: "512MB"
1504
+ memory: "1GB"
1463
1505
  })
1464
1506
  .firestore.document(`${actions.commonTerms.collections.ceremonies.name}/{ceremonyId}/${actions.commonTerms.collections.participants.name}/{participantId}`)
1465
1507
  .onUpdate(async (participantChanges) => {
@@ -1475,8 +1517,8 @@ const coordinateCeremonyParticipant = functionsV1__namespace
1475
1517
  // Extract data.
1476
1518
  const { contributionProgress: prevContributionProgress, status: prevStatus, contributionStep: prevContributionStep } = exParticipant.data();
1477
1519
  const { contributionProgress: changedContributionProgress, status: changedStatus, contributionStep: changedContributionStep } = changedParticipant.data();
1478
- printLog(`Coordinate participant ${exParticipant.id} for ceremony ${ceremonyId}`, LogLevel.DEBUG);
1479
- 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);
1480
1522
  // Define pre-conditions.
1481
1523
  const participantReadyToContribute = changedStatus === "READY" /* ParticipantStatus.READY */;
1482
1524
  const participantReadyForFirstContribution = participantReadyToContribute && prevContributionProgress === 0;
@@ -1486,8 +1528,8 @@ const coordinateCeremonyParticipant = functionsV1__namespace
1486
1528
  prevContributionProgress !== 0;
1487
1529
  const participantCompletedEveryCircuitContribution = changedStatus === "DONE" /* ParticipantStatus.DONE */ && prevStatus !== "DONE" /* ParticipantStatus.DONE */;
1488
1530
  const participantCompletedContribution = prevContributionProgress === changedContributionProgress &&
1489
- prevStatus === "CONTRIBUTING" /* ParticipantStatus.CONTRIBUTING */ &&
1490
- prevContributionStep === "VERIFYING" /* ParticipantContributionStep.VERIFYING */ &&
1531
+ (prevStatus === "CONTRIBUTING" /* ParticipantStatus.CONTRIBUTING */ ||
1532
+ prevContributionStep === "VERIFYING" /* ParticipantContributionStep.VERIFYING */) &&
1491
1533
  changedStatus === "CONTRIBUTED" /* ParticipantStatus.CONTRIBUTED */ &&
1492
1534
  changedContributionStep === "COMPLETED" /* ParticipantContributionStep.COMPLETED */;
1493
1535
  // Step (2).
@@ -1495,7 +1537,7 @@ const coordinateCeremonyParticipant = functionsV1__namespace
1495
1537
  participantResumingContributionAfterTimeout ||
1496
1538
  participantReadyForNextContribution) {
1497
1539
  // Step (2.A).
1498
- 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);
1499
1541
  // Get the circuit.
1500
1542
  const circuit = await getCircuitDocumentByPosition(ceremonyId, changedContributionProgress);
1501
1543
  // Coordinate.
@@ -1504,7 +1546,7 @@ const coordinateCeremonyParticipant = functionsV1__namespace
1504
1546
  }
1505
1547
  else if (participantCompletedContribution || participantCompletedEveryCircuitContribution) {
1506
1548
  // Step (2.B).
1507
- 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);
1508
1550
  // Get the circuit.
1509
1551
  const circuit = await getCircuitDocumentByPosition(ceremonyId, prevContributionProgress);
1510
1552
  // Coordinate.
@@ -1528,11 +1570,9 @@ const checkIfVMRunning = async (ec2, vmInstanceId, attempts = 5) => {
1528
1570
  const isVMRunning = await actions.checkIfRunning(ec2, vmInstanceId);
1529
1571
  if (!isVMRunning) {
1530
1572
  printLog(`VM not running, ${attempts - 1} attempts remaining. Retrying in 1 minute...`, LogLevel.DEBUG);
1531
- return await checkIfVMRunning(ec2, vmInstanceId, attempts - 1);
1532
- }
1533
- else {
1534
- return true;
1573
+ return checkIfVMRunning(ec2, vmInstanceId, attempts - 1);
1535
1574
  }
1575
+ return true;
1536
1576
  };
1537
1577
  /**
1538
1578
  * Verify the contribution of a participant computed while contributing to a specific circuit of a ceremony.
@@ -1560,297 +1600,325 @@ const checkIfVMRunning = async (ec2, vmInstanceId, attempts = 5) => {
1560
1600
  * 1.A.4.C.1) If true, update circuit waiting for queue and average timings accordingly to contribution verification results;
1561
1601
  * 2) Send all updates atomically to the Firestore database.
1562
1602
  */
1563
- const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB", timeoutSeconds: 3600, region: "europe-west1" }, async (request) => {
1564
- if (!request.auth || (!request.auth.token.participant && !request.auth.token.coordinator))
1565
- logAndThrowError(SPECIFIC_ERRORS.SE_AUTH_NO_CURRENT_AUTH_USER);
1566
- if (!request.data.ceremonyId ||
1567
- !request.data.circuitId ||
1568
- !request.data.contributorOrCoordinatorIdentifier ||
1569
- !request.data.bucketName)
1570
- logAndThrowError(COMMON_ERRORS.CM_MISSING_OR_WRONG_INPUT_DATA);
1571
- if (!process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_NAME ||
1572
- !process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_VERSION ||
1573
- !process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_COMMIT_HASH)
1574
- logAndThrowError(COMMON_ERRORS.CM_WRONG_CONFIGURATION);
1575
- // Step (0).
1576
- // Prepare and start timer.
1577
- const verifyContributionTimer = new timerNode.Timer({ label: actions.commonTerms.cloudFunctionsNames.verifyContribution });
1578
- verifyContributionTimer.start();
1579
- // Get DB.
1580
- const firestore = admin.firestore();
1581
- // Prepare batch of txs.
1582
- const batch = firestore.batch();
1583
- // Extract data.
1584
- const { ceremonyId, circuitId, contributorOrCoordinatorIdentifier, bucketName } = request.data;
1585
- const userId = request.auth?.uid;
1586
- // Look for the ceremony, circuit and participant document.
1587
- const ceremonyDoc = await getDocumentById(actions.commonTerms.collections.ceremonies.name, ceremonyId);
1588
- const circuitDoc = await getDocumentById(actions.getCircuitsCollectionPath(ceremonyId), circuitId);
1589
- const participantDoc = await getDocumentById(actions.getParticipantsCollectionPath(ceremonyId), userId);
1590
- if (!ceremonyDoc.data() || !circuitDoc.data() || !participantDoc.data())
1591
- logAndThrowError(COMMON_ERRORS.CM_INEXISTENT_DOCUMENT_DATA);
1592
- // Extract documents data.
1593
- const { state } = ceremonyDoc.data();
1594
- const { status, contributions, verificationStartedAt, contributionStartedAt } = participantDoc.data();
1595
- const { waitingQueue, prefix, avgTimings, verification, files } = circuitDoc.data();
1596
- const { completedContributions, failedContributions } = waitingQueue;
1597
- const { contributionComputation: avgContributionComputationTime, fullContribution: avgFullContributionTime, verifyCloudFunction: avgVerifyCloudFunctionTime } = avgTimings;
1598
- const { cfOrVm, vm } = verification;
1599
- // we might not have it if the circuit is not using VM.
1600
- let vmInstanceId = "";
1601
- if (vm)
1602
- vmInstanceId = vm.vmInstanceId;
1603
- // Define pre-conditions.
1604
- const isFinalizing = state === "CLOSED" /* CeremonyState.CLOSED */ && request.auth && request.auth.token.coordinator; // true only when the coordinator verifies the final contributions.
1605
- const isContributing = status === "CONTRIBUTING" /* ParticipantStatus.CONTRIBUTING */;
1606
- const isUsingVM = cfOrVm === "VM" /* CircuitContributionVerificationMechanism.VM */ && !!vmInstanceId;
1607
- // Prepare state.
1608
- let isContributionValid = false;
1609
- let verifyCloudFunctionExecutionTime = 0; // time spent while executing the verify contribution cloud function.
1610
- let verifyCloudFunctionTime = 0; // time spent while executing the core business logic of this cloud function.
1611
- let fullContributionTime = 0; // time spent while doing non-verification contributions tasks (download, compute, upload).
1612
- let contributionComputationTime = 0; // time spent while computing the contribution.
1613
- let lastZkeyBlake2bHash = ""; // the Blake2B hash of the last zKey.
1614
- let verificationTranscriptTemporaryLocalPath = ""; // the local temporary path for the verification transcript.
1615
- let transcriptBlake2bHash = ""; // the Blake2B hash of the verification transcript.
1616
- let commandId = ""; // the unique identifier of the VM command.
1617
- // Derive necessary data.
1618
- const lastZkeyIndex = actions.formatZkeyIndex(completedContributions + 1);
1619
- const verificationTranscriptCompleteFilename = `${prefix}_${isFinalizing
1620
- ? `${contributorOrCoordinatorIdentifier}_${actions.finalContributionIndex}_verification_transcript.log`
1621
- : `${lastZkeyIndex}_${contributorOrCoordinatorIdentifier}_verification_transcript.log`}`;
1622
- const lastZkeyFilename = `${prefix}_${isFinalizing ? actions.finalContributionIndex : lastZkeyIndex}.zkey`;
1623
- // Prepare state for VM verification (if needed).
1624
- const ec2 = await createEC2Client();
1625
- const ssm = await createSSMClient();
1626
- // Step (1.A.1).
1627
- // Get storage paths.
1628
- const verificationTranscriptStoragePathAndFilename = actions.getTranscriptStorageFilePath(prefix, verificationTranscriptCompleteFilename);
1629
- // the zKey storage path is required to be sent to the VM api
1630
- const lastZkeyStoragePath = actions.getZkeyStorageFilePath(prefix, `${prefix}_${isFinalizing ? actions.finalContributionIndex : lastZkeyIndex}.zkey`);
1631
- const verificationTaskTimer = new timerNode.Timer({ label: `${ceremonyId}-${circuitId}-${participantDoc.id}` });
1632
- const completeVerification = async () => {
1633
- // Stop verification task timer.
1634
- printLog("Completing verification", LogLevel.DEBUG);
1635
- verificationTaskTimer.stop();
1636
- verifyCloudFunctionExecutionTime = verificationTaskTimer.ms();
1637
- if (isUsingVM) {
1638
- // Create temporary path.
1639
- verificationTranscriptTemporaryLocalPath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}.log`);
1640
- await sleep(1000); // wait 1s for file creation.
1641
- // Download from bucket.
1642
- // nb. the transcript MUST be uploaded from the VM by verification commands.
1643
- await downloadArtifactFromS3Bucket(bucketName, verificationTranscriptStoragePathAndFilename, verificationTranscriptTemporaryLocalPath);
1644
- // Read the verification trascript and validate data by checking for core info ("ZKey Ok!").
1645
- const content = fs.readFileSync(verificationTranscriptTemporaryLocalPath, "utf-8");
1646
- if (content.includes("ZKey Ok!"))
1647
- isContributionValid = true;
1648
- // If the contribution is valid, then format and store the trascript.
1649
- if (isContributionValid) {
1650
- // eslint-disable-next-line no-control-regex
1651
- const updated = content.replace(/\x1b[[0-9;]*m/g, "");
1652
- 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);
1653
1681
  }
1654
- }
1655
- printLog(`The contribution has been verified - Result ${isContributionValid}`, LogLevel.DEBUG);
1656
- // Create a new contribution document.
1657
- const contributionDoc = await firestore
1658
- .collection(actions.getContributionsCollectionPath(ceremonyId, circuitId))
1659
- .doc()
1660
- .get();
1661
- // Step (1.A.4).
1662
- if (isContributionValid) {
1663
- // Sleep ~3 seconds to wait for verification transcription.
1664
- await sleep(3000);
1665
- // 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();
1666
1691
  if (isUsingVM) {
1667
- // Retrieve the contribution hash from the command output.
1668
- lastZkeyBlake2bHash = await actions.retrieveCommandOutput(ssm, vmInstanceId, commandId);
1669
- const hashRegex = /[a-fA-F0-9]{64}/;
1670
- const match = lastZkeyBlake2bHash.match(hashRegex);
1671
- lastZkeyBlake2bHash = match.at(0);
1672
- // re upload the formatted verification transcript
1673
- 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();
1674
1769
  }
1675
1770
  else {
1676
- // Upload verification transcript.
1677
- /// nb. do not use multi-part upload here due to small file size.
1678
- 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
+ });
1679
1787
  }
1680
- // Compute verification transcript hash.
1681
- transcriptBlake2bHash = await actions.blake512FromPath(verificationTranscriptTemporaryLocalPath);
1682
- // Free resources by unlinking transcript temporary file.
1683
- fs.unlinkSync(verificationTranscriptTemporaryLocalPath);
1684
- // Filter participant contributions to find the data related to the one verified.
1685
- const participantContributions = contributions.filter((contribution) => !!contribution.hash && !!contribution.computationTime && !contribution.doc);
1686
- /// @dev (there must be only one contribution with an empty 'doc' field).
1687
- if (participantContributions.length !== 1)
1688
- logAndThrowError(SPECIFIC_ERRORS.SE_VERIFICATION_NO_PARTICIPANT_CONTRIBUTION_DATA);
1689
- // Get contribution computation time.
1690
- contributionComputationTime = contributions.at(0).computationTime;
1691
- // Step (1.A.4.A.2).
1692
- batch.create(contributionDoc.ref, {
1693
- participantId: participantDoc.id,
1694
- contributionComputationTime,
1695
- verificationComputationTime: verifyCloudFunctionExecutionTime,
1696
- zkeyIndex: isFinalizing ? actions.finalContributionIndex : lastZkeyIndex,
1697
- files: {
1698
- transcriptFilename: verificationTranscriptCompleteFilename,
1699
- lastZkeyFilename,
1700
- transcriptStoragePath: verificationTranscriptStoragePathAndFilename,
1701
- lastZkeyStoragePath,
1702
- transcriptBlake2bHash,
1703
- lastZkeyBlake2bHash
1704
- },
1705
- verificationSoftware: {
1706
- name: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_NAME),
1707
- version: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_VERSION),
1708
- commitHash: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_COMMIT_HASH)
1709
- },
1710
- valid: isContributionValid,
1711
- lastUpdated: getCurrentServerTimestampInMillis()
1712
- });
1713
- verifyContributionTimer.stop();
1714
- verifyCloudFunctionTime = verifyContributionTimer.ms();
1715
- }
1716
- else {
1717
- // Step (1.A.4.B).
1718
- // Free-up storage by deleting invalid contribution.
1719
- await deleteObject(bucketName, lastZkeyStoragePath);
1720
- // Step (1.A.4.B.1).
1721
- batch.create(contributionDoc.ref, {
1722
- participantId: participantDoc.id,
1723
- verificationComputationTime: verifyCloudFunctionExecutionTime,
1724
- zkeyIndex: isFinalizing ? actions.finalContributionIndex : lastZkeyIndex,
1725
- verificationSoftware: {
1726
- name: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_NAME),
1727
- version: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_VERSION),
1728
- commitHash: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_COMMIT_HASH)
1729
- },
1730
- valid: isContributionValid,
1731
- lastUpdated: getCurrentServerTimestampInMillis()
1732
- });
1733
- }
1734
- // Stop VM instance
1735
- if (isUsingVM) {
1736
- // using try and catch as the VM stopping function can throw
1737
- // however we want to continue without stopping as the
1738
- // verification was valid, and inform the coordinator
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
+ });
1877
+ }
1878
+ // CF approach.
1879
+ printLog(`CF mechanism`, LogLevel.DEBUG);
1880
+ const potStoragePath = actions.getPotStorageFilePath(files.potFilename);
1881
+ const firstZkeyStoragePath = actions.getZkeyStorageFilePath(prefix, `${prefix}_${actions.genesisZkeyIndex}.zkey`);
1882
+ printLog(`pot file: ${potStoragePath}`, LogLevel.DEBUG);
1883
+ printLog(`zkey file: ${firstZkeyStoragePath}`, LogLevel.DEBUG);
1884
+ // Prepare temporary file paths.
1885
+ // (nb. these are needed to download the necessary artifacts for verification from AWS S3).
1886
+ verificationTranscriptTemporaryLocalPath = createTemporaryLocalPath(verificationTranscriptCompleteFilename);
1887
+ const potTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}.pot`);
1888
+ const firstZkeyTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}_genesis.zkey`);
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);
1893
+ // Create and populate transcript.
1894
+ const transcriptLogger = actions.createCustomLoggerForFile(verificationTranscriptTemporaryLocalPath);
1895
+ transcriptLogger.info(`${isFinalizing ? `Final verification` : `Verification`} transcript for ${prefix} circuit Phase 2 contribution.\n${isFinalizing ? `Coordinator ` : `Contributor # ${Number(lastZkeyIndex)}`} (${contributorOrCoordinatorIdentifier})\n`);
1896
+ // Step (1.A.2).
1897
+ await downloadArtifactFromS3Bucket(bucketName, potStoragePath, potTempFilePath);
1898
+ await downloadArtifactFromS3Bucket(bucketName, firstZkeyStoragePath, firstZkeyTempFilePath);
1899
+ await downloadArtifactFromS3Bucket(bucketName, lastZkeyStoragePath, lastZkeyTempFilePath);
1900
+ // Step (1.A.4).
1901
+ isContributionValid = await snarkjs.zKey.verifyFromInit(firstZkeyTempFilePath, potTempFilePath, lastZkeyTempFilePath, transcriptLogger);
1902
+ await dumpLog(verificationTranscriptTemporaryLocalPath);
1903
+ // Compute contribution hash.
1904
+ lastZkeyBlake2bHash = await actions.blake512FromPath(lastZkeyTempFilePath);
1905
+ // Free resources by unlinking temporary folders.
1906
+ // Do not free-up verification transcript path here.
1739
1907
  try {
1740
- await actions.stopEC2Instance(ec2, vmInstanceId);
1908
+ fs.unlinkSync(potTempFilePath);
1909
+ fs.unlinkSync(firstZkeyTempFilePath);
1910
+ fs.unlinkSync(lastZkeyTempFilePath);
1741
1911
  }
1742
1912
  catch (error) {
1743
- printLog(`Error while stopping VM instance ${vmInstanceId} - Error ${error}`, LogLevel.WARN);
1913
+ printLog(`Error while unlinking temporary files - Error ${error}`, LogLevel.WARN);
1744
1914
  }
1915
+ await completeVerification();
1745
1916
  }
1746
- // Step (1.A.4.C)
1747
- if (!isFinalizing) {
1748
- // Step (1.A.4.C.1)
1749
- // Compute new average contribution/verification time.
1750
- fullContributionTime = Number(verificationStartedAt) - Number(contributionStartedAt);
1751
- const newAvgContributionComputationTime = avgContributionComputationTime > 0
1752
- ? (avgContributionComputationTime + contributionComputationTime) / 2
1753
- : contributionComputationTime;
1754
- const newAvgFullContributionTime = avgFullContributionTime > 0
1755
- ? (avgFullContributionTime + fullContributionTime) / 2
1756
- : fullContributionTime;
1757
- const newAvgVerifyCloudFunctionTime = avgVerifyCloudFunctionTime > 0
1758
- ? (avgVerifyCloudFunctionTime + verifyCloudFunctionTime) / 2
1759
- : verifyCloudFunctionTime;
1760
- // Prepare tx to update circuit average contribution/verification time.
1761
- const updatedCircuitDoc = await getDocumentById(actions.getCircuitsCollectionPath(ceremonyId), circuitId);
1762
- const { waitingQueue: updatedWaitingQueue } = updatedCircuitDoc.data();
1763
- /// @dev this must happen only for valid contributions.
1764
- batch.update(circuitDoc.ref, {
1765
- avgTimings: {
1766
- contributionComputation: isContributionValid
1767
- ? newAvgContributionComputationTime
1768
- : avgContributionComputationTime,
1769
- fullContribution: isContributionValid ? newAvgFullContributionTime : avgFullContributionTime,
1770
- verifyCloudFunction: isContributionValid
1771
- ? newAvgVerifyCloudFunctionTime
1772
- : avgVerifyCloudFunctionTime
1773
- },
1774
- waitingQueue: {
1775
- ...updatedWaitingQueue,
1776
- completedContributions: isContributionValid
1777
- ? completedContributions + 1
1778
- : completedContributions,
1779
- failedContributions: isContributionValid ? failedContributions : failedContributions + 1
1780
- },
1781
- lastUpdated: getCurrentServerTimestampInMillis()
1782
- });
1783
- }
1784
- // Step (2).
1785
- await batch.commit();
1786
- 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);
1787
- };
1788
- // Step (1).
1789
- if (isContributing || isFinalizing) {
1790
- // Prepare timer.
1791
- verificationTaskTimer.start();
1792
- // Step (1.A.3.0).
1793
- if (isUsingVM) {
1794
- printLog(`Starting the VM mechanism`, LogLevel.DEBUG);
1795
- // Prepare for VM execution.
1796
- let isVMRunning = false; // true when the VM is up, otherwise false.
1797
- // Step (1.A.3.1).
1798
- await actions.startEC2Instance(ec2, vmInstanceId);
1799
- await sleep(60000); // nb. wait for VM startup (1 mins + retry).
1800
- // Check if the startup is running.
1801
- isVMRunning = await checkIfVMRunning(ec2, vmInstanceId);
1802
- printLog(`VM running: ${isVMRunning}`, LogLevel.DEBUG);
1803
- // Step (1.A.3.2).
1804
- // Prepare.
1805
- const verificationCommand = actions.vmContributionVerificationCommand(bucketName, lastZkeyStoragePath, verificationTranscriptStoragePathAndFilename);
1806
- // Run.
1807
- commandId = await actions.runCommandUsingSSM(ssm, vmInstanceId, verificationCommand);
1808
- printLog(`Starting the execution of command ${commandId}`, LogLevel.DEBUG);
1809
- // Step (1.A.3.3).
1810
- return waitForVMCommandExecution(ssm, vmInstanceId, commandId)
1811
- .then(async () => {
1812
- // Command execution successfully completed.
1813
- printLog(`Command ${commandId} execution has been successfully completed`, LogLevel.DEBUG);
1814
- await completeVerification();
1815
- })
1816
- .catch((error) => {
1817
- // Command execution aborted.
1818
- printLog(`Command ${commandId} execution has been aborted - Error ${error}`, LogLevel.DEBUG);
1819
- logAndThrowError(COMMON_ERRORS.CM_INVALID_COMMAND_EXECUTION);
1820
- });
1821
- }
1822
- // CF approach.
1823
- printLog(`CF mechanism`, LogLevel.DEBUG);
1824
- const potStoragePath = actions.getPotStorageFilePath(files.potFilename);
1825
- const firstZkeyStoragePath = actions.getZkeyStorageFilePath(prefix, `${prefix}_${actions.genesisZkeyIndex}.zkey`);
1826
- // Prepare temporary file paths.
1827
- // (nb. these are needed to download the necessary artifacts for verification from AWS S3).
1828
- verificationTranscriptTemporaryLocalPath = createTemporaryLocalPath(verificationTranscriptCompleteFilename);
1829
- const potTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}.pot`);
1830
- const firstZkeyTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}_genesis.zkey`);
1831
- const lastZkeyTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}_last.zkey`);
1832
- // Create and populate transcript.
1833
- const transcriptLogger = actions.createCustomLoggerForFile(verificationTranscriptTemporaryLocalPath);
1834
- transcriptLogger.info(`${isFinalizing ? `Final verification` : `Verification`} transcript for ${prefix} circuit Phase 2 contribution.\n${isFinalizing ? `Coordinator ` : `Contributor # ${Number(lastZkeyIndex)}`} (${contributorOrCoordinatorIdentifier})\n`);
1835
- // Step (1.A.2).
1836
- await downloadArtifactFromS3Bucket(bucketName, potStoragePath, potTempFilePath);
1837
- await downloadArtifactFromS3Bucket(bucketName, firstZkeyStoragePath, firstZkeyTempFilePath);
1838
- await downloadArtifactFromS3Bucket(bucketName, lastZkeyStoragePath, lastZkeyTempFilePath);
1839
- // Step (1.A.4).
1840
- isContributionValid = await snarkjs.zKey.verifyFromInit(firstZkeyTempFilePath, potTempFilePath, lastZkeyTempFilePath, transcriptLogger);
1841
- // Compute contribution hash.
1842
- lastZkeyBlake2bHash = await actions.blake512FromPath(lastZkeyTempFilePath);
1843
- // Free resources by unlinking temporary folders.
1844
- // Do not free-up verification transcript path here.
1845
- try {
1846
- fs.unlinkSync(potTempFilePath);
1847
- fs.unlinkSync(firstZkeyTempFilePath);
1848
- fs.unlinkSync(lastZkeyTempFilePath);
1849
- }
1850
- catch (error) {
1851
- printLog(`Error while unlinking temporary files - Error ${error}`, LogLevel.WARN);
1852
- }
1853
- await completeVerification();
1917
+ return null;
1918
+ }
1919
+ catch (error) {
1920
+ logAndThrowError(makeError("unknown", error));
1921
+ return null;
1854
1922
  }
1855
1923
  });
1856
1924
  /**
@@ -1861,7 +1929,7 @@ const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB"
1861
1929
  const refreshParticipantAfterContributionVerification = functionsV1__namespace
1862
1930
  .region("europe-west1")
1863
1931
  .runWith({
1864
- memory: "512MB"
1932
+ memory: "1GB"
1865
1933
  })
1866
1934
  .firestore.document(`/${actions.commonTerms.collections.ceremonies.name}/{ceremony}/${actions.commonTerms.collections.circuits.name}/{circuit}/${actions.commonTerms.collections.contributions.name}/{contributions}`)
1867
1935
  .onCreate(async (createdContribution) => {
@@ -1912,7 +1980,7 @@ const refreshParticipantAfterContributionVerification = functionsV1__namespace
1912
1980
  lastUpdated: getCurrentServerTimestampInMillis()
1913
1981
  });
1914
1982
  await batch.commit();
1915
- 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);
1916
1984
  });
1917
1985
  /**
1918
1986
  * Finalize the ceremony circuit.
@@ -1922,7 +1990,7 @@ const refreshParticipantAfterContributionVerification = functionsV1__namespace
1922
1990
  const finalizeCircuit = functionsV1__namespace
1923
1991
  .region("europe-west1")
1924
1992
  .runWith({
1925
- memory: "512MB"
1993
+ memory: "1GB"
1926
1994
  })
1927
1995
  .https.onCall(async (data, context) => {
1928
1996
  if (!context.auth || !context.auth.token.coordinator)
@@ -2066,7 +2134,7 @@ const checkIfBucketIsDedicatedToCeremony = async (bucketName) => {
2066
2134
  const createBucket = functions__namespace
2067
2135
  .region("europe-west1")
2068
2136
  .runWith({
2069
- memory: "512MB"
2137
+ memory: "1GB"
2070
2138
  })
2071
2139
  .https.onCall(async (data, context) => {
2072
2140
  // Check if the user has the coordinator claim.
@@ -2077,6 +2145,7 @@ const createBucket = functions__namespace
2077
2145
  // Connect to S3 client.
2078
2146
  const S3 = await getS3Client();
2079
2147
  try {
2148
+ printLog(`Creating AWS S3 bucket ${data.bucketName} ...`, LogLevel.LOG);
2080
2149
  // Try to get information about the bucket.
2081
2150
  await S3.send(new clientS3.HeadBucketCommand({ Bucket: data.bucketName }));
2082
2151
  // If the command succeeded, the bucket exists, throw an error.
@@ -2156,7 +2225,7 @@ const createBucket = functions__namespace
2156
2225
  const checkIfObjectExist = functions__namespace
2157
2226
  .region("europe-west1")
2158
2227
  .runWith({
2159
- memory: "512MB"
2228
+ memory: "1GB"
2160
2229
  })
2161
2230
  .https.onCall(async (data, context) => {
2162
2231
  // Check if the user has the coordinator claim.
@@ -2202,7 +2271,7 @@ const checkIfObjectExist = functions__namespace
2202
2271
  const generateGetObjectPreSignedUrl = functions__namespace
2203
2272
  .region("europe-west1")
2204
2273
  .runWith({
2205
- memory: "512MB"
2274
+ memory: "1GB"
2206
2275
  })
2207
2276
  .https.onCall(async (data, context) => {
2208
2277
  if (!context.auth)
@@ -2242,7 +2311,7 @@ const generateGetObjectPreSignedUrl = functions__namespace
2242
2311
  const startMultiPartUpload = functions__namespace
2243
2312
  .region("europe-west1")
2244
2313
  .runWith({
2245
- memory: "512MB"
2314
+ memory: "2GB"
2246
2315
  })
2247
2316
  .https.onCall(async (data, context) => {
2248
2317
  if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
@@ -2297,7 +2366,7 @@ const startMultiPartUpload = functions__namespace
2297
2366
  const generatePreSignedUrlsParts = functions__namespace
2298
2367
  .region("europe-west1")
2299
2368
  .runWith({
2300
- memory: "512MB",
2369
+ memory: "1GB",
2301
2370
  timeoutSeconds: 300
2302
2371
  })
2303
2372
  .https.onCall(async (data, context) => {
@@ -2358,7 +2427,7 @@ const generatePreSignedUrlsParts = functions__namespace
2358
2427
  const completeMultiPartUpload = functions__namespace
2359
2428
  .region("europe-west1")
2360
2429
  .runWith({
2361
- memory: "512MB"
2430
+ memory: "2GB"
2362
2431
  })
2363
2432
  .https.onCall(async (data, context) => {
2364
2433
  if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
@@ -2407,6 +2476,216 @@ const completeMultiPartUpload = functions__namespace
2407
2476
  }
2408
2477
  });
2409
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
+
2410
2689
  dotenv.config();
2411
2690
  /**
2412
2691
  * Check and remove the current contributor if it doesn't complete the contribution on the specified amount of time.
@@ -2429,7 +2708,7 @@ dotenv.config();
2429
2708
  const checkAndRemoveBlockingContributor = functions__namespace
2430
2709
  .region("europe-west1")
2431
2710
  .runWith({
2432
- memory: "512MB"
2711
+ memory: "1GB"
2433
2712
  })
2434
2713
  .pubsub.schedule("every 1 minutes")
2435
2714
  .onRun(async () => {
@@ -2448,7 +2727,7 @@ const checkAndRemoveBlockingContributor = functions__namespace
2448
2727
  // Get ceremony circuits.
2449
2728
  const circuits = await getCeremonyCircuits(ceremony.id);
2450
2729
  // Extract ceremony data.
2451
- const { timeoutMechanismType, penalty } = ceremony.data();
2730
+ const { timeoutType: timeoutMechanismType, penalty } = ceremony.data();
2452
2731
  for (const circuit of circuits) {
2453
2732
  if (!circuit.data())
2454
2733
  // Do not use `logAndThrowError` method to avoid the function to exit before checking every ceremony.
@@ -2461,7 +2740,7 @@ const checkAndRemoveBlockingContributor = functions__namespace
2461
2740
  // Case (A).
2462
2741
  if (!currentContributor)
2463
2742
  // Do not use `logAndThrowError` method to avoid the function to exit before checking every ceremony.
2464
- 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);
2465
2744
  else if (avgFullContribution === 0 &&
2466
2745
  avgContributionComputation === 0 &&
2467
2746
  avgVerifyCloudFunction === 0 &&
@@ -2487,7 +2766,7 @@ const checkAndRemoveBlockingContributor = functions__namespace
2487
2766
  ? Number(contributionStartedAt) +
2488
2767
  Number(avgFullContribution) +
2489
2768
  Number(timeoutDynamicThreshold)
2490
- : Number(contributionStartedAt) + Number(fixedTimeWindow) * 60000; // * 60000 = convert minutes to millis.
2769
+ : (Number(contributionStartedAt) + Number(fixedTimeWindow)) * 60000; // * 60000 = convert minutes to millis.
2491
2770
  // Case (D).
2492
2771
  const timeoutExpirationDateInMsForVerificationCloudFunction = contributionStep === "VERIFYING" /* ParticipantContributionStep.VERIFYING */ &&
2493
2772
  !!verificationStartedAt
@@ -2498,17 +2777,18 @@ const checkAndRemoveBlockingContributor = functions__namespace
2498
2777
  if (timeoutExpirationDateInMsForBlockingContributor < currentServerTimestamp &&
2499
2778
  (contributionStep === "DOWNLOADING" /* ParticipantContributionStep.DOWNLOADING */ ||
2500
2779
  contributionStep === "COMPUTING" /* ParticipantContributionStep.COMPUTING */ ||
2501
- contributionStep === "UPLOADING" /* ParticipantContributionStep.UPLOADING */))
2780
+ contributionStep === "UPLOADING" /* ParticipantContributionStep.UPLOADING */ ||
2781
+ contributionStep === "COMPLETED" /* ParticipantContributionStep.COMPLETED */))
2502
2782
  timeoutType = "BLOCKING_CONTRIBUTION" /* TimeoutType.BLOCKING_CONTRIBUTION */;
2503
2783
  if (timeoutExpirationDateInMsForVerificationCloudFunction > 0 &&
2504
2784
  timeoutExpirationDateInMsForVerificationCloudFunction < currentServerTimestamp &&
2505
2785
  contributionStep === "VERIFYING" /* ParticipantContributionStep.VERIFYING */)
2506
2786
  timeoutType = "BLOCKING_CLOUD_FUNCTION" /* TimeoutType.BLOCKING_CLOUD_FUNCTION */;
2507
- printLog(`${timeoutType} detected for circuit ${circuit.id} - ceremony ${ceremony.id}`, LogLevel.DEBUG);
2508
2787
  if (!timeoutType)
2509
2788
  // Do not use `logAndThrowError` method to avoid the function to exit before checking every ceremony.
2510
- 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);
2511
2790
  else {
2791
+ printLog(`${timeoutType} detected for circuit ${circuit.id} - ceremony ${ceremony.id}`, LogLevel.WARN);
2512
2792
  // Case (E).
2513
2793
  let nextCurrentContributorId = "";
2514
2794
  // Prepare Firestore batch of txs.
@@ -2559,7 +2839,7 @@ const checkAndRemoveBlockingContributor = functions__namespace
2559
2839
  });
2560
2840
  // Send atomic update for Firestore.
2561
2841
  await batch.commit();
2562
- 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);
2563
2843
  }
2564
2844
  }
2565
2845
  }
@@ -2575,7 +2855,7 @@ const checkAndRemoveBlockingContributor = functions__namespace
2575
2855
  const resumeContributionAfterTimeoutExpiration = functions__namespace
2576
2856
  .region("europe-west1")
2577
2857
  .runWith({
2578
- memory: "512MB"
2858
+ memory: "1GB"
2579
2859
  })
2580
2860
  .https.onCall(async (data, context) => {
2581
2861
  if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
@@ -2608,9 +2888,11 @@ const resumeContributionAfterTimeoutExpiration = functions__namespace
2608
2888
 
2609
2889
  admin.initializeApp();
2610
2890
 
2891
+ exports.bandadaValidateProof = bandadaValidateProof;
2611
2892
  exports.checkAndPrepareCoordinatorForFinalization = checkAndPrepareCoordinatorForFinalization;
2612
2893
  exports.checkAndRemoveBlockingContributor = checkAndRemoveBlockingContributor;
2613
2894
  exports.checkIfObjectExist = checkIfObjectExist;
2895
+ exports.checkNonceOfSIWEAddress = checkNonceOfSIWEAddress;
2614
2896
  exports.checkParticipantForCeremony = checkParticipantForCeremony;
2615
2897
  exports.completeMultiPartUpload = completeMultiPartUpload;
2616
2898
  exports.coordinateCeremonyParticipant = coordinateCeremonyParticipant;