@devtion/backend 0.0.0-7e983e3 → 0.0.0-9614e0c
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 +26 -0
- package/dist/src/functions/index.js +42 -86
- package/dist/src/functions/index.mjs +44 -88
- package/dist/types/functions/circuit.d.ts.map +1 -1
- package/dist/types/functions/user.d.ts.map +1 -1
- package/dist/types/lib/utils.d.ts +1 -1
- package/dist/types/lib/utils.d.ts.map +1 -1
- package/dist/types/types/index.d.ts +1 -1
- package/dist/types/types/index.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/functions/ceremony.ts +1 -1
- package/src/functions/circuit.ts +27 -97
- package/src/functions/participant.ts +1 -1
- package/src/functions/storage.ts +1 -1
- package/src/functions/timeout.ts +2 -2
- package/src/functions/user.ts +23 -8
- package/src/lib/utils.ts +1 -1
- package/src/types/index.ts +1 -1
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 @
|
|
3
|
-
* @version 1.0.
|
|
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('@
|
|
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
|
|
559
|
-
if (!
|
|
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
|
-
|
|
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.
|
|
@@ -1292,6 +1304,7 @@ const coordinate = async (participant, circuit, isSingleParticipantCoordination,
|
|
|
1292
1304
|
printLog(`Coordinate - executing scenario A - single - participantResumingAfterTimeoutExpiration`, LogLevel.DEBUG);
|
|
1293
1305
|
newParticipantStatus = "CONTRIBUTING" /* ParticipantStatus.CONTRIBUTING */;
|
|
1294
1306
|
newContributionStep = "DOWNLOADING" /* ParticipantContributionStep.DOWNLOADING */;
|
|
1307
|
+
newCurrentContributorId = participant.id;
|
|
1295
1308
|
}
|
|
1296
1309
|
// Scenario (B).
|
|
1297
1310
|
else if (participantIsNotCurrentContributor) {
|
|
@@ -1400,53 +1413,6 @@ const waitForVMCommandExecution = (resolve, reject, ssm, vmInstanceId, commandId
|
|
|
1400
1413
|
}
|
|
1401
1414
|
}, 60000); // 1 minute.
|
|
1402
1415
|
};
|
|
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
1416
|
/**
|
|
1451
1417
|
* This method is used to coordinate the waiting queues of ceremony circuits.
|
|
1452
1418
|
* @dev this cloud function is triggered whenever an update of a document related to a participant of a ceremony occurs.
|
|
@@ -1757,7 +1723,9 @@ const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB"
|
|
|
1757
1723
|
const newAvgVerifyCloudFunctionTime = avgVerifyCloudFunctionTime > 0
|
|
1758
1724
|
? (avgVerifyCloudFunctionTime + verifyCloudFunctionTime) / 2
|
|
1759
1725
|
: verifyCloudFunctionTime;
|
|
1760
|
-
// Prepare tx to update circuit average contribution/verification time.
|
|
1726
|
+
// Prepare tx to update circuit average contribution/verification time.
|
|
1727
|
+
const updatedCircuitDoc = await getDocumentById(actions.getCircuitsCollectionPath(ceremonyId), circuitId);
|
|
1728
|
+
const { waitingQueue: updatedWaitingQueue } = updatedCircuitDoc.data();
|
|
1761
1729
|
/// @dev this must happen only for valid contributions.
|
|
1762
1730
|
batch.update(circuitDoc.ref, {
|
|
1763
1731
|
avgTimings: {
|
|
@@ -1770,7 +1738,7 @@ const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB"
|
|
|
1770
1738
|
: avgVerifyCloudFunctionTime
|
|
1771
1739
|
},
|
|
1772
1740
|
waitingQueue: {
|
|
1773
|
-
...
|
|
1741
|
+
...updatedWaitingQueue,
|
|
1774
1742
|
completedContributions: isContributionValid
|
|
1775
1743
|
? completedContributions + 1
|
|
1776
1744
|
: completedContributions,
|
|
@@ -1835,33 +1803,21 @@ const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB"
|
|
|
1835
1803
|
await downloadArtifactFromS3Bucket(bucketName, potStoragePath, potTempFilePath);
|
|
1836
1804
|
await downloadArtifactFromS3Bucket(bucketName, firstZkeyStoragePath, firstZkeyTempFilePath);
|
|
1837
1805
|
await downloadArtifactFromS3Bucket(bucketName, lastZkeyStoragePath, lastZkeyTempFilePath);
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
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
|
-
});
|
|
1806
|
+
// Step (1.A.4).
|
|
1807
|
+
isContributionValid = await snarkjs.zKey.verifyFromInit(firstZkeyTempFilePath, potTempFilePath, lastZkeyTempFilePath, transcriptLogger);
|
|
1808
|
+
// Compute contribution hash.
|
|
1809
|
+
lastZkeyBlake2bHash = await actions.blake512FromPath(lastZkeyTempFilePath);
|
|
1810
|
+
// Free resources by unlinking temporary folders.
|
|
1811
|
+
// Do not free-up verification transcript path here.
|
|
1812
|
+
try {
|
|
1813
|
+
fs.unlinkSync(potTempFilePath);
|
|
1814
|
+
fs.unlinkSync(firstZkeyTempFilePath);
|
|
1815
|
+
fs.unlinkSync(lastZkeyTempFilePath);
|
|
1816
|
+
}
|
|
1817
|
+
catch (error) {
|
|
1818
|
+
printLog(`Error while unlinking temporary files - Error ${error}`, LogLevel.WARN);
|
|
1819
|
+
}
|
|
1820
|
+
await completeVerification();
|
|
1865
1821
|
}
|
|
1866
1822
|
}
|
|
1867
1823
|
});
|
|
@@ -2523,7 +2479,7 @@ const checkAndRemoveBlockingContributor = functions__namespace
|
|
|
2523
2479
|
// Prepare Firestore batch of txs.
|
|
2524
2480
|
const batch = firestore.batch();
|
|
2525
2481
|
// Remove current contributor from waiting queue.
|
|
2526
|
-
contributors.shift(
|
|
2482
|
+
contributors.shift();
|
|
2527
2483
|
// Check if someone else is ready to start the contribution.
|
|
2528
2484
|
if (contributors.length > 0) {
|
|
2529
2485
|
// Step (E.1).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @module @
|
|
3
|
-
* @version 1.0.
|
|
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 '@
|
|
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
|
|
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
|
|
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
|
|
536
|
-
if (!
|
|
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
|
-
|
|
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.
|
|
@@ -1269,6 +1281,7 @@ const coordinate = async (participant, circuit, isSingleParticipantCoordination,
|
|
|
1269
1281
|
printLog(`Coordinate - executing scenario A - single - participantResumingAfterTimeoutExpiration`, LogLevel.DEBUG);
|
|
1270
1282
|
newParticipantStatus = "CONTRIBUTING" /* ParticipantStatus.CONTRIBUTING */;
|
|
1271
1283
|
newContributionStep = "DOWNLOADING" /* ParticipantContributionStep.DOWNLOADING */;
|
|
1284
|
+
newCurrentContributorId = participant.id;
|
|
1272
1285
|
}
|
|
1273
1286
|
// Scenario (B).
|
|
1274
1287
|
else if (participantIsNotCurrentContributor) {
|
|
@@ -1377,53 +1390,6 @@ const waitForVMCommandExecution = (resolve, reject, ssm, vmInstanceId, commandId
|
|
|
1377
1390
|
}
|
|
1378
1391
|
}, 60000); // 1 minute.
|
|
1379
1392
|
};
|
|
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
1393
|
/**
|
|
1428
1394
|
* This method is used to coordinate the waiting queues of ceremony circuits.
|
|
1429
1395
|
* @dev this cloud function is triggered whenever an update of a document related to a participant of a ceremony occurs.
|
|
@@ -1734,7 +1700,9 @@ const verifycontribution = functionsV2.https.onCall({ memory: "16GiB", timeoutSe
|
|
|
1734
1700
|
const newAvgVerifyCloudFunctionTime = avgVerifyCloudFunctionTime > 0
|
|
1735
1701
|
? (avgVerifyCloudFunctionTime + verifyCloudFunctionTime) / 2
|
|
1736
1702
|
: verifyCloudFunctionTime;
|
|
1737
|
-
// Prepare tx to update circuit average contribution/verification time.
|
|
1703
|
+
// Prepare tx to update circuit average contribution/verification time.
|
|
1704
|
+
const updatedCircuitDoc = await getDocumentById(getCircuitsCollectionPath(ceremonyId), circuitId);
|
|
1705
|
+
const { waitingQueue: updatedWaitingQueue } = updatedCircuitDoc.data();
|
|
1738
1706
|
/// @dev this must happen only for valid contributions.
|
|
1739
1707
|
batch.update(circuitDoc.ref, {
|
|
1740
1708
|
avgTimings: {
|
|
@@ -1747,7 +1715,7 @@ const verifycontribution = functionsV2.https.onCall({ memory: "16GiB", timeoutSe
|
|
|
1747
1715
|
: avgVerifyCloudFunctionTime
|
|
1748
1716
|
},
|
|
1749
1717
|
waitingQueue: {
|
|
1750
|
-
...
|
|
1718
|
+
...updatedWaitingQueue,
|
|
1751
1719
|
completedContributions: isContributionValid
|
|
1752
1720
|
? completedContributions + 1
|
|
1753
1721
|
: completedContributions,
|
|
@@ -1812,33 +1780,21 @@ const verifycontribution = functionsV2.https.onCall({ memory: "16GiB", timeoutSe
|
|
|
1812
1780
|
await downloadArtifactFromS3Bucket(bucketName, potStoragePath, potTempFilePath);
|
|
1813
1781
|
await downloadArtifactFromS3Bucket(bucketName, firstZkeyStoragePath, firstZkeyTempFilePath);
|
|
1814
1782
|
await downloadArtifactFromS3Bucket(bucketName, lastZkeyStoragePath, lastZkeyTempFilePath);
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
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
|
-
});
|
|
1783
|
+
// Step (1.A.4).
|
|
1784
|
+
isContributionValid = await zKey.verifyFromInit(firstZkeyTempFilePath, potTempFilePath, lastZkeyTempFilePath, transcriptLogger);
|
|
1785
|
+
// Compute contribution hash.
|
|
1786
|
+
lastZkeyBlake2bHash = await blake512FromPath(lastZkeyTempFilePath);
|
|
1787
|
+
// Free resources by unlinking temporary folders.
|
|
1788
|
+
// Do not free-up verification transcript path here.
|
|
1789
|
+
try {
|
|
1790
|
+
fs.unlinkSync(potTempFilePath);
|
|
1791
|
+
fs.unlinkSync(firstZkeyTempFilePath);
|
|
1792
|
+
fs.unlinkSync(lastZkeyTempFilePath);
|
|
1793
|
+
}
|
|
1794
|
+
catch (error) {
|
|
1795
|
+
printLog(`Error while unlinking temporary files - Error ${error}`, LogLevel.WARN);
|
|
1796
|
+
}
|
|
1797
|
+
await completeVerification();
|
|
1842
1798
|
}
|
|
1843
1799
|
}
|
|
1844
1800
|
});
|
|
@@ -2500,7 +2456,7 @@ const checkAndRemoveBlockingContributor = functions
|
|
|
2500
2456
|
// Prepare Firestore batch of txs.
|
|
2501
2457
|
const batch = firestore.batch();
|
|
2502
2458
|
// Remove current contributor from waiting queue.
|
|
2503
|
-
contributors.shift(
|
|
2459
|
+
contributors.shift();
|
|
2504
2460
|
// Check if someone else is ready to start the contribution.
|
|
2505
2461
|
if (contributors.length > 0) {
|
|
2506
2462
|
// Step (E.1).
|
|
@@ -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;
|
|
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,0EA0Z9B,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,+CAA+C,wEA4EtD,CAAA;AAEN;;;;GAIG;AACH,eAAO,MAAM,eAAe,uDA8EtB,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,
|
|
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,mEAsGvB,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 "@
|
|
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,
|
|
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 "@
|
|
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,
|
|
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-
|
|
3
|
+
"version": "0.0.0-9614e0c",
|
|
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
|
-
"@
|
|
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": "
|
|
88
|
+
"gitHead": "61b8937938f16a4a92a0468a0ba7ec8306188a60"
|
|
89
89
|
}
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
vmBootstrapCommand,
|
|
19
19
|
vmDependenciesAndCacheArtifactsCommand,
|
|
20
20
|
vmBootstrapScriptFilename
|
|
21
|
-
} from "@
|
|
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"
|
package/src/functions/circuit.ts
CHANGED
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
createCustomLoggerForFile,
|
|
38
38
|
retrieveCommandStatus,
|
|
39
39
|
stopEC2Instance
|
|
40
|
-
} from "@
|
|
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.
|
|
@@ -763,7 +708,9 @@ export const verifycontribution = functionsV2.https.onCall(
|
|
|
763
708
|
? (avgVerifyCloudFunctionTime + verifyCloudFunctionTime) / 2
|
|
764
709
|
: verifyCloudFunctionTime
|
|
765
710
|
|
|
766
|
-
// Prepare tx to update circuit average contribution/verification time.
|
|
711
|
+
// Prepare tx to update circuit average contribution/verification time.
|
|
712
|
+
const updatedCircuitDoc = await getDocumentById(getCircuitsCollectionPath(ceremonyId), circuitId)
|
|
713
|
+
const { waitingQueue: updatedWaitingQueue } = updatedCircuitDoc.data()!
|
|
767
714
|
/// @dev this must happen only for valid contributions.
|
|
768
715
|
batch.update(circuitDoc.ref, {
|
|
769
716
|
avgTimings: {
|
|
@@ -776,7 +723,7 @@ export const verifycontribution = functionsV2.https.onCall(
|
|
|
776
723
|
: avgVerifyCloudFunctionTime
|
|
777
724
|
},
|
|
778
725
|
waitingQueue: {
|
|
779
|
-
...
|
|
726
|
+
...updatedWaitingQueue,
|
|
780
727
|
completedContributions: isContributionValid
|
|
781
728
|
? completedContributions + 1
|
|
782
729
|
: completedContributions,
|
|
@@ -879,45 +826,28 @@ export const verifycontribution = functionsV2.https.onCall(
|
|
|
879
826
|
await downloadArtifactFromS3Bucket(bucketName, firstZkeyStoragePath, firstZkeyTempFilePath)
|
|
880
827
|
await downloadArtifactFromS3Bucket(bucketName, lastZkeyStoragePath, lastZkeyTempFilePath)
|
|
881
828
|
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
829
|
+
// Step (1.A.4).
|
|
830
|
+
isContributionValid = await zKey.verifyFromInit(
|
|
831
|
+
firstZkeyTempFilePath,
|
|
832
|
+
potTempFilePath,
|
|
833
|
+
lastZkeyTempFilePath,
|
|
834
|
+
transcriptLogger
|
|
887
835
|
)
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
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
|
-
})
|
|
836
|
+
|
|
837
|
+
// Compute contribution hash.
|
|
838
|
+
lastZkeyBlake2bHash = await blake512FromPath(lastZkeyTempFilePath)
|
|
839
|
+
|
|
840
|
+
// Free resources by unlinking temporary folders.
|
|
841
|
+
// Do not free-up verification transcript path here.
|
|
842
|
+
try {
|
|
843
|
+
fs.unlinkSync(potTempFilePath)
|
|
844
|
+
fs.unlinkSync(firstZkeyTempFilePath)
|
|
845
|
+
fs.unlinkSync(lastZkeyTempFilePath)
|
|
846
|
+
} catch (error: any) {
|
|
847
|
+
printLog(`Error while unlinking temporary files - Error ${error}`, LogLevel.WARN)
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
await completeVerification()
|
|
921
851
|
}
|
|
922
852
|
}
|
|
923
853
|
}
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
ParticipantContributionStep,
|
|
9
9
|
getParticipantsCollectionPath,
|
|
10
10
|
commonTerms
|
|
11
|
-
} from "@
|
|
11
|
+
} from "@devtion/actions"
|
|
12
12
|
import { FieldValue } from "firebase-admin/firestore"
|
|
13
13
|
import {
|
|
14
14
|
PermanentlyStoreCurrentContributionTimeAndHash,
|
package/src/functions/storage.ts
CHANGED
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
ParticipantContributionStep,
|
|
21
21
|
formatZkeyIndex,
|
|
22
22
|
getZkeyStorageFilePath
|
|
23
|
-
} from "@
|
|
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"
|
package/src/functions/timeout.ts
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
ParticipantStatus,
|
|
10
10
|
getTimeoutsCollectionPath,
|
|
11
11
|
commonTerms
|
|
12
|
-
} from "@
|
|
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(
|
|
177
|
+
contributors.shift()
|
|
178
178
|
|
|
179
179
|
// Check if someone else is ready to start the contribution.
|
|
180
180
|
if (contributors.length > 0) {
|
package/src/functions/user.ts
CHANGED
|
@@ -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 "@
|
|
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
|
|
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 (!
|
|
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
|
-
|
|
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,15 @@ 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
|
+
})
|
|
117
|
+
|
|
104
118
|
printLog(`Authenticated user document with identifier ${uid} has been correctly stored`, LogLevel.DEBUG)
|
|
119
|
+
printLog(`Authenticated user avatar with identifier ${uid} has been correctly stored`, LogLevel.DEBUG)
|
|
105
120
|
})
|
|
106
121
|
/**
|
|
107
122
|
* Set custom claims for role-based access control on the newly created user.
|
package/src/lib/utils.ts
CHANGED
package/src/types/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CeremonyInputData, CircuitDocument, ETagWithPartNumber } from "@
|
|
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.
|