@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 +26 -0
- package/dist/src/functions/index.js +50 -91
- package/dist/src/functions/index.mjs +52 -93
- package/dist/types/functions/ceremony.d.ts.map +1 -1
- package/dist/types/functions/circuit.d.ts.map +1 -1
- package/dist/types/functions/timeout.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 +4 -3
- package/src/functions/circuit.ts +30 -99
- package/src/functions/participant.ts +1 -1
- package/src/functions/storage.ts +1 -1
- package/src/functions/timeout.ts +4 -3
- package/src/functions/user.ts +22 -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.
|
|
@@ -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
|
-
|
|
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
|
-
...
|
|
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
|
-
|
|
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
|
-
});
|
|
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(
|
|
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 @
|
|
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.
|
|
@@ -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
|
-
|
|
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
|
-
...
|
|
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
|
-
|
|
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
|
-
});
|
|
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(
|
|
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,
|
|
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;
|
|
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,
|
|
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,
|
|
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 "@
|
|
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-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
|
-
"@
|
|
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": "df754397c15a7086a163688fb4d3f4e7c98ad379"
|
|
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"
|
|
@@ -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
|
-
|
|
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!)
|
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.
|
|
@@ -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
|
-
...
|
|
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
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
830
|
+
// Step (1.A.4).
|
|
831
|
+
isContributionValid = await zKey.verifyFromInit(
|
|
832
|
+
firstZkeyTempFilePath,
|
|
833
|
+
potTempFilePath,
|
|
834
|
+
lastZkeyTempFilePath,
|
|
835
|
+
transcriptLogger
|
|
887
836
|
)
|
|
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
|
-
})
|
|
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 "@
|
|
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) {
|
|
@@ -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
|
|
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,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
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.
|