@devtion/backend 0.0.0-7e983e3 → 0.0.0-8b5a17f

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -100,6 +100,32 @@ yarn firebase:init
100
100
 
101
101
  ### Deployment
102
102
 
103
+ #### AWS Infrastructure
104
+
105
+ 0. Login or create a [new AWS Account](https://portal.aws.amazon.com/billing/signup?nc2=h_ct&src=header_signup&redirect_url=https%3A%2F%2Faws.amazon.com%2Fregistration-confirmation#/start/email).
106
+ - The AWS free tier account will cover a good number of requests for ceremonies but there could be some costs based on your ceremony circuits size.
107
+ 1. Create an access key for a user with Admin privileges (__NOT ROOT USER__)
108
+ 2. Setup the `awscli` ([docs](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html)) and add the keys for this user.
109
+ 3. Install `terraform` ([docs](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli))
110
+ 4. Decide on an AWS region (by default this is **us-east-1**) - if you want to change you will need to do the following:
111
+ 1. update **aws/lambda/index.mjs** ([exact line](https://github.com/privacy-scaling-explorations/p0tion/blob/dev/packages/backend/aws/lambda/index.mjs#L3)) to the new region
112
+ 2. update **main.tf** ([exact line](https://github.com/privacy-scaling-explorations/p0tion/blob/dev/packages/backend/aws/main.tf#L2)) to the new region
113
+ 5. zip the Lambda folder:
114
+ 1. `cd lambda`
115
+ 2. `zip -r ../lambda.zip .`
116
+ 6. Run terraform:
117
+ 1. `terraform init`
118
+ 2. `terraform plan`
119
+ 3. `terraform apply`
120
+ 4. `terraform output secret_key`
121
+ - To print the secret access key for the IAM user
122
+ 6. Store the other values (sns_topic_arn etc.)
123
+ - These will be needed for the .env file configuration
124
+
125
+ The IAM user created with the steps above can be used for all p0tion's features.
126
+
127
+ #### Firebase
128
+
103
129
  Deploy the current configuration to the `prod` project running
104
130
 
105
131
  ```bash
@@ -1,6 +1,6 @@
1
1
  /**
2
- * @module @p0tion/backend
3
- * @version 1.0.5
2
+ * @module @devtion/backend
3
+ * @version 1.0.6
4
4
  * @file MPC Phase 2 backend for Firebase services management
5
5
  * @copyright Ethereum Foundation 2022
6
6
  * @license MIT
@@ -11,7 +11,7 @@
11
11
  var admin = require('firebase-admin');
12
12
  var functions = require('firebase-functions');
13
13
  var dotenv = require('dotenv');
14
- var actions = require('@p0tion/actions');
14
+ var actions = require('@devtion/actions');
15
15
  var htmlEntities = require('html-entities');
16
16
  var firestore = require('firebase-admin/firestore');
17
17
  var clientS3 = require('@aws-sdk/client-s3');
@@ -544,8 +544,10 @@ const registerAuthUser = functions__namespace
544
544
  const { uid } = user;
545
545
  // Reference to a document using uid.
546
546
  const userRef = firestore.collection(actions.commonTerms.collections.users.name).doc(uid);
547
- // html encode the display name
548
- const encodedDisplayName = htmlEntities.encode(displayName);
547
+ // html encode the display name (or put the ID if the name is not displayed)
548
+ const encodedDisplayName = user.displayName === "Null" || user.displayName === null ? user.uid : htmlEntities.encode(displayName);
549
+ // store the avatar URL of a contributor
550
+ let avatarUrl = "";
549
551
  // we only do reputation check if the user is not a coordinator
550
552
  if (!(email?.endsWith(`@${process.env.CUSTOM_CLAIMS_COORDINATOR_EMAIL_ADDRESS_OR_DOMAIN}`) ||
551
553
  email === process.env.CUSTOM_CLAIMS_COORDINATOR_EMAIL_ADDRESS_OR_DOMAIN)) {
@@ -555,14 +557,16 @@ const registerAuthUser = functions__namespace
555
557
  const vars = getGitHubVariables();
556
558
  // this return true or false
557
559
  try {
558
- const res = await actions.githubReputation(user.providerData[0].uid, vars.minimumFollowing, vars.minimumFollowers, vars.minimumPublicRepos);
559
- if (!res) {
560
+ const { reputable, avatarUrl: avatarURL } = await actions.githubReputation(user.providerData[0].uid, vars.minimumFollowing, vars.minimumFollowers, vars.minimumPublicRepos);
561
+ if (!reputable) {
560
562
  // Delete user
561
563
  await auth.deleteUser(user.uid);
562
564
  // Throw error
563
- logAndThrowError(makeError("permission-denied", "The user is not allowed to sign up because their Github reputation is not high enough.", `The user ${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.`));
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.`));
564
566
  }
565
- printLog(`Github reputation check passed for user ${user.displayName}`, LogLevel.DEBUG);
567
+ // store locally
568
+ avatarUrl = avatarURL;
569
+ printLog(`Github reputation check passed for user ${user.displayName === "Null" || user.displayName === null ? user.uid : user.displayName}`, LogLevel.DEBUG);
566
570
  }
567
571
  catch (error) {
568
572
  // Delete user
@@ -572,6 +576,8 @@ const registerAuthUser = functions__namespace
572
576
  }
573
577
  }
574
578
  // Set document (nb. we refer to providerData[0] because we use Github OAuth provider only).
579
+ // In future releases we might want to loop through the providerData array as we support
580
+ // more providers.
575
581
  await userRef.set({
576
582
  name: encodedDisplayName,
577
583
  encodedDisplayName,
@@ -584,7 +590,13 @@ const registerAuthUser = functions__namespace
584
590
  photoURL: photoURL || "",
585
591
  lastUpdated: getCurrentServerTimestampInMillis()
586
592
  });
593
+ // we want to create a new collection for the users to store the avatars
594
+ const avatarRef = firestore.collection(actions.commonTerms.collections.avatars.name).doc(uid);
595
+ await avatarRef.set({
596
+ avatarUrl: avatarUrl || "",
597
+ });
587
598
  printLog(`Authenticated user document with identifier ${uid} has been correctly stored`, LogLevel.DEBUG);
599
+ printLog(`Authenticated user avatar with identifier ${uid} has been correctly stored`, LogLevel.DEBUG);
588
600
  });
589
601
  /**
590
602
  * Set custom claims for role-based access control on the newly created user.
@@ -721,7 +733,7 @@ const setupCeremony = functions__namespace
721
733
  // Check if using the VM approach for contribution verification.
722
734
  if (circuit.verification.cfOrVm === "VM" /* CircuitContributionVerificationMechanism.VM */) {
723
735
  // VM command to be run at the startup.
724
- const startupCommand = actions.vmBootstrapCommand(bucketName);
736
+ const startupCommand = actions.vmBootstrapCommand(`${bucketName}/circuits/${circuit.name}`);
725
737
  // Get EC2 client.
726
738
  const ec2Client = await createEC2Client();
727
739
  // Get AWS variables.
@@ -730,7 +742,8 @@ const setupCeremony = functions__namespace
730
742
  const vmCommands = actions.vmDependenciesAndCacheArtifactsCommand(`${bucketName}/${circuit.files?.initialZkeyStoragePath}`, `${bucketName}/${circuit.files?.potStoragePath}`, snsTopic, region);
731
743
  printLog(`Check VM dependencies and cache artifacts commands ${vmCommands.join("\n")}`, LogLevel.DEBUG);
732
744
  // Upload the post-startup commands script file.
733
- await uploadFileToBucketNoFile(bucketName, actions.vmBootstrapScriptFilename, vmCommands.join("\n"));
745
+ printLog(`Uploading VM post-startup commands script file ${actions.vmBootstrapScriptFilename}`, LogLevel.DEBUG);
746
+ await uploadFileToBucketNoFile(bucketName, `circuits/${circuit.name}/${actions.vmBootstrapScriptFilename}`, vmCommands.join("\n"));
734
747
  // Compute the VM disk space requirement (in GB).
735
748
  const vmDiskSize = actions.computeDiskSizeForVM(circuit.zKeySizeInBytes, circuit.metadata?.pot);
736
749
  printLog(`Check VM startup commands ${startupCommand.join("\n")}`, LogLevel.DEBUG);
@@ -1292,6 +1305,7 @@ const coordinate = async (participant, circuit, isSingleParticipantCoordination,
1292
1305
  printLog(`Coordinate - executing scenario A - single - participantResumingAfterTimeoutExpiration`, LogLevel.DEBUG);
1293
1306
  newParticipantStatus = "CONTRIBUTING" /* ParticipantStatus.CONTRIBUTING */;
1294
1307
  newContributionStep = "DOWNLOADING" /* ParticipantContributionStep.DOWNLOADING */;
1308
+ newCurrentContributorId = participant.id;
1295
1309
  }
1296
1310
  // Scenario (B).
1297
1311
  else if (participantIsNotCurrentContributor) {
@@ -1400,53 +1414,6 @@ const waitForVMCommandExecution = (resolve, reject, ssm, vmInstanceId, commandId
1400
1414
  }
1401
1415
  }, 60000); // 1 minute.
1402
1416
  };
1403
- /**
1404
- * Wait until the artifacts have been downloaded.
1405
- * @param {any} resolve the promise.
1406
- * @param {any} reject the promise.
1407
- * @param {string} potTempFilePath the tmp path to the locally downloaded pot file.
1408
- * @param {string} firstZkeyTempFilePath the tmp path to the locally downloaded first zkey file.
1409
- * @param {string} lastZkeyTempFilePath the tmp path to the locally downloaded last zkey file.
1410
- */
1411
- const waitForFileDownload = (resolve, reject, potTempFilePath, firstZkeyTempFilePath, lastZkeyTempFilePath, circuitId, participantId) => {
1412
- const maxWaitTime = 5 * 60 * 1000; // 5 minutes
1413
- // every second check if the file download was completed
1414
- const interval = setInterval(async () => {
1415
- printLog(`Verifying that the artifacts were downloaded for circuit ${circuitId} and participant ${participantId}`, LogLevel.DEBUG);
1416
- try {
1417
- // check if files have been downloaded
1418
- if (!fs.existsSync(potTempFilePath)) {
1419
- printLog(`Pot file not found at ${potTempFilePath}`, LogLevel.DEBUG);
1420
- }
1421
- if (!fs.existsSync(firstZkeyTempFilePath)) {
1422
- printLog(`First zkey file not found at ${firstZkeyTempFilePath}`, LogLevel.DEBUG);
1423
- }
1424
- if (!fs.existsSync(lastZkeyTempFilePath)) {
1425
- printLog(`Last zkey file not found at ${lastZkeyTempFilePath}`, LogLevel.DEBUG);
1426
- }
1427
- // if all files were downloaded
1428
- if (fs.existsSync(potTempFilePath) && fs.existsSync(firstZkeyTempFilePath) && fs.existsSync(lastZkeyTempFilePath)) {
1429
- printLog(`All required files are present on disk.`, LogLevel.INFO);
1430
- // resolve the promise
1431
- resolve();
1432
- }
1433
- }
1434
- catch (error) {
1435
- // if we have an error then we print it as a warning and reject
1436
- printLog(`Error while downloading files: ${error}`, LogLevel.WARN);
1437
- reject();
1438
- }
1439
- finally {
1440
- printLog(`Clearing the interval for file download. Circuit ${circuitId} and participant ${participantId}`, LogLevel.DEBUG);
1441
- clearInterval(interval);
1442
- }
1443
- }, 5000);
1444
- // we want to clean in 5 minutes in case
1445
- setTimeout(() => {
1446
- clearInterval(interval);
1447
- reject(new Error('Timeout exceeded while waiting for files to be downloaded.'));
1448
- }, maxWaitTime);
1449
- };
1450
1417
  /**
1451
1418
  * This method is used to coordinate the waiting queues of ceremony circuits.
1452
1419
  * @dev this cloud function is triggered whenever an update of a document related to a participant of a ceremony occurs.
@@ -1681,8 +1648,6 @@ const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB"
1681
1648
  lastZkeyBlake2bHash = match.at(0);
1682
1649
  // re upload the formatted verification transcript
1683
1650
  await uploadFileToBucket(bucketName, verificationTranscriptStoragePathAndFilename, verificationTranscriptTemporaryLocalPath, true);
1684
- // Stop VM instance.
1685
- await actions.stopEC2Instance(ec2, vmInstanceId);
1686
1651
  }
1687
1652
  else {
1688
1653
  // Upload verification transcript.
@@ -1743,6 +1708,9 @@ const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB"
1743
1708
  lastUpdated: getCurrentServerTimestampInMillis()
1744
1709
  });
1745
1710
  }
1711
+ // Stop VM instance
1712
+ if (isUsingVM)
1713
+ await actions.stopEC2Instance(ec2, vmInstanceId);
1746
1714
  // Step (1.A.4.C)
1747
1715
  if (!isFinalizing) {
1748
1716
  // Step (1.A.4.C.1)
@@ -1757,7 +1725,9 @@ const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB"
1757
1725
  const newAvgVerifyCloudFunctionTime = avgVerifyCloudFunctionTime > 0
1758
1726
  ? (avgVerifyCloudFunctionTime + verifyCloudFunctionTime) / 2
1759
1727
  : verifyCloudFunctionTime;
1760
- // Prepare tx to update circuit average contribution/verification time.
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();
1761
1731
  /// @dev this must happen only for valid contributions.
1762
1732
  batch.update(circuitDoc.ref, {
1763
1733
  avgTimings: {
@@ -1770,7 +1740,7 @@ const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB"
1770
1740
  : avgVerifyCloudFunctionTime
1771
1741
  },
1772
1742
  waitingQueue: {
1773
- ...waitingQueue,
1743
+ ...updatedWaitingQueue,
1774
1744
  completedContributions: isContributionValid
1775
1745
  ? completedContributions + 1
1776
1746
  : completedContributions,
@@ -1835,33 +1805,21 @@ const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB"
1835
1805
  await downloadArtifactFromS3Bucket(bucketName, potStoragePath, potTempFilePath);
1836
1806
  await downloadArtifactFromS3Bucket(bucketName, firstZkeyStoragePath, firstZkeyTempFilePath);
1837
1807
  await downloadArtifactFromS3Bucket(bucketName, lastZkeyStoragePath, lastZkeyTempFilePath);
1838
- await sleep(6000);
1839
- // wait until the files are actually downloaded
1840
- return new Promise((resolve, reject) => waitForFileDownload(resolve, reject, potTempFilePath, firstZkeyTempFilePath, lastZkeyTempFilePath, circuitId, participantDoc.id))
1841
- .then(async () => {
1842
- printLog(`Downloads from AWS S3 bucket completed - ceremony ${ceremonyId} circuit ${circuitId}`, LogLevel.DEBUG);
1843
- // Step (1.A.4).
1844
- isContributionValid = await snarkjs.zKey.verifyFromInit(firstZkeyTempFilePath, potTempFilePath, lastZkeyTempFilePath, transcriptLogger);
1845
- // Compute contribution hash.
1846
- lastZkeyBlake2bHash = await actions.blake512FromPath(lastZkeyTempFilePath);
1847
- // Free resources by unlinking temporary folders.
1848
- // Do not free-up verification transcript path here.
1849
- try {
1850
- fs.unlinkSync(potTempFilePath);
1851
- fs.unlinkSync(firstZkeyTempFilePath);
1852
- fs.unlinkSync(lastZkeyTempFilePath);
1853
- }
1854
- catch (error) {
1855
- printLog(`Error while unlinking temporary files - Error ${error}`, LogLevel.WARN);
1856
- }
1857
- await completeVerification();
1858
- })
1859
- .catch((error) => {
1860
- // Throw the new error
1861
- const commonError = COMMON_ERRORS.CM_INVALID_REQUEST;
1862
- const additionalDetails = error.toString();
1863
- logAndThrowError(makeError(commonError.code, commonError.message, additionalDetails));
1864
- });
1808
+ // Step (1.A.4).
1809
+ isContributionValid = await snarkjs.zKey.verifyFromInit(firstZkeyTempFilePath, potTempFilePath, lastZkeyTempFilePath, transcriptLogger);
1810
+ // Compute contribution hash.
1811
+ lastZkeyBlake2bHash = await actions.blake512FromPath(lastZkeyTempFilePath);
1812
+ // Free resources by unlinking temporary folders.
1813
+ // Do not free-up verification transcript path here.
1814
+ try {
1815
+ fs.unlinkSync(potTempFilePath);
1816
+ fs.unlinkSync(firstZkeyTempFilePath);
1817
+ fs.unlinkSync(lastZkeyTempFilePath);
1818
+ }
1819
+ catch (error) {
1820
+ printLog(`Error while unlinking temporary files - Error ${error}`, LogLevel.WARN);
1821
+ }
1822
+ await completeVerification();
1865
1823
  }
1866
1824
  }
1867
1825
  });
@@ -2523,7 +2481,7 @@ const checkAndRemoveBlockingContributor = functions__namespace
2523
2481
  // Prepare Firestore batch of txs.
2524
2482
  const batch = firestore.batch();
2525
2483
  // Remove current contributor from waiting queue.
2526
- contributors.shift(1);
2484
+ contributors.shift();
2527
2485
  // Check if someone else is ready to start the contribution.
2528
2486
  if (contributors.length > 0) {
2529
2487
  // Step (E.1).
@@ -2607,7 +2565,8 @@ const resumeContributionAfterTimeoutExpiration = functions__namespace
2607
2565
  if (status === "EXHUMED" /* ParticipantStatus.EXHUMED */)
2608
2566
  await participantDoc.ref.update({
2609
2567
  status: "READY" /* ParticipantStatus.READY */,
2610
- lastUpdated: getCurrentServerTimestampInMillis()
2568
+ lastUpdated: getCurrentServerTimestampInMillis(),
2569
+ tempContributionData: {}
2611
2570
  });
2612
2571
  else
2613
2572
  logAndThrowError(SPECIFIC_ERRORS.SE_CONTRIBUTE_CANNOT_PROGRESS_TO_NEXT_CIRCUIT);
@@ -1,6 +1,6 @@
1
1
  /**
2
- * @module @p0tion/backend
3
- * @version 1.0.5
2
+ * @module @devtion/backend
3
+ * @version 1.0.6
4
4
  * @file MPC Phase 2 backend for Firebase services management
5
5
  * @copyright Ethereum Foundation 2022
6
6
  * @license MIT
@@ -9,7 +9,7 @@
9
9
  import admin from 'firebase-admin';
10
10
  import * as functions from 'firebase-functions';
11
11
  import dotenv from 'dotenv';
12
- import { getCircuitsCollectionPath, getTimeoutsCollectionPath, commonTerms, finalContributionIndex, getContributionsCollectionPath, githubReputation, getBucketName, vmBootstrapCommand, vmDependenciesAndCacheArtifactsCommand, vmBootstrapScriptFilename, computeDiskSizeForVM, createEC2Instance, getParticipantsCollectionPath, terminateEC2Instance, formatZkeyIndex, getTranscriptStorageFilePath, getZkeyStorageFilePath, startEC2Instance, vmContributionVerificationCommand, runCommandUsingSSM, getPotStorageFilePath, genesisZkeyIndex, createCustomLoggerForFile, blake512FromPath, getVerificationKeyStorageFilePath, getVerifierContractStorageFilePath, computeSHA256ToHex, retrieveCommandStatus, checkIfRunning, retrieveCommandOutput, stopEC2Instance, verificationKeyAcronym, verifierSmartContractAcronym } from '@p0tion/actions';
12
+ import { getCircuitsCollectionPath, getTimeoutsCollectionPath, commonTerms, finalContributionIndex, getContributionsCollectionPath, githubReputation, getBucketName, vmBootstrapCommand, vmDependenciesAndCacheArtifactsCommand, vmBootstrapScriptFilename, computeDiskSizeForVM, createEC2Instance, getParticipantsCollectionPath, terminateEC2Instance, formatZkeyIndex, getTranscriptStorageFilePath, getZkeyStorageFilePath, startEC2Instance, vmContributionVerificationCommand, runCommandUsingSSM, getPotStorageFilePath, genesisZkeyIndex, createCustomLoggerForFile, blake512FromPath, getVerificationKeyStorageFilePath, getVerifierContractStorageFilePath, computeSHA256ToHex, retrieveCommandStatus, checkIfRunning, retrieveCommandOutput, stopEC2Instance, verificationKeyAcronym, verifierSmartContractAcronym } from '@devtion/actions';
13
13
  import { encode } from 'html-entities';
14
14
  import { Timestamp, FieldValue } from 'firebase-admin/firestore';
15
15
  import { S3Client, GetObjectCommand, PutObjectCommand, DeleteObjectCommand, HeadBucketCommand, CreateBucketCommand, PutPublicAccessBlockCommand, PutBucketCorsCommand, HeadObjectCommand, CreateMultipartUploadCommand, UploadPartCommand, CompleteMultipartUploadCommand } from '@aws-sdk/client-s3';
@@ -19,7 +19,7 @@ import { pipeline } from 'node:stream';
19
19
  import { promisify } from 'node:util';
20
20
  import fs, { readFileSync } from 'fs';
21
21
  import mime from 'mime-types';
22
- import { setTimeout as setTimeout$1 } from 'timers/promises';
22
+ import { setTimeout } from 'timers/promises';
23
23
  import fetch from '@adobe/node-fetch-retry';
24
24
  import path from 'path';
25
25
  import os from 'os';
@@ -191,7 +191,7 @@ const getCurrentServerTimestampInMillis = () => Timestamp.now().toMillis();
191
191
  * Interrupt the current execution for a specified amount of time.
192
192
  * @param ms <number> - the amount of time expressed in milliseconds.
193
193
  */
194
- const sleep = async (ms) => setTimeout$1(ms);
194
+ const sleep = async (ms) => setTimeout(ms);
195
195
  /**
196
196
  * Query for ceremony circuits.
197
197
  * @notice the order by sequence position is fundamental to maintain parallelism among contributions for different circuits.
@@ -521,8 +521,10 @@ const registerAuthUser = functions
521
521
  const { uid } = user;
522
522
  // Reference to a document using uid.
523
523
  const userRef = firestore.collection(commonTerms.collections.users.name).doc(uid);
524
- // html encode the display name
525
- const encodedDisplayName = encode(displayName);
524
+ // html encode the display name (or put the ID if the name is not displayed)
525
+ const encodedDisplayName = user.displayName === "Null" || user.displayName === null ? user.uid : encode(displayName);
526
+ // store the avatar URL of a contributor
527
+ let avatarUrl = "";
526
528
  // we only do reputation check if the user is not a coordinator
527
529
  if (!(email?.endsWith(`@${process.env.CUSTOM_CLAIMS_COORDINATOR_EMAIL_ADDRESS_OR_DOMAIN}`) ||
528
530
  email === process.env.CUSTOM_CLAIMS_COORDINATOR_EMAIL_ADDRESS_OR_DOMAIN)) {
@@ -532,14 +534,16 @@ const registerAuthUser = functions
532
534
  const vars = getGitHubVariables();
533
535
  // this return true or false
534
536
  try {
535
- const res = await githubReputation(user.providerData[0].uid, vars.minimumFollowing, vars.minimumFollowers, vars.minimumPublicRepos);
536
- if (!res) {
537
+ const { reputable, avatarUrl: avatarURL } = await githubReputation(user.providerData[0].uid, vars.minimumFollowing, vars.minimumFollowers, vars.minimumPublicRepos);
538
+ if (!reputable) {
537
539
  // Delete user
538
540
  await auth.deleteUser(user.uid);
539
541
  // Throw error
540
- logAndThrowError(makeError("permission-denied", "The user is not allowed to sign up because their Github reputation is not high enough.", `The user ${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.`));
542
+ logAndThrowError(makeError("permission-denied", "The user is not allowed to sign up because their Github reputation is not high enough.", `The user ${user.displayName === "Null" || user.displayName === null ? user.uid : user.displayName} is not allowed to sign up because their Github reputation is not high enough. Please contact the administrator if you think this is a mistake.`));
541
543
  }
542
- printLog(`Github reputation check passed for user ${user.displayName}`, LogLevel.DEBUG);
544
+ // store locally
545
+ avatarUrl = avatarURL;
546
+ printLog(`Github reputation check passed for user ${user.displayName === "Null" || user.displayName === null ? user.uid : user.displayName}`, LogLevel.DEBUG);
543
547
  }
544
548
  catch (error) {
545
549
  // Delete user
@@ -549,6 +553,8 @@ const registerAuthUser = functions
549
553
  }
550
554
  }
551
555
  // Set document (nb. we refer to providerData[0] because we use Github OAuth provider only).
556
+ // In future releases we might want to loop through the providerData array as we support
557
+ // more providers.
552
558
  await userRef.set({
553
559
  name: encodedDisplayName,
554
560
  encodedDisplayName,
@@ -561,7 +567,13 @@ const registerAuthUser = functions
561
567
  photoURL: photoURL || "",
562
568
  lastUpdated: getCurrentServerTimestampInMillis()
563
569
  });
570
+ // we want to create a new collection for the users to store the avatars
571
+ const avatarRef = firestore.collection(commonTerms.collections.avatars.name).doc(uid);
572
+ await avatarRef.set({
573
+ avatarUrl: avatarUrl || "",
574
+ });
564
575
  printLog(`Authenticated user document with identifier ${uid} has been correctly stored`, LogLevel.DEBUG);
576
+ printLog(`Authenticated user avatar with identifier ${uid} has been correctly stored`, LogLevel.DEBUG);
565
577
  });
566
578
  /**
567
579
  * Set custom claims for role-based access control on the newly created user.
@@ -698,7 +710,7 @@ const setupCeremony = functions
698
710
  // Check if using the VM approach for contribution verification.
699
711
  if (circuit.verification.cfOrVm === "VM" /* CircuitContributionVerificationMechanism.VM */) {
700
712
  // VM command to be run at the startup.
701
- const startupCommand = vmBootstrapCommand(bucketName);
713
+ const startupCommand = vmBootstrapCommand(`${bucketName}/circuits/${circuit.name}`);
702
714
  // Get EC2 client.
703
715
  const ec2Client = await createEC2Client();
704
716
  // Get AWS variables.
@@ -707,7 +719,8 @@ const setupCeremony = functions
707
719
  const vmCommands = vmDependenciesAndCacheArtifactsCommand(`${bucketName}/${circuit.files?.initialZkeyStoragePath}`, `${bucketName}/${circuit.files?.potStoragePath}`, snsTopic, region);
708
720
  printLog(`Check VM dependencies and cache artifacts commands ${vmCommands.join("\n")}`, LogLevel.DEBUG);
709
721
  // Upload the post-startup commands script file.
710
- await uploadFileToBucketNoFile(bucketName, vmBootstrapScriptFilename, vmCommands.join("\n"));
722
+ printLog(`Uploading VM post-startup commands script file ${vmBootstrapScriptFilename}`, LogLevel.DEBUG);
723
+ await uploadFileToBucketNoFile(bucketName, `circuits/${circuit.name}/${vmBootstrapScriptFilename}`, vmCommands.join("\n"));
711
724
  // Compute the VM disk space requirement (in GB).
712
725
  const vmDiskSize = computeDiskSizeForVM(circuit.zKeySizeInBytes, circuit.metadata?.pot);
713
726
  printLog(`Check VM startup commands ${startupCommand.join("\n")}`, LogLevel.DEBUG);
@@ -1269,6 +1282,7 @@ const coordinate = async (participant, circuit, isSingleParticipantCoordination,
1269
1282
  printLog(`Coordinate - executing scenario A - single - participantResumingAfterTimeoutExpiration`, LogLevel.DEBUG);
1270
1283
  newParticipantStatus = "CONTRIBUTING" /* ParticipantStatus.CONTRIBUTING */;
1271
1284
  newContributionStep = "DOWNLOADING" /* ParticipantContributionStep.DOWNLOADING */;
1285
+ newCurrentContributorId = participant.id;
1272
1286
  }
1273
1287
  // Scenario (B).
1274
1288
  else if (participantIsNotCurrentContributor) {
@@ -1377,53 +1391,6 @@ const waitForVMCommandExecution = (resolve, reject, ssm, vmInstanceId, commandId
1377
1391
  }
1378
1392
  }, 60000); // 1 minute.
1379
1393
  };
1380
- /**
1381
- * Wait until the artifacts have been downloaded.
1382
- * @param {any} resolve the promise.
1383
- * @param {any} reject the promise.
1384
- * @param {string} potTempFilePath the tmp path to the locally downloaded pot file.
1385
- * @param {string} firstZkeyTempFilePath the tmp path to the locally downloaded first zkey file.
1386
- * @param {string} lastZkeyTempFilePath the tmp path to the locally downloaded last zkey file.
1387
- */
1388
- const waitForFileDownload = (resolve, reject, potTempFilePath, firstZkeyTempFilePath, lastZkeyTempFilePath, circuitId, participantId) => {
1389
- const maxWaitTime = 5 * 60 * 1000; // 5 minutes
1390
- // every second check if the file download was completed
1391
- const interval = setInterval(async () => {
1392
- printLog(`Verifying that the artifacts were downloaded for circuit ${circuitId} and participant ${participantId}`, LogLevel.DEBUG);
1393
- try {
1394
- // check if files have been downloaded
1395
- if (!fs.existsSync(potTempFilePath)) {
1396
- printLog(`Pot file not found at ${potTempFilePath}`, LogLevel.DEBUG);
1397
- }
1398
- if (!fs.existsSync(firstZkeyTempFilePath)) {
1399
- printLog(`First zkey file not found at ${firstZkeyTempFilePath}`, LogLevel.DEBUG);
1400
- }
1401
- if (!fs.existsSync(lastZkeyTempFilePath)) {
1402
- printLog(`Last zkey file not found at ${lastZkeyTempFilePath}`, LogLevel.DEBUG);
1403
- }
1404
- // if all files were downloaded
1405
- if (fs.existsSync(potTempFilePath) && fs.existsSync(firstZkeyTempFilePath) && fs.existsSync(lastZkeyTempFilePath)) {
1406
- printLog(`All required files are present on disk.`, LogLevel.INFO);
1407
- // resolve the promise
1408
- resolve();
1409
- }
1410
- }
1411
- catch (error) {
1412
- // if we have an error then we print it as a warning and reject
1413
- printLog(`Error while downloading files: ${error}`, LogLevel.WARN);
1414
- reject();
1415
- }
1416
- finally {
1417
- printLog(`Clearing the interval for file download. Circuit ${circuitId} and participant ${participantId}`, LogLevel.DEBUG);
1418
- clearInterval(interval);
1419
- }
1420
- }, 5000);
1421
- // we want to clean in 5 minutes in case
1422
- setTimeout(() => {
1423
- clearInterval(interval);
1424
- reject(new Error('Timeout exceeded while waiting for files to be downloaded.'));
1425
- }, maxWaitTime);
1426
- };
1427
1394
  /**
1428
1395
  * This method is used to coordinate the waiting queues of ceremony circuits.
1429
1396
  * @dev this cloud function is triggered whenever an update of a document related to a participant of a ceremony occurs.
@@ -1658,8 +1625,6 @@ const verifycontribution = functionsV2.https.onCall({ memory: "16GiB", timeoutSe
1658
1625
  lastZkeyBlake2bHash = match.at(0);
1659
1626
  // re upload the formatted verification transcript
1660
1627
  await uploadFileToBucket(bucketName, verificationTranscriptStoragePathAndFilename, verificationTranscriptTemporaryLocalPath, true);
1661
- // Stop VM instance.
1662
- await stopEC2Instance(ec2, vmInstanceId);
1663
1628
  }
1664
1629
  else {
1665
1630
  // Upload verification transcript.
@@ -1720,6 +1685,9 @@ const verifycontribution = functionsV2.https.onCall({ memory: "16GiB", timeoutSe
1720
1685
  lastUpdated: getCurrentServerTimestampInMillis()
1721
1686
  });
1722
1687
  }
1688
+ // Stop VM instance
1689
+ if (isUsingVM)
1690
+ await stopEC2Instance(ec2, vmInstanceId);
1723
1691
  // Step (1.A.4.C)
1724
1692
  if (!isFinalizing) {
1725
1693
  // Step (1.A.4.C.1)
@@ -1734,7 +1702,9 @@ const verifycontribution = functionsV2.https.onCall({ memory: "16GiB", timeoutSe
1734
1702
  const newAvgVerifyCloudFunctionTime = avgVerifyCloudFunctionTime > 0
1735
1703
  ? (avgVerifyCloudFunctionTime + verifyCloudFunctionTime) / 2
1736
1704
  : verifyCloudFunctionTime;
1737
- // Prepare tx to update circuit average contribution/verification time.
1705
+ // Prepare tx to update circuit average contribution/verification time.
1706
+ const updatedCircuitDoc = await getDocumentById(getCircuitsCollectionPath(ceremonyId), circuitId);
1707
+ const { waitingQueue: updatedWaitingQueue } = updatedCircuitDoc.data();
1738
1708
  /// @dev this must happen only for valid contributions.
1739
1709
  batch.update(circuitDoc.ref, {
1740
1710
  avgTimings: {
@@ -1747,7 +1717,7 @@ const verifycontribution = functionsV2.https.onCall({ memory: "16GiB", timeoutSe
1747
1717
  : avgVerifyCloudFunctionTime
1748
1718
  },
1749
1719
  waitingQueue: {
1750
- ...waitingQueue,
1720
+ ...updatedWaitingQueue,
1751
1721
  completedContributions: isContributionValid
1752
1722
  ? completedContributions + 1
1753
1723
  : completedContributions,
@@ -1812,33 +1782,21 @@ const verifycontribution = functionsV2.https.onCall({ memory: "16GiB", timeoutSe
1812
1782
  await downloadArtifactFromS3Bucket(bucketName, potStoragePath, potTempFilePath);
1813
1783
  await downloadArtifactFromS3Bucket(bucketName, firstZkeyStoragePath, firstZkeyTempFilePath);
1814
1784
  await downloadArtifactFromS3Bucket(bucketName, lastZkeyStoragePath, lastZkeyTempFilePath);
1815
- await sleep(6000);
1816
- // wait until the files are actually downloaded
1817
- return new Promise((resolve, reject) => waitForFileDownload(resolve, reject, potTempFilePath, firstZkeyTempFilePath, lastZkeyTempFilePath, circuitId, participantDoc.id))
1818
- .then(async () => {
1819
- printLog(`Downloads from AWS S3 bucket completed - ceremony ${ceremonyId} circuit ${circuitId}`, LogLevel.DEBUG);
1820
- // Step (1.A.4).
1821
- isContributionValid = await zKey.verifyFromInit(firstZkeyTempFilePath, potTempFilePath, lastZkeyTempFilePath, transcriptLogger);
1822
- // Compute contribution hash.
1823
- lastZkeyBlake2bHash = await blake512FromPath(lastZkeyTempFilePath);
1824
- // Free resources by unlinking temporary folders.
1825
- // Do not free-up verification transcript path here.
1826
- try {
1827
- fs.unlinkSync(potTempFilePath);
1828
- fs.unlinkSync(firstZkeyTempFilePath);
1829
- fs.unlinkSync(lastZkeyTempFilePath);
1830
- }
1831
- catch (error) {
1832
- printLog(`Error while unlinking temporary files - Error ${error}`, LogLevel.WARN);
1833
- }
1834
- await completeVerification();
1835
- })
1836
- .catch((error) => {
1837
- // Throw the new error
1838
- const commonError = COMMON_ERRORS.CM_INVALID_REQUEST;
1839
- const additionalDetails = error.toString();
1840
- logAndThrowError(makeError(commonError.code, commonError.message, additionalDetails));
1841
- });
1785
+ // Step (1.A.4).
1786
+ isContributionValid = await zKey.verifyFromInit(firstZkeyTempFilePath, potTempFilePath, lastZkeyTempFilePath, transcriptLogger);
1787
+ // Compute contribution hash.
1788
+ lastZkeyBlake2bHash = await blake512FromPath(lastZkeyTempFilePath);
1789
+ // Free resources by unlinking temporary folders.
1790
+ // Do not free-up verification transcript path here.
1791
+ try {
1792
+ fs.unlinkSync(potTempFilePath);
1793
+ fs.unlinkSync(firstZkeyTempFilePath);
1794
+ fs.unlinkSync(lastZkeyTempFilePath);
1795
+ }
1796
+ catch (error) {
1797
+ printLog(`Error while unlinking temporary files - Error ${error}`, LogLevel.WARN);
1798
+ }
1799
+ await completeVerification();
1842
1800
  }
1843
1801
  }
1844
1802
  });
@@ -2500,7 +2458,7 @@ const checkAndRemoveBlockingContributor = functions
2500
2458
  // Prepare Firestore batch of txs.
2501
2459
  const batch = firestore.batch();
2502
2460
  // Remove current contributor from waiting queue.
2503
- contributors.shift(1);
2461
+ contributors.shift();
2504
2462
  // Check if someone else is ready to start the contribution.
2505
2463
  if (contributors.length > 0) {
2506
2464
  // Step (E.1).
@@ -2584,7 +2542,8 @@ const resumeContributionAfterTimeoutExpiration = functions
2584
2542
  if (status === "EXHUMED" /* ParticipantStatus.EXHUMED */)
2585
2543
  await participantDoc.ref.update({
2586
2544
  status: "READY" /* ParticipantStatus.READY */,
2587
- lastUpdated: getCurrentServerTimestampInMillis()
2545
+ lastUpdated: getCurrentServerTimestampInMillis(),
2546
+ tempContributionData: {}
2588
2547
  });
2589
2548
  else
2590
2549
  logAndThrowError(SPECIFIC_ERRORS.SE_CONTRIBUTE_CANNOT_PROGRESS_TO_NEXT_CIRCUIT);
@@ -1 +1 @@
1
- {"version":3,"file":"ceremony.d.ts","sourceRoot":"","sources":["../../../src/functions/ceremony.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,oBAAoB,CAAA;AAuC/C;;;;;GAKG;AACH,eAAO,MAAM,aAAa,kCAiBpB,CAAA;AAEN;;;;;GAKG;AACH,eAAO,MAAM,YAAY,kCAkBnB,CAAA;AAEN;;;;GAIG;AACH,eAAO,MAAM,aAAa,mDAyHpB,CAAA;AAEN;;;GAGG;AACH,eAAO,MAAM,+BAA+B,oEAsCtC,CAAA;AAEN;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,mDAiEvB,CAAA"}
1
+ {"version":3,"file":"ceremony.d.ts","sourceRoot":"","sources":["../../../src/functions/ceremony.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,oBAAoB,CAAA;AAuC/C;;;;;GAKG;AACH,eAAO,MAAM,aAAa,kCAiBpB,CAAA;AAEN;;;;;GAKG;AACH,eAAO,MAAM,YAAY,kCAkBnB,CAAA;AAEN;;;;GAIG;AACH,eAAO,MAAM,aAAa,mDA0HpB,CAAA;AAEN;;;GAGG;AACH,eAAO,MAAM,+BAA+B,oEAsCtC,CAAA;AAEN;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,mDAiEvB,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"circuit.d.ts","sourceRoot":"","sources":["../../../src/functions/circuit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,WAAW,MAAM,uBAAuB,CAAA;AACpD,OAAO,KAAK,WAAW,MAAM,uBAAuB,CAAA;AAyCpD,OAAO,EAAuB,sBAAsB,EAAE,MAAM,gBAAgB,CAAA;AAyR5E;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,6BAA6B,4FAoGpC,CAAA;AA8BN;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,kBAAkB,0EAya9B,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,+CAA+C,wEA4EtD,CAAA;AAEN;;;;GAIG;AACH,eAAO,MAAM,eAAe,uDA8EtB,CAAA"}
1
+ {"version":3,"file":"circuit.d.ts","sourceRoot":"","sources":["../../../src/functions/circuit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,WAAW,MAAM,uBAAuB,CAAA;AACpD,OAAO,KAAK,WAAW,MAAM,uBAAuB,CAAA;AAyCpD,OAAO,EAAuB,sBAAsB,EAAE,MAAM,gBAAgB,CAAA;AAkO5E;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,6BAA6B,4FAoGpC,CAAA;AA8BN;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,kBAAkB,0EA2Z9B,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,+CAA+C,wEA4EtD,CAAA;AAEN;;;;GAIG;AACH,eAAO,MAAM,eAAe,uDA8EtB,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"timeout.d.ts","sourceRoot":"","sources":["../../../src/functions/timeout.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,oBAAoB,CAAA;AAuB/C;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,iCAAiC,kCA6MxC,CAAA;AAEN;;;GAGG;AACH,eAAO,MAAM,wCAAwC,mDAyC/C,CAAA"}
1
+ {"version":3,"file":"timeout.d.ts","sourceRoot":"","sources":["../../../src/functions/timeout.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,oBAAoB,CAAA;AAuB/C;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,iCAAiC,kCA6MxC,CAAA;AAEN;;;GAGG;AACH,eAAO,MAAM,wCAAwC,mDA0C/C,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../../src/functions/user.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,oBAAoB,CAAA;AAW/C;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,mEAuFvB,CAAA;AACN;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,mEA+BpC,CAAA"}
1
+ {"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../../src/functions/user.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,oBAAoB,CAAA;AAW/C;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,mEAqGvB,CAAA;AACN;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,mEA+BpC,CAAA"}
@@ -1,6 +1,6 @@
1
1
  import { DocumentData, QuerySnapshot, DocumentSnapshot, QueryDocumentSnapshot, WhereFilterOp } from "firebase-admin/firestore";
2
2
  import admin from "firebase-admin";
3
- import { CircuitDocument } from "@p0tion/actions";
3
+ import { CircuitDocument } from "@devtion/actions";
4
4
  import { SSMClient } from "@aws-sdk/client-ssm";
5
5
  import { EC2Client } from "@aws-sdk/client-ec2";
6
6
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/lib/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,qBAAqB,EAErB,aAAa,EAChB,MAAM,0BAA0B,CAAA;AACjC,OAAO,KAAK,MAAM,gBAAgB,CAAA;AAWlC,OAAO,EAOH,eAAe,EAClB,MAAM,iBAAiB,CAAA;AAIxB,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAM/C;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,eACZ,MAAM,cACN,MAAM,KACnB,QAAQ,iBAAiB,YAAY,CAAC,CASxC,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,iCAAiC,QAAO,MAAoC,CAAA;AAEzF;;;GAGG;AACH,eAAO,MAAM,KAAK,OAAc,MAAM,KAAG,QAAQ,IAAI,CAAmB,CAAA;AAExE;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,eAAsB,MAAM,KAAG,QAAQ,MAAM,sBAAsB,YAAY,CAAC,CAAC,CAYhH,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,+BAA+B,eAC5B,MAAM,aACP,MAAM,KAClB,QAAQ,MAAM,sBAAsB,YAAY,CAAC,CAAC,CAUpD,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,eACpB,MAAM,iBACH,MAAM,KACtB,QAAQ,cAAc,YAAY,CAAC,CASrC,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,QAAa,QAAQ,MAAM,sBAAsB,YAAY,CAAC,CAAC,CAWhG,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,eACzB,MAAM,oBACA,MAAM,KACzB,QAAQ,sBAAsB,YAAY,CAAC,CAY7C,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,qBAAsB,MAAM,KAAG,MAAkD,CAAA;AAEtH;;;;;;GAMG;AACH,eAAO,MAAM,4BAA4B,eAAsB,MAAM,aAAa,MAAM,iBAAiB,MAAM,kBA6B9G,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,eACf,MAAM,aACP,MAAM,iBACF,MAAM,aACX,OAAO,kBA4BpB,CAAA;AAED,eAAO,MAAM,wBAAwB,eACrB,MAAM,aACP,MAAM,QACX,MAAM,aACF,OAAO,kBAyBpB,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,YAAY,eAAsB,MAAM,aAAa,MAAM,kBAWvE,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,6BAA6B,UAC/B,MAAM,wBACS,OAAO,SACtB,aAAa,KACrB,QAAQ,MAAM,SAAS,cAAc,CAAC,MAAM,SAAS,aAAa,CAAC,CAYxD,CAAA;AAEd;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,eACjB,MAAM,aACP,MAAM,KAClB,QAAQ,sBAAsB,YAAY,CAAC,CAgB7C,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,oBAAqB,eAAe,KAAG,eAKvE,CAAA;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,QAAO,GAarC,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,eAAe,QAAO,GAkBlC,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,eAAe,QAAa,QAAQ,SAAS,CAYzD,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,eAAe,QAAa,QAAQ,SAAS,CAYzD,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,cAAqB,MAAM,KAAG,QAAQ,MAAM,CAQxE,CAAA"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/lib/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,qBAAqB,EAErB,aAAa,EAChB,MAAM,0BAA0B,CAAA;AACjC,OAAO,KAAK,MAAM,gBAAgB,CAAA;AAWlC,OAAO,EAOH,eAAe,EAClB,MAAM,kBAAkB,CAAA;AAIzB,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAM/C;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,eACZ,MAAM,cACN,MAAM,KACnB,QAAQ,iBAAiB,YAAY,CAAC,CASxC,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,iCAAiC,QAAO,MAAoC,CAAA;AAEzF;;;GAGG;AACH,eAAO,MAAM,KAAK,OAAc,MAAM,KAAG,QAAQ,IAAI,CAAmB,CAAA;AAExE;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,eAAsB,MAAM,KAAG,QAAQ,MAAM,sBAAsB,YAAY,CAAC,CAAC,CAYhH,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,+BAA+B,eAC5B,MAAM,aACP,MAAM,KAClB,QAAQ,MAAM,sBAAsB,YAAY,CAAC,CAAC,CAUpD,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,eACpB,MAAM,iBACH,MAAM,KACtB,QAAQ,cAAc,YAAY,CAAC,CASrC,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,QAAa,QAAQ,MAAM,sBAAsB,YAAY,CAAC,CAAC,CAWhG,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,eACzB,MAAM,oBACA,MAAM,KACzB,QAAQ,sBAAsB,YAAY,CAAC,CAY7C,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,qBAAsB,MAAM,KAAG,MAAkD,CAAA;AAEtH;;;;;;GAMG;AACH,eAAO,MAAM,4BAA4B,eAAsB,MAAM,aAAa,MAAM,iBAAiB,MAAM,kBA6B9G,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,eACf,MAAM,aACP,MAAM,iBACF,MAAM,aACX,OAAO,kBA4BpB,CAAA;AAED,eAAO,MAAM,wBAAwB,eACrB,MAAM,aACP,MAAM,QACX,MAAM,aACF,OAAO,kBAyBpB,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,YAAY,eAAsB,MAAM,aAAa,MAAM,kBAWvE,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,6BAA6B,UAC/B,MAAM,wBACS,OAAO,SACtB,aAAa,KACrB,QAAQ,MAAM,SAAS,cAAc,CAAC,MAAM,SAAS,aAAa,CAAC,CAYxD,CAAA;AAEd;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,eACjB,MAAM,aACP,MAAM,KAClB,QAAQ,sBAAsB,YAAY,CAAC,CAgB7C,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,oBAAqB,eAAe,KAAG,eAKvE,CAAA;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,QAAO,GAarC,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,eAAe,QAAO,GAkBlC,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,eAAe,QAAa,QAAQ,SAAS,CAYzD,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,eAAe,QAAa,QAAQ,SAAS,CAYzD,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,cAAqB,MAAM,KAAG,QAAQ,MAAM,CAQxE,CAAA"}
@@ -1,4 +1,4 @@
1
- import { CeremonyInputData, CircuitDocument, ETagWithPartNumber } from "@p0tion/actions";
1
+ import { CeremonyInputData, CircuitDocument, ETagWithPartNumber } from "@devtion/actions";
2
2
  /**
3
3
  * Group all the necessary data needed for running the `setupCeremony` cloud function.
4
4
  * @typedef {Object} SetupCeremonyData
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/types/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AAExF;;;;;;GAMG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC5B,iBAAiB,EAAE,iBAAiB,CAAA;IACpC,cAAc,EAAE,MAAM,CAAA;IACtB,QAAQ,EAAE,KAAK,CAAC,eAAe,CAAC,CAAA;CACnC,CAAA;AAED;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC3B,UAAU,EAAE,MAAM,CAAA;CACrB,CAAA;AAED;;;;;GAKG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACjC,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;CACpB,CAAA;AAED;;;;;;GAMG;AACH,MAAM,MAAM,wBAAwB,GAAG,sBAAsB,GAAG;IAC5D,UAAU,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,8BAA8B,GAAG,sBAAsB,GAAG;IAClE,QAAQ,EAAE,MAAM,CAAA;IAChB,aAAa,EAAE,MAAM,CAAA;IACrB,UAAU,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,2BAA2B,GAAG,sBAAsB,GAAG;IAC/D,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,KAAK,CAAC,kBAAkB,CAAC,CAAA;IAChC,UAAU,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAED;;;;;;GAMG;AACH,MAAM,MAAM,8CAA8C,GAAG;IACzD,UAAU,EAAE,MAAM,CAAA;IAClB,2BAA2B,EAAE,MAAM,CAAA;IACnC,gBAAgB,EAAE,MAAM,CAAA;CAC3B,CAAA;AAED;;;;;GAKG;AACH,MAAM,MAAM,kDAAkD,GAAG;IAC7D,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;;;;GAKG;AACH,MAAM,MAAM,kDAAkD,GAAG;IAC7D,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,kBAAkB,CAAA;CAC5B,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACjC,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,kCAAkC,EAAE,MAAM,CAAA;CAC7C,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAC9B,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;CACjB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/types/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAEzF;;;;;;GAMG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC5B,iBAAiB,EAAE,iBAAiB,CAAA;IACpC,cAAc,EAAE,MAAM,CAAA;IACtB,QAAQ,EAAE,KAAK,CAAC,eAAe,CAAC,CAAA;CACnC,CAAA;AAED;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC3B,UAAU,EAAE,MAAM,CAAA;CACrB,CAAA;AAED;;;;;GAKG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACjC,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;CACpB,CAAA;AAED;;;;;;GAMG;AACH,MAAM,MAAM,wBAAwB,GAAG,sBAAsB,GAAG;IAC5D,UAAU,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,8BAA8B,GAAG,sBAAsB,GAAG;IAClE,QAAQ,EAAE,MAAM,CAAA;IAChB,aAAa,EAAE,MAAM,CAAA;IACrB,UAAU,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,2BAA2B,GAAG,sBAAsB,GAAG;IAC/D,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,KAAK,CAAC,kBAAkB,CAAC,CAAA;IAChC,UAAU,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAED;;;;;;GAMG;AACH,MAAM,MAAM,8CAA8C,GAAG;IACzD,UAAU,EAAE,MAAM,CAAA;IAClB,2BAA2B,EAAE,MAAM,CAAA;IACnC,gBAAgB,EAAE,MAAM,CAAA;CAC3B,CAAA;AAED;;;;;GAKG;AACH,MAAM,MAAM,kDAAkD,GAAG;IAC7D,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;;;;GAKG;AACH,MAAM,MAAM,kDAAkD,GAAG;IAC7D,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,kBAAkB,CAAA;CAC5B,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACjC,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,kCAAkC,EAAE,MAAM,CAAA;CAC7C,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAC9B,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;CACjB,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devtion/backend",
3
- "version": "0.0.0-7e983e3",
3
+ "version": "0.0.0-8b5a17f",
4
4
  "description": "MPC Phase 2 backend for Firebase services management",
5
5
  "repository": "git@github.com:privacy-scaling-explorations/p0tion.git",
6
6
  "homepage": "https://github.com/privacy-scaling-explorations/p0tion",
@@ -67,7 +67,7 @@
67
67
  "@aws-sdk/client-ssm": "^3.357.0",
68
68
  "@aws-sdk/middleware-endpoint": "^3.329.0",
69
69
  "@aws-sdk/s3-request-presigner": "^3.329.0",
70
- "@p0tion/actions": "^1.0.5",
70
+ "@devtion/actions": "latest",
71
71
  "blakejs": "^1.2.1",
72
72
  "dotenv": "^16.0.3",
73
73
  "ethers": "5.7.2",
@@ -85,5 +85,5 @@
85
85
  "publishConfig": {
86
86
  "access": "public"
87
87
  },
88
- "gitHead": "afae72061a3b366b05508de53fe91e9b254cc3d5"
88
+ "gitHead": "df754397c15a7086a163688fb4d3f4e7c98ad379"
89
89
  }
@@ -18,7 +18,7 @@ import {
18
18
  vmBootstrapCommand,
19
19
  vmDependenciesAndCacheArtifactsCommand,
20
20
  vmBootstrapScriptFilename
21
- } from "@p0tion/actions"
21
+ } from "@devtion/actions"
22
22
  import { encode } from "html-entities"
23
23
  import { SetupCeremonyData } from "../types/index"
24
24
  import { COMMON_ERRORS, logAndThrowError, printLog, SPECIFIC_ERRORS } from "../lib/errors"
@@ -146,7 +146,7 @@ export const setupCeremony = functions
146
146
  // Check if using the VM approach for contribution verification.
147
147
  if (circuit.verification.cfOrVm === CircuitContributionVerificationMechanism.VM) {
148
148
  // VM command to be run at the startup.
149
- const startupCommand = vmBootstrapCommand(bucketName)
149
+ const startupCommand = vmBootstrapCommand(`${bucketName}/circuits/${circuit.name!}`)
150
150
 
151
151
  // Get EC2 client.
152
152
  const ec2Client = await createEC2Client()
@@ -165,7 +165,8 @@ export const setupCeremony = functions
165
165
  printLog(`Check VM dependencies and cache artifacts commands ${vmCommands.join("\n")}`, LogLevel.DEBUG)
166
166
 
167
167
  // Upload the post-startup commands script file.
168
- await uploadFileToBucketNoFile(bucketName, vmBootstrapScriptFilename, vmCommands.join("\n"))
168
+ printLog(`Uploading VM post-startup commands script file ${vmBootstrapScriptFilename}`, LogLevel.DEBUG)
169
+ await uploadFileToBucketNoFile(bucketName, `circuits/${circuit.name!}/${vmBootstrapScriptFilename}`, vmCommands.join("\n"))
169
170
 
170
171
  // Compute the VM disk space requirement (in GB).
171
172
  const vmDiskSize = computeDiskSizeForVM(circuit.zKeySizeInBytes!, circuit.metadata?.pot!)
@@ -37,7 +37,7 @@ import {
37
37
  createCustomLoggerForFile,
38
38
  retrieveCommandStatus,
39
39
  stopEC2Instance
40
- } from "@p0tion/actions"
40
+ } from "@devtion/actions"
41
41
  import { zKey } from "snarkjs"
42
42
  import { CommandInvocationStatus, SSMClient } from "@aws-sdk/client-ssm"
43
43
  import { FinalizeCircuitData, VerifyContributionData } from "../types/index"
@@ -131,6 +131,7 @@ const coordinate = async (
131
131
 
132
132
  newParticipantStatus = ParticipantStatus.CONTRIBUTING
133
133
  newContributionStep = ParticipantContributionStep.DOWNLOADING
134
+ newCurrentContributorId = participant.id
134
135
  }
135
136
  // Scenario (B).
136
137
  else if (participantIsNotCurrentContributor) {
@@ -265,62 +266,6 @@ const waitForVMCommandExecution = (
265
266
  }, 60000) // 1 minute.
266
267
  }
267
268
 
268
- /**
269
- * Wait until the artifacts have been downloaded.
270
- * @param {any} resolve the promise.
271
- * @param {any} reject the promise.
272
- * @param {string} potTempFilePath the tmp path to the locally downloaded pot file.
273
- * @param {string} firstZkeyTempFilePath the tmp path to the locally downloaded first zkey file.
274
- * @param {string} lastZkeyTempFilePath the tmp path to the locally downloaded last zkey file.
275
- */
276
- const waitForFileDownload = (
277
- resolve: any,
278
- reject: any,
279
- potTempFilePath: string,
280
- firstZkeyTempFilePath: string,
281
- lastZkeyTempFilePath: string,
282
- circuitId: string,
283
- participantId: string
284
- ) => {
285
- const maxWaitTime = 5 * 60 * 1000 // 5 minutes
286
- // every second check if the file download was completed
287
- const interval = setInterval(async () => {
288
- printLog(`Verifying that the artifacts were downloaded for circuit ${circuitId} and participant ${participantId}`, LogLevel.DEBUG)
289
- try {
290
- // check if files have been downloaded
291
- if (!fs.existsSync(potTempFilePath)) {
292
- printLog(`Pot file not found at ${potTempFilePath}`, LogLevel.DEBUG)
293
- }
294
- if (!fs.existsSync(firstZkeyTempFilePath)) {
295
- printLog(`First zkey file not found at ${firstZkeyTempFilePath}`, LogLevel.DEBUG)
296
- }
297
- if (!fs.existsSync(lastZkeyTempFilePath)) {
298
- printLog(`Last zkey file not found at ${lastZkeyTempFilePath}`, LogLevel.DEBUG)
299
- }
300
-
301
- // if all files were downloaded
302
- if (fs.existsSync(potTempFilePath) && fs.existsSync(firstZkeyTempFilePath) && fs.existsSync(lastZkeyTempFilePath)) {
303
- printLog(`All required files are present on disk.`, LogLevel.INFO)
304
- // resolve the promise
305
- resolve()
306
- }
307
- } catch (error: any) {
308
- // if we have an error then we print it as a warning and reject
309
- printLog(`Error while downloading files: ${error}`, LogLevel.WARN)
310
- reject()
311
- } finally {
312
- printLog(`Clearing the interval for file download. Circuit ${circuitId} and participant ${participantId}`, LogLevel.DEBUG)
313
- clearInterval(interval)
314
- }
315
- }, 5000)
316
-
317
- // we want to clean in 5 minutes in case
318
- setTimeout(() => {
319
- clearInterval(interval)
320
- reject(new Error('Timeout exceeded while waiting for files to be downloaded.'))
321
- }, maxWaitTime)
322
- }
323
-
324
269
  /**
325
270
  * This method is used to coordinate the waiting queues of ceremony circuits.
326
271
  * @dev this cloud function is triggered whenever an update of a document related to a participant of a ceremony occurs.
@@ -666,8 +611,6 @@ export const verifycontribution = functionsV2.https.onCall(
666
611
  true
667
612
  )
668
613
 
669
- // Stop VM instance.
670
- await stopEC2Instance(ec2, vmInstanceId)
671
614
  } else {
672
615
  // Upload verification transcript.
673
616
  /// nb. do not use multi-part upload here due to small file size.
@@ -744,6 +687,9 @@ export const verifycontribution = functionsV2.https.onCall(
744
687
  })
745
688
  }
746
689
 
690
+ // Stop VM instance
691
+ if (isUsingVM) await stopEC2Instance(ec2, vmInstanceId)
692
+
747
693
  // Step (1.A.4.C)
748
694
  if (!isFinalizing) {
749
695
  // Step (1.A.4.C.1)
@@ -763,7 +709,9 @@ export const verifycontribution = functionsV2.https.onCall(
763
709
  ? (avgVerifyCloudFunctionTime + verifyCloudFunctionTime) / 2
764
710
  : verifyCloudFunctionTime
765
711
 
766
- // Prepare tx to update circuit average contribution/verification time.
712
+ // Prepare tx to update circuit average contribution/verification time.
713
+ const updatedCircuitDoc = await getDocumentById(getCircuitsCollectionPath(ceremonyId), circuitId)
714
+ const { waitingQueue: updatedWaitingQueue } = updatedCircuitDoc.data()!
767
715
  /// @dev this must happen only for valid contributions.
768
716
  batch.update(circuitDoc.ref, {
769
717
  avgTimings: {
@@ -776,7 +724,7 @@ export const verifycontribution = functionsV2.https.onCall(
776
724
  : avgVerifyCloudFunctionTime
777
725
  },
778
726
  waitingQueue: {
779
- ...waitingQueue,
727
+ ...updatedWaitingQueue,
780
728
  completedContributions: isContributionValid
781
729
  ? completedContributions + 1
782
730
  : completedContributions,
@@ -879,45 +827,28 @@ export const verifycontribution = functionsV2.https.onCall(
879
827
  await downloadArtifactFromS3Bucket(bucketName, firstZkeyStoragePath, firstZkeyTempFilePath)
880
828
  await downloadArtifactFromS3Bucket(bucketName, lastZkeyStoragePath, lastZkeyTempFilePath)
881
829
 
882
- await sleep(6000)
883
-
884
- // wait until the files are actually downloaded
885
- return new Promise<void>((resolve, reject) =>
886
- waitForFileDownload(resolve, reject, potTempFilePath, firstZkeyTempFilePath, lastZkeyTempFilePath, circuitId, participantDoc.id)
830
+ // Step (1.A.4).
831
+ isContributionValid = await zKey.verifyFromInit(
832
+ firstZkeyTempFilePath,
833
+ potTempFilePath,
834
+ lastZkeyTempFilePath,
835
+ transcriptLogger
887
836
  )
888
- .then(async () => {
889
- printLog(`Downloads from AWS S3 bucket completed - ceremony ${ceremonyId} circuit ${circuitId}`, LogLevel.DEBUG)
890
-
891
- // Step (1.A.4).
892
- isContributionValid = await zKey.verifyFromInit(
893
- firstZkeyTempFilePath,
894
- potTempFilePath,
895
- lastZkeyTempFilePath,
896
- transcriptLogger
897
- )
898
-
899
- // Compute contribution hash.
900
- lastZkeyBlake2bHash = await blake512FromPath(lastZkeyTempFilePath)
901
-
902
- // Free resources by unlinking temporary folders.
903
- // Do not free-up verification transcript path here.
904
- try {
905
- fs.unlinkSync(potTempFilePath)
906
- fs.unlinkSync(firstZkeyTempFilePath)
907
- fs.unlinkSync(lastZkeyTempFilePath)
908
- } catch (error: any) {
909
- printLog(`Error while unlinking temporary files - Error ${error}`, LogLevel.WARN)
910
- }
911
-
912
- await completeVerification()
913
- })
914
- .catch((error: any) => {
915
- // Throw the new error
916
- const commonError = COMMON_ERRORS.CM_INVALID_REQUEST
917
- const additionalDetails = error.toString()
918
-
919
- logAndThrowError(makeError(commonError.code, commonError.message, additionalDetails))
920
- })
837
+
838
+ // Compute contribution hash.
839
+ lastZkeyBlake2bHash = await blake512FromPath(lastZkeyTempFilePath)
840
+
841
+ // Free resources by unlinking temporary folders.
842
+ // Do not free-up verification transcript path here.
843
+ try {
844
+ fs.unlinkSync(potTempFilePath)
845
+ fs.unlinkSync(firstZkeyTempFilePath)
846
+ fs.unlinkSync(lastZkeyTempFilePath)
847
+ } catch (error: any) {
848
+ printLog(`Error while unlinking temporary files - Error ${error}`, LogLevel.WARN)
849
+ }
850
+
851
+ await completeVerification()
921
852
  }
922
853
  }
923
854
  }
@@ -8,7 +8,7 @@ import {
8
8
  ParticipantContributionStep,
9
9
  getParticipantsCollectionPath,
10
10
  commonTerms
11
- } from "@p0tion/actions"
11
+ } from "@devtion/actions"
12
12
  import { FieldValue } from "firebase-admin/firestore"
13
13
  import {
14
14
  PermanentlyStoreCurrentContributionTimeAndHash,
@@ -20,7 +20,7 @@ import {
20
20
  ParticipantContributionStep,
21
21
  formatZkeyIndex,
22
22
  getZkeyStorageFilePath
23
- } from "@p0tion/actions"
23
+ } from "@devtion/actions"
24
24
  import { getCeremonyCircuits, getDocumentById } from "../lib/utils"
25
25
  import { COMMON_ERRORS, logAndThrowError, makeError, printLog, SPECIFIC_ERRORS } from "../lib/errors"
26
26
  import { LogLevel } from "../types/enums"
@@ -9,7 +9,7 @@ import {
9
9
  ParticipantStatus,
10
10
  getTimeoutsCollectionPath,
11
11
  commonTerms
12
- } from "@p0tion/actions"
12
+ } from "@devtion/actions"
13
13
  import {
14
14
  getCeremonyCircuits,
15
15
  getCurrentServerTimestampInMillis,
@@ -174,7 +174,7 @@ export const checkAndRemoveBlockingContributor = functions
174
174
  const batch = firestore.batch()
175
175
 
176
176
  // Remove current contributor from waiting queue.
177
- contributors.shift(1)
177
+ contributors.shift()
178
178
 
179
179
  // Check if someone else is ready to start the contribution.
180
180
  if (contributors.length > 0) {
@@ -281,7 +281,8 @@ export const resumeContributionAfterTimeoutExpiration = functions
281
281
  if (status === ParticipantStatus.EXHUMED)
282
282
  await participantDoc.ref.update({
283
283
  status: ParticipantStatus.READY,
284
- lastUpdated: getCurrentServerTimestampInMillis()
284
+ lastUpdated: getCurrentServerTimestampInMillis(),
285
+ tempContributionData: {}
285
286
  })
286
287
  else logAndThrowError(SPECIFIC_ERRORS.SE_CONTRIBUTE_CANNOT_PROGRESS_TO_NEXT_CIRCUIT)
287
288
 
@@ -2,7 +2,7 @@ import * as functions from "firebase-functions"
2
2
  import { UserRecord } from "firebase-functions/v1/auth"
3
3
  import admin from "firebase-admin"
4
4
  import dotenv from "dotenv"
5
- import { commonTerms, githubReputation } from "@p0tion/actions"
5
+ import { commonTerms, githubReputation } from "@devtion/actions"
6
6
  import { encode } from "html-entities"
7
7
  import { getGitHubVariables, getCurrentServerTimestampInMillis } from "../lib/utils"
8
8
  import { logAndThrowError, makeError, printLog, SPECIFIC_ERRORS } from "../lib/errors"
@@ -40,8 +40,11 @@ export const registerAuthUser = functions
40
40
  const { uid } = user
41
41
  // Reference to a document using uid.
42
42
  const userRef = firestore.collection(commonTerms.collections.users.name).doc(uid)
43
- // html encode the display name
44
- const encodedDisplayName = encode(displayName)
43
+ // html encode the display name (or put the ID if the name is not displayed)
44
+ const encodedDisplayName = user.displayName === "Null" || user.displayName === null ? user.uid : encode(displayName)
45
+
46
+ // store the avatar URL of a contributor
47
+ let avatarUrl: string = ""
45
48
  // we only do reputation check if the user is not a coordinator
46
49
  if (
47
50
  !(
@@ -56,13 +59,13 @@ export const registerAuthUser = functions
56
59
 
57
60
  // this return true or false
58
61
  try {
59
- const res = await githubReputation(
62
+ const { reputable, avatarUrl: avatarURL } = await githubReputation(
60
63
  user.providerData[0].uid,
61
64
  vars.minimumFollowing,
62
65
  vars.minimumFollowers,
63
66
  vars.minimumPublicRepos
64
67
  )
65
- if (!res) {
68
+ if (!reputable) {
66
69
  // Delete user
67
70
  await auth.deleteUser(user.uid)
68
71
  // Throw error
@@ -70,11 +73,13 @@ export const registerAuthUser = functions
70
73
  makeError(
71
74
  "permission-denied",
72
75
  "The user is not allowed to sign up because their Github reputation is not high enough.",
73
- `The user ${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.`
76
+ `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.`
74
77
  )
75
78
  )
76
- }
77
- printLog(`Github reputation check passed for user ${user.displayName}`, LogLevel.DEBUG)
79
+ }
80
+ // store locally
81
+ avatarUrl = avatarURL
82
+ printLog(`Github reputation check passed for user ${user.displayName === "Null" || user.displayName === null ? user.uid : user.displayName }`, LogLevel.DEBUG)
78
83
  } catch (error: any) {
79
84
  // Delete user
80
85
  await auth.deleteUser(user.uid)
@@ -89,6 +94,8 @@ export const registerAuthUser = functions
89
94
  }
90
95
  }
91
96
  // Set document (nb. we refer to providerData[0] because we use Github OAuth provider only).
97
+ // In future releases we might want to loop through the providerData array as we support
98
+ // more providers.
92
99
  await userRef.set({
93
100
  name: encodedDisplayName,
94
101
  encodedDisplayName,
@@ -101,7 +108,14 @@ export const registerAuthUser = functions
101
108
  photoURL: photoURL || "",
102
109
  lastUpdated: getCurrentServerTimestampInMillis()
103
110
  })
111
+
112
+ // we want to create a new collection for the users to store the avatars
113
+ const avatarRef = firestore.collection(commonTerms.collections.avatars.name).doc(uid)
114
+ await avatarRef.set({
115
+ avatarUrl: avatarUrl || "",
116
+ })
104
117
  printLog(`Authenticated user document with identifier ${uid} has been correctly stored`, LogLevel.DEBUG)
118
+ printLog(`Authenticated user avatar with identifier ${uid} has been correctly stored`, LogLevel.DEBUG)
105
119
  })
106
120
  /**
107
121
  * Set custom claims for role-based access control on the newly created user.
package/src/lib/utils.ts CHANGED
@@ -25,7 +25,7 @@ import {
25
25
  CeremonyState,
26
26
  finalContributionIndex,
27
27
  CircuitDocument
28
- } from "@p0tion/actions"
28
+ } from "@devtion/actions"
29
29
  import fetch from "@adobe/node-fetch-retry"
30
30
  import path from "path"
31
31
  import os from "os"
@@ -1,4 +1,4 @@
1
- import { CeremonyInputData, CircuitDocument, ETagWithPartNumber } from "@p0tion/actions"
1
+ import { CeremonyInputData, CircuitDocument, ETagWithPartNumber } from "@devtion/actions"
2
2
 
3
3
  /**
4
4
  * Group all the necessary data needed for running the `setupCeremony` cloud function.