@devtion/backend 0.0.0-5d170d3 → 0.0.0-7cfaa5d
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 +127 -153
- package/dist/src/functions/index.mjs +127 -153
- package/dist/types/functions/ceremony.d.ts.map +1 -1
- package/dist/types/functions/circuit.d.ts.map +1 -1
- package/dist/types/functions/storage.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/errors.d.ts +1 -0
- package/dist/types/lib/errors.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 +8 -3
- package/src/functions/circuit.ts +117 -181
- package/src/functions/participant.ts +8 -8
- package/src/functions/storage.ts +5 -3
- package/src/functions/timeout.ts +4 -3
- package/src/functions/user.ts +31 -7
- package/src/lib/errors.ts +5 -0
- package/src/lib/utils.ts +3 -3
- 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
|
+
5. 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.8
|
|
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');
|
|
@@ -144,7 +144,8 @@ const SPECIFIC_ERRORS = {
|
|
|
144
144
|
SE_VM_FAILED_COMMAND_EXECUTION: makeError("failed-precondition", "VM command execution failed", "Please, contact the coordinator if this error persists."),
|
|
145
145
|
SE_VM_TIMEDOUT_COMMAND_EXECUTION: makeError("deadline-exceeded", "VM command execution took too long and has been timed-out", "Please, contact the coordinator if this error persists."),
|
|
146
146
|
SE_VM_CANCELLED_COMMAND_EXECUTION: makeError("cancelled", "VM command execution has been cancelled", "Please, contact the coordinator if this error persists."),
|
|
147
|
-
SE_VM_DELAYED_COMMAND_EXECUTION: makeError("unavailable", "VM command execution has been delayed since there were no available instance at the moment", "Please, contact the coordinator if this error persists.")
|
|
147
|
+
SE_VM_DELAYED_COMMAND_EXECUTION: makeError("unavailable", "VM command execution has been delayed since there were no available instance at the moment", "Please, contact the coordinator if this error persists."),
|
|
148
|
+
SE_VM_UNKNOWN_COMMAND_STATUS: makeError("unavailable", "VM command execution has failed due to an unknown status code", "Please, contact the coordinator if this error persists.")
|
|
148
149
|
};
|
|
149
150
|
/**
|
|
150
151
|
* A set of common errors.
|
|
@@ -328,7 +329,7 @@ const downloadArtifactFromS3Bucket = async (bucketName, objectKey, localFilePath
|
|
|
328
329
|
const writeStream = node_fs.createWriteStream(localFilePath);
|
|
329
330
|
const streamPipeline = node_util.promisify(node_stream.pipeline);
|
|
330
331
|
await streamPipeline(response.body, writeStream);
|
|
331
|
-
writeStream.on(
|
|
332
|
+
writeStream.on("finish", () => {
|
|
332
333
|
writeStream.end();
|
|
333
334
|
});
|
|
334
335
|
};
|
|
@@ -544,8 +545,10 @@ const registerAuthUser = functions__namespace
|
|
|
544
545
|
const { uid } = user;
|
|
545
546
|
// Reference to a document using uid.
|
|
546
547
|
const userRef = firestore.collection(actions.commonTerms.collections.users.name).doc(uid);
|
|
547
|
-
// html encode the display name
|
|
548
|
-
const encodedDisplayName = htmlEntities.encode(displayName);
|
|
548
|
+
// html encode the display name (or put the ID if the name is not displayed)
|
|
549
|
+
const encodedDisplayName = user.displayName === "Null" || user.displayName === null ? user.uid : htmlEntities.encode(displayName);
|
|
550
|
+
// store the avatar URL of a contributor
|
|
551
|
+
let avatarUrl = "";
|
|
549
552
|
// we only do reputation check if the user is not a coordinator
|
|
550
553
|
if (!(email?.endsWith(`@${process.env.CUSTOM_CLAIMS_COORDINATOR_EMAIL_ADDRESS_OR_DOMAIN}`) ||
|
|
551
554
|
email === process.env.CUSTOM_CLAIMS_COORDINATOR_EMAIL_ADDRESS_OR_DOMAIN)) {
|
|
@@ -555,14 +558,18 @@ const registerAuthUser = functions__namespace
|
|
|
555
558
|
const vars = getGitHubVariables();
|
|
556
559
|
// this return true or false
|
|
557
560
|
try {
|
|
558
|
-
const
|
|
559
|
-
if (!
|
|
561
|
+
const { reputable, avatarUrl: avatarURL } = await actions.githubReputation(user.providerData[0].uid, vars.minimumFollowing, vars.minimumFollowers, vars.minimumPublicRepos);
|
|
562
|
+
if (!reputable) {
|
|
560
563
|
// Delete user
|
|
561
564
|
await auth.deleteUser(user.uid);
|
|
562
565
|
// 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
|
|
566
|
+
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
|
|
567
|
+
? user.uid
|
|
568
|
+
: 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
569
|
}
|
|
565
|
-
|
|
570
|
+
// store locally
|
|
571
|
+
avatarUrl = avatarURL;
|
|
572
|
+
printLog(`Github reputation check passed for user ${user.displayName === "Null" || user.displayName === null ? user.uid : user.displayName}`, LogLevel.DEBUG);
|
|
566
573
|
}
|
|
567
574
|
catch (error) {
|
|
568
575
|
// Delete user
|
|
@@ -572,6 +579,8 @@ const registerAuthUser = functions__namespace
|
|
|
572
579
|
}
|
|
573
580
|
}
|
|
574
581
|
// Set document (nb. we refer to providerData[0] because we use Github OAuth provider only).
|
|
582
|
+
// In future releases we might want to loop through the providerData array as we support
|
|
583
|
+
// more providers.
|
|
575
584
|
await userRef.set({
|
|
576
585
|
name: encodedDisplayName,
|
|
577
586
|
encodedDisplayName,
|
|
@@ -584,7 +593,13 @@ const registerAuthUser = functions__namespace
|
|
|
584
593
|
photoURL: photoURL || "",
|
|
585
594
|
lastUpdated: getCurrentServerTimestampInMillis()
|
|
586
595
|
});
|
|
596
|
+
// we want to create a new collection for the users to store the avatars
|
|
597
|
+
const avatarRef = firestore.collection(actions.commonTerms.collections.avatars.name).doc(uid);
|
|
598
|
+
await avatarRef.set({
|
|
599
|
+
avatarUrl: avatarUrl || ""
|
|
600
|
+
});
|
|
587
601
|
printLog(`Authenticated user document with identifier ${uid} has been correctly stored`, LogLevel.DEBUG);
|
|
602
|
+
printLog(`Authenticated user avatar with identifier ${uid} has been correctly stored`, LogLevel.DEBUG);
|
|
588
603
|
});
|
|
589
604
|
/**
|
|
590
605
|
* Set custom claims for role-based access control on the newly created user.
|
|
@@ -721,7 +736,7 @@ const setupCeremony = functions__namespace
|
|
|
721
736
|
// Check if using the VM approach for contribution verification.
|
|
722
737
|
if (circuit.verification.cfOrVm === "VM" /* CircuitContributionVerificationMechanism.VM */) {
|
|
723
738
|
// VM command to be run at the startup.
|
|
724
|
-
const startupCommand = actions.vmBootstrapCommand(bucketName);
|
|
739
|
+
const startupCommand = actions.vmBootstrapCommand(`${bucketName}/circuits/${circuit.name}`);
|
|
725
740
|
// Get EC2 client.
|
|
726
741
|
const ec2Client = await createEC2Client();
|
|
727
742
|
// Get AWS variables.
|
|
@@ -730,7 +745,8 @@ const setupCeremony = functions__namespace
|
|
|
730
745
|
const vmCommands = actions.vmDependenciesAndCacheArtifactsCommand(`${bucketName}/${circuit.files?.initialZkeyStoragePath}`, `${bucketName}/${circuit.files?.potStoragePath}`, snsTopic, region);
|
|
731
746
|
printLog(`Check VM dependencies and cache artifacts commands ${vmCommands.join("\n")}`, LogLevel.DEBUG);
|
|
732
747
|
// Upload the post-startup commands script file.
|
|
733
|
-
|
|
748
|
+
printLog(`Uploading VM post-startup commands script file ${actions.vmBootstrapScriptFilename}`, LogLevel.DEBUG);
|
|
749
|
+
await uploadFileToBucketNoFile(bucketName, `circuits/${circuit.name}/${actions.vmBootstrapScriptFilename}`, vmCommands.join("\n"));
|
|
734
750
|
// Compute the VM disk space requirement (in GB).
|
|
735
751
|
const vmDiskSize = actions.computeDiskSizeForVM(circuit.zKeySizeInBytes, circuit.metadata?.pot);
|
|
736
752
|
printLog(`Check VM startup commands ${startupCommand.join("\n")}`, LogLevel.DEBUG);
|
|
@@ -877,7 +893,7 @@ dotenv.config();
|
|
|
877
893
|
* @dev true when the participant can participate (1.A, 3.B, 1.D); otherwise false.
|
|
878
894
|
*/
|
|
879
895
|
const checkParticipantForCeremony = functions__namespace
|
|
880
|
-
.region(
|
|
896
|
+
.region("europe-west1")
|
|
881
897
|
.runWith({
|
|
882
898
|
memory: "512MB"
|
|
883
899
|
})
|
|
@@ -981,7 +997,7 @@ const checkParticipantForCeremony = functions__namespace
|
|
|
981
997
|
* 2) the participant has just finished the contribution for a circuit (contributionProgress != 0 && status = CONTRIBUTED && contributionStep = COMPLETED).
|
|
982
998
|
*/
|
|
983
999
|
const progressToNextCircuitForContribution = functions__namespace
|
|
984
|
-
.region(
|
|
1000
|
+
.region("europe-west1")
|
|
985
1001
|
.runWith({
|
|
986
1002
|
memory: "512MB"
|
|
987
1003
|
})
|
|
@@ -1028,7 +1044,7 @@ const progressToNextCircuitForContribution = functions__namespace
|
|
|
1028
1044
|
* 5) Completed contribution computation and verification.
|
|
1029
1045
|
*/
|
|
1030
1046
|
const progressToNextContributionStep = functions__namespace
|
|
1031
|
-
.region(
|
|
1047
|
+
.region("europe-west1")
|
|
1032
1048
|
.runWith({
|
|
1033
1049
|
memory: "512MB"
|
|
1034
1050
|
})
|
|
@@ -1079,7 +1095,7 @@ const progressToNextContributionStep = functions__namespace
|
|
|
1079
1095
|
* @dev enable the current contributor to resume a contribution from where it had left off.
|
|
1080
1096
|
*/
|
|
1081
1097
|
const permanentlyStoreCurrentContributionTimeAndHash = functions__namespace
|
|
1082
|
-
.region(
|
|
1098
|
+
.region("europe-west1")
|
|
1083
1099
|
.runWith({
|
|
1084
1100
|
memory: "512MB"
|
|
1085
1101
|
})
|
|
@@ -1121,7 +1137,7 @@ const permanentlyStoreCurrentContributionTimeAndHash = functions__namespace
|
|
|
1121
1137
|
* @dev enable the current contributor to resume a multi-part upload from where it had left off.
|
|
1122
1138
|
*/
|
|
1123
1139
|
const temporaryStoreCurrentContributionMultiPartUploadId = functions__namespace
|
|
1124
|
-
.region(
|
|
1140
|
+
.region("europe-west1")
|
|
1125
1141
|
.runWith({
|
|
1126
1142
|
memory: "512MB"
|
|
1127
1143
|
})
|
|
@@ -1159,7 +1175,7 @@ const temporaryStoreCurrentContributionMultiPartUploadId = functions__namespace
|
|
|
1159
1175
|
* @dev enable the current contributor to resume a multi-part upload from where it had left off.
|
|
1160
1176
|
*/
|
|
1161
1177
|
const temporaryStoreCurrentContributionUploadedChunkData = functions__namespace
|
|
1162
|
-
.region(
|
|
1178
|
+
.region("europe-west1")
|
|
1163
1179
|
.runWith({
|
|
1164
1180
|
memory: "512MB"
|
|
1165
1181
|
})
|
|
@@ -1201,7 +1217,7 @@ const temporaryStoreCurrentContributionUploadedChunkData = functions__namespace
|
|
|
1201
1217
|
* contributed to every selected ceremony circuits (= DONE).
|
|
1202
1218
|
*/
|
|
1203
1219
|
const checkAndPrepareCoordinatorForFinalization = functions__namespace
|
|
1204
|
-
.region(
|
|
1220
|
+
.region("europe-west1")
|
|
1205
1221
|
.runWith({
|
|
1206
1222
|
memory: "512MB"
|
|
1207
1223
|
})
|
|
@@ -1292,6 +1308,7 @@ const coordinate = async (participant, circuit, isSingleParticipantCoordination,
|
|
|
1292
1308
|
printLog(`Coordinate - executing scenario A - single - participantResumingAfterTimeoutExpiration`, LogLevel.DEBUG);
|
|
1293
1309
|
newParticipantStatus = "CONTRIBUTING" /* ParticipantStatus.CONTRIBUTING */;
|
|
1294
1310
|
newContributionStep = "DOWNLOADING" /* ParticipantContributionStep.DOWNLOADING */;
|
|
1311
|
+
newCurrentContributorId = participant.id;
|
|
1295
1312
|
}
|
|
1296
1313
|
// Scenario (B).
|
|
1297
1314
|
else if (participantIsNotCurrentContributor) {
|
|
@@ -1352,39 +1369,54 @@ const coordinate = async (participant, circuit, isSingleParticipantCoordination,
|
|
|
1352
1369
|
* Wait until the command has completed its execution inside the VM.
|
|
1353
1370
|
* @dev this method implements a custom interval to check 5 times after 1 minute if the command execution
|
|
1354
1371
|
* has been completed or not by calling the `retrieveCommandStatus` method.
|
|
1355
|
-
* @param {any} resolve the promise.
|
|
1356
|
-
* @param {any} reject the promise.
|
|
1357
1372
|
* @param {SSMClient} ssm the SSM client.
|
|
1358
1373
|
* @param {string} vmInstanceId the unique identifier of the VM instance.
|
|
1359
1374
|
* @param {string} commandId the unique identifier of the VM command.
|
|
1360
1375
|
* @returns <Promise<void>> true when the command execution succeed; otherwise false.
|
|
1361
1376
|
*/
|
|
1362
|
-
const waitForVMCommandExecution = (
|
|
1363
|
-
const
|
|
1377
|
+
const waitForVMCommandExecution = (ssm, vmInstanceId, commandId) => new Promise((resolve, reject) => {
|
|
1378
|
+
const poll = async () => {
|
|
1364
1379
|
try {
|
|
1365
1380
|
// Get command status.
|
|
1366
1381
|
const cmdStatus = await actions.retrieveCommandStatus(ssm, vmInstanceId, commandId);
|
|
1367
1382
|
printLog(`Checking command ${commandId} status => ${cmdStatus}`, LogLevel.DEBUG);
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1383
|
+
let error;
|
|
1384
|
+
switch (cmdStatus) {
|
|
1385
|
+
case clientSsm.CommandInvocationStatus.CANCELLING:
|
|
1386
|
+
case clientSsm.CommandInvocationStatus.CANCELLED: {
|
|
1387
|
+
error = SPECIFIC_ERRORS.SE_VM_CANCELLED_COMMAND_EXECUTION;
|
|
1388
|
+
break;
|
|
1389
|
+
}
|
|
1390
|
+
case clientSsm.CommandInvocationStatus.DELAYED: {
|
|
1391
|
+
error = SPECIFIC_ERRORS.SE_VM_DELAYED_COMMAND_EXECUTION;
|
|
1392
|
+
break;
|
|
1393
|
+
}
|
|
1394
|
+
case clientSsm.CommandInvocationStatus.FAILED: {
|
|
1395
|
+
error = SPECIFIC_ERRORS.SE_VM_FAILED_COMMAND_EXECUTION;
|
|
1396
|
+
break;
|
|
1397
|
+
}
|
|
1398
|
+
case clientSsm.CommandInvocationStatus.TIMED_OUT: {
|
|
1399
|
+
error = SPECIFIC_ERRORS.SE_VM_TIMEDOUT_COMMAND_EXECUTION;
|
|
1400
|
+
break;
|
|
1401
|
+
}
|
|
1402
|
+
case clientSsm.CommandInvocationStatus.IN_PROGRESS:
|
|
1403
|
+
case clientSsm.CommandInvocationStatus.PENDING: {
|
|
1404
|
+
// wait a minute and poll again
|
|
1405
|
+
setTimeout(poll, 60000);
|
|
1406
|
+
return;
|
|
1407
|
+
}
|
|
1408
|
+
case clientSsm.CommandInvocationStatus.SUCCESS: {
|
|
1409
|
+
printLog(`Command ${commandId} successfully completed`, LogLevel.DEBUG);
|
|
1410
|
+
// Resolve the promise.
|
|
1411
|
+
resolve();
|
|
1412
|
+
return;
|
|
1413
|
+
}
|
|
1414
|
+
default: {
|
|
1415
|
+
logAndThrowError(SPECIFIC_ERRORS.SE_VM_UNKNOWN_COMMAND_STATUS);
|
|
1416
|
+
}
|
|
1384
1417
|
}
|
|
1385
|
-
|
|
1386
|
-
logAndThrowError(
|
|
1387
|
-
reject();
|
|
1418
|
+
if (error) {
|
|
1419
|
+
logAndThrowError(error);
|
|
1388
1420
|
}
|
|
1389
1421
|
}
|
|
1390
1422
|
catch (error) {
|
|
@@ -1394,59 +1426,9 @@ const waitForVMCommandExecution = (resolve, reject, ssm, vmInstanceId, commandId
|
|
|
1394
1426
|
// Reject the promise.
|
|
1395
1427
|
reject();
|
|
1396
1428
|
}
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
}
|
|
1401
|
-
}, 60000); // 1 minute.
|
|
1402
|
-
};
|
|
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
|
-
};
|
|
1429
|
+
};
|
|
1430
|
+
setTimeout(poll, 60000);
|
|
1431
|
+
});
|
|
1450
1432
|
/**
|
|
1451
1433
|
* This method is used to coordinate the waiting queues of ceremony circuits.
|
|
1452
1434
|
* @dev this cloud function is triggered whenever an update of a document related to a participant of a ceremony occurs.
|
|
@@ -1467,7 +1449,7 @@ const waitForFileDownload = (resolve, reject, potTempFilePath, firstZkeyTempFile
|
|
|
1467
1449
|
* - Just completed a contribution or all contributions for each circuit. If yes, coordinate (multi-participant scenario).
|
|
1468
1450
|
*/
|
|
1469
1451
|
const coordinateCeremonyParticipant = functionsV1__namespace
|
|
1470
|
-
.region(
|
|
1452
|
+
.region("europe-west1")
|
|
1471
1453
|
.runWith({
|
|
1472
1454
|
memory: "512MB"
|
|
1473
1455
|
})
|
|
@@ -1570,7 +1552,7 @@ const checkIfVMRunning = async (ec2, vmInstanceId, attempts = 5) => {
|
|
|
1570
1552
|
* 1.A.4.C.1) If true, update circuit waiting for queue and average timings accordingly to contribution verification results;
|
|
1571
1553
|
* 2) Send all updates atomically to the Firestore database.
|
|
1572
1554
|
*/
|
|
1573
|
-
const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB", timeoutSeconds: 3600, region:
|
|
1555
|
+
const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB", timeoutSeconds: 3600, region: "europe-west1" }, async (request) => {
|
|
1574
1556
|
if (!request.auth || (!request.auth.token.participant && !request.auth.token.coordinator))
|
|
1575
1557
|
logAndThrowError(SPECIFIC_ERRORS.SE_AUTH_NO_CURRENT_AUTH_USER);
|
|
1576
1558
|
if (!request.data.ceremonyId ||
|
|
@@ -1681,8 +1663,6 @@ const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB"
|
|
|
1681
1663
|
lastZkeyBlake2bHash = match.at(0);
|
|
1682
1664
|
// re upload the formatted verification transcript
|
|
1683
1665
|
await uploadFileToBucket(bucketName, verificationTranscriptStoragePathAndFilename, verificationTranscriptTemporaryLocalPath, true);
|
|
1684
|
-
// Stop VM instance.
|
|
1685
|
-
await actions.stopEC2Instance(ec2, vmInstanceId);
|
|
1686
1666
|
}
|
|
1687
1667
|
else {
|
|
1688
1668
|
// Upload verification transcript.
|
|
@@ -1743,6 +1723,9 @@ const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB"
|
|
|
1743
1723
|
lastUpdated: getCurrentServerTimestampInMillis()
|
|
1744
1724
|
});
|
|
1745
1725
|
}
|
|
1726
|
+
// Stop VM instance
|
|
1727
|
+
if (isUsingVM)
|
|
1728
|
+
await actions.stopEC2Instance(ec2, vmInstanceId);
|
|
1746
1729
|
// Step (1.A.4.C)
|
|
1747
1730
|
if (!isFinalizing) {
|
|
1748
1731
|
// Step (1.A.4.C.1)
|
|
@@ -1758,6 +1741,8 @@ const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB"
|
|
|
1758
1741
|
? (avgVerifyCloudFunctionTime + verifyCloudFunctionTime) / 2
|
|
1759
1742
|
: verifyCloudFunctionTime;
|
|
1760
1743
|
// Prepare tx to update circuit average contribution/verification time.
|
|
1744
|
+
const updatedCircuitDoc = await getDocumentById(actions.getCircuitsCollectionPath(ceremonyId), circuitId);
|
|
1745
|
+
const { waitingQueue: updatedWaitingQueue } = updatedCircuitDoc.data();
|
|
1761
1746
|
/// @dev this must happen only for valid contributions.
|
|
1762
1747
|
batch.update(circuitDoc.ref, {
|
|
1763
1748
|
avgTimings: {
|
|
@@ -1770,7 +1755,7 @@ const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB"
|
|
|
1770
1755
|
: avgVerifyCloudFunctionTime
|
|
1771
1756
|
},
|
|
1772
1757
|
waitingQueue: {
|
|
1773
|
-
...
|
|
1758
|
+
...updatedWaitingQueue,
|
|
1774
1759
|
completedContributions: isContributionValid
|
|
1775
1760
|
? completedContributions + 1
|
|
1776
1761
|
: completedContributions,
|
|
@@ -1805,7 +1790,7 @@ const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB"
|
|
|
1805
1790
|
commandId = await actions.runCommandUsingSSM(ssm, vmInstanceId, verificationCommand);
|
|
1806
1791
|
printLog(`Starting the execution of command ${commandId}`, LogLevel.DEBUG);
|
|
1807
1792
|
// Step (1.A.3.3).
|
|
1808
|
-
return
|
|
1793
|
+
return waitForVMCommandExecution(ssm, vmInstanceId, commandId)
|
|
1809
1794
|
.then(async () => {
|
|
1810
1795
|
// Command execution successfully completed.
|
|
1811
1796
|
printLog(`Command ${commandId} execution has been successfully completed`, LogLevel.DEBUG);
|
|
@@ -1817,52 +1802,38 @@ const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB"
|
|
|
1817
1802
|
logAndThrowError(COMMON_ERRORS.CM_INVALID_COMMAND_EXECUTION);
|
|
1818
1803
|
});
|
|
1819
1804
|
}
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
fs.unlinkSync(potTempFilePath);
|
|
1851
|
-
fs.unlinkSync(firstZkeyTempFilePath);
|
|
1852
|
-
fs.unlinkSync(lastZkeyTempFilePath);
|
|
1853
|
-
}
|
|
1854
|
-
catch (error) {
|
|
1855
|
-
printLog(`Error while unlinking temporary files - Error ${error}`, LogLevel.WARN);
|
|
1856
|
-
}
|
|
1857
|
-
await completeVerification();
|
|
1858
|
-
})
|
|
1859
|
-
.catch((error) => {
|
|
1860
|
-
// Throw the new error
|
|
1861
|
-
const commonError = COMMON_ERRORS.CM_INVALID_REQUEST;
|
|
1862
|
-
const additionalDetails = error.toString();
|
|
1863
|
-
logAndThrowError(makeError(commonError.code, commonError.message, additionalDetails));
|
|
1864
|
-
});
|
|
1805
|
+
// CF approach.
|
|
1806
|
+
printLog(`CF mechanism`, LogLevel.DEBUG);
|
|
1807
|
+
const potStoragePath = actions.getPotStorageFilePath(files.potFilename);
|
|
1808
|
+
const firstZkeyStoragePath = actions.getZkeyStorageFilePath(prefix, `${prefix}_${actions.genesisZkeyIndex}.zkey`);
|
|
1809
|
+
// Prepare temporary file paths.
|
|
1810
|
+
// (nb. these are needed to download the necessary artifacts for verification from AWS S3).
|
|
1811
|
+
verificationTranscriptTemporaryLocalPath = createTemporaryLocalPath(verificationTranscriptCompleteFilename);
|
|
1812
|
+
const potTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}.pot`);
|
|
1813
|
+
const firstZkeyTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}_genesis.zkey`);
|
|
1814
|
+
const lastZkeyTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}_last.zkey`);
|
|
1815
|
+
// Create and populate transcript.
|
|
1816
|
+
const transcriptLogger = actions.createCustomLoggerForFile(verificationTranscriptTemporaryLocalPath);
|
|
1817
|
+
transcriptLogger.info(`${isFinalizing ? `Final verification` : `Verification`} transcript for ${prefix} circuit Phase 2 contribution.\n${isFinalizing ? `Coordinator ` : `Contributor # ${Number(lastZkeyIndex)}`} (${contributorOrCoordinatorIdentifier})\n`);
|
|
1818
|
+
// Step (1.A.2).
|
|
1819
|
+
await downloadArtifactFromS3Bucket(bucketName, potStoragePath, potTempFilePath);
|
|
1820
|
+
await downloadArtifactFromS3Bucket(bucketName, firstZkeyStoragePath, firstZkeyTempFilePath);
|
|
1821
|
+
await downloadArtifactFromS3Bucket(bucketName, lastZkeyStoragePath, lastZkeyTempFilePath);
|
|
1822
|
+
// Step (1.A.4).
|
|
1823
|
+
isContributionValid = await snarkjs.zKey.verifyFromInit(firstZkeyTempFilePath, potTempFilePath, lastZkeyTempFilePath, transcriptLogger);
|
|
1824
|
+
// Compute contribution hash.
|
|
1825
|
+
lastZkeyBlake2bHash = await actions.blake512FromPath(lastZkeyTempFilePath);
|
|
1826
|
+
// Free resources by unlinking temporary folders.
|
|
1827
|
+
// Do not free-up verification transcript path here.
|
|
1828
|
+
try {
|
|
1829
|
+
fs.unlinkSync(potTempFilePath);
|
|
1830
|
+
fs.unlinkSync(firstZkeyTempFilePath);
|
|
1831
|
+
fs.unlinkSync(lastZkeyTempFilePath);
|
|
1832
|
+
}
|
|
1833
|
+
catch (error) {
|
|
1834
|
+
printLog(`Error while unlinking temporary files - Error ${error}`, LogLevel.WARN);
|
|
1865
1835
|
}
|
|
1836
|
+
await completeVerification();
|
|
1866
1837
|
}
|
|
1867
1838
|
});
|
|
1868
1839
|
/**
|
|
@@ -1871,7 +1842,7 @@ const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB"
|
|
|
1871
1842
|
* this does not happen if the participant is actually the coordinator who is finalizing the ceremony.
|
|
1872
1843
|
*/
|
|
1873
1844
|
const refreshParticipantAfterContributionVerification = functionsV1__namespace
|
|
1874
|
-
.region(
|
|
1845
|
+
.region("europe-west1")
|
|
1875
1846
|
.runWith({
|
|
1876
1847
|
memory: "512MB"
|
|
1877
1848
|
})
|
|
@@ -1932,7 +1903,7 @@ const refreshParticipantAfterContributionVerification = functionsV1__namespace
|
|
|
1932
1903
|
* and verification key extracted from the circuit final contribution (as part of the ceremony finalization process).
|
|
1933
1904
|
*/
|
|
1934
1905
|
const finalizeCircuit = functionsV1__namespace
|
|
1935
|
-
.region(
|
|
1906
|
+
.region("europe-west1")
|
|
1936
1907
|
.runWith({
|
|
1937
1908
|
memory: "512MB"
|
|
1938
1909
|
})
|
|
@@ -2129,8 +2100,10 @@ const createBucket = functions__namespace
|
|
|
2129
2100
|
CORSConfiguration: {
|
|
2130
2101
|
CORSRules: [
|
|
2131
2102
|
{
|
|
2132
|
-
AllowedMethods: ["GET"],
|
|
2133
|
-
AllowedOrigins: ["*"]
|
|
2103
|
+
AllowedMethods: ["GET", "PUT"],
|
|
2104
|
+
AllowedOrigins: ["*"],
|
|
2105
|
+
ExposeHeaders: ["ETag", "Content-Length"],
|
|
2106
|
+
AllowedHeaders: ["*"]
|
|
2134
2107
|
}
|
|
2135
2108
|
]
|
|
2136
2109
|
}
|
|
@@ -2523,7 +2496,7 @@ const checkAndRemoveBlockingContributor = functions__namespace
|
|
|
2523
2496
|
// Prepare Firestore batch of txs.
|
|
2524
2497
|
const batch = firestore.batch();
|
|
2525
2498
|
// Remove current contributor from waiting queue.
|
|
2526
|
-
contributors.shift(
|
|
2499
|
+
contributors.shift();
|
|
2527
2500
|
// Check if someone else is ready to start the contribution.
|
|
2528
2501
|
if (contributors.length > 0) {
|
|
2529
2502
|
// Step (E.1).
|
|
@@ -2607,7 +2580,8 @@ const resumeContributionAfterTimeoutExpiration = functions__namespace
|
|
|
2607
2580
|
if (status === "EXHUMED" /* ParticipantStatus.EXHUMED */)
|
|
2608
2581
|
await participantDoc.ref.update({
|
|
2609
2582
|
status: "READY" /* ParticipantStatus.READY */,
|
|
2610
|
-
lastUpdated: getCurrentServerTimestampInMillis()
|
|
2583
|
+
lastUpdated: getCurrentServerTimestampInMillis(),
|
|
2584
|
+
tempContributionData: {}
|
|
2611
2585
|
});
|
|
2612
2586
|
else
|
|
2613
2587
|
logAndThrowError(SPECIFIC_ERRORS.SE_CONTRIBUTE_CANNOT_PROGRESS_TO_NEXT_CIRCUIT);
|