@devtion/backend 0.0.0-eb3bb7d → 0.0.0-f11504f
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 +7 -7
- package/dist/src/functions/index.js +139 -103
- package/dist/src/functions/index.mjs +141 -105
- 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/package.json +3 -3
- package/src/functions/ceremony.ts +9 -4
- package/src/functions/circuit.ts +138 -116
- package/src/functions/participant.ts +9 -9
- package/src/functions/storage.ts +7 -4
- package/src/functions/timeout.ts +4 -3
- package/src/functions/user.ts +19 -9
- package/src/lib/errors.ts +5 -0
- package/src/lib/utils.ts +11 -9
- package/src/types/index.ts +1 -1
package/README.md
CHANGED
|
@@ -51,7 +51,7 @@ Launching the ready-to-run customized scripts everyone could handle whatever is
|
|
|
51
51
|
|
|
52
52
|
## 🛠 Installation
|
|
53
53
|
|
|
54
|
-
**
|
|
54
|
+
**Prerequisites**
|
|
55
55
|
|
|
56
56
|
- Node.js version 16.0 or higher.
|
|
57
57
|
- Yarn version 3.5.0 or higher.
|
|
@@ -102,10 +102,10 @@ yarn firebase:init
|
|
|
102
102
|
|
|
103
103
|
#### AWS Infrastructure
|
|
104
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).
|
|
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
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 (
|
|
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.
|
|
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
109
|
3. Install `terraform` ([docs](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli))
|
|
110
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
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
|
|
@@ -117,9 +117,9 @@ yarn firebase:init
|
|
|
117
117
|
1. `terraform init`
|
|
118
118
|
2. `terraform plan`
|
|
119
119
|
3. `terraform apply`
|
|
120
|
-
4. `terraform output secret_key`
|
|
120
|
+
4. `terraform output secret_key`
|
|
121
121
|
- To print the secret access key for the IAM user
|
|
122
|
-
|
|
122
|
+
5. Store the other values (sns_topic_arn etc.)
|
|
123
123
|
- These will be needed for the .env file configuration
|
|
124
124
|
|
|
125
125
|
The IAM user created with the steps above can be used for all p0tion's features.
|
|
@@ -148,7 +148,7 @@ yarn firebase:deploy-firestore
|
|
|
148
148
|
|
|
149
149
|
Firebase provides a [Local Emulator Suite](https://firebase.google.com/docs/emulator-suite) as a set of advanced dev-tools w/ a rich user-interface to build and test apps locally using Firebase services as Cloud Functions, Firestore and Authentication.
|
|
150
150
|
|
|
151
|
-
**
|
|
151
|
+
**Prerequisites**
|
|
152
152
|
|
|
153
153
|
- You will need Java JDK version 11 or higher to run the Firebase Local Emulator.
|
|
154
154
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module @p0tion/backend
|
|
3
|
-
* @version 1.0
|
|
3
|
+
* @version 1.1.0
|
|
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.
|
|
@@ -287,7 +288,7 @@ const queryOpenedCeremonies = async () => {
|
|
|
287
288
|
const getCircuitDocumentByPosition = async (ceremonyId, sequencePosition) => {
|
|
288
289
|
// Query for all ceremony circuits.
|
|
289
290
|
const circuits = await getCeremonyCircuits(ceremonyId);
|
|
290
|
-
// Apply a filter using the sequence
|
|
291
|
+
// Apply a filter using the sequence position.
|
|
291
292
|
const matchedCircuits = circuits.filter((circuit) => circuit.data().sequencePosition === sequencePosition);
|
|
292
293
|
if (matchedCircuits.length !== 1)
|
|
293
294
|
logAndThrowError(COMMON_ERRORS.CM_NO_CIRCUIT_FOR_GIVEN_SEQUENCE_POSITION);
|
|
@@ -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
|
};
|
|
@@ -452,12 +453,14 @@ const htmlEncodeCircuitData = (circuitDocument) => ({
|
|
|
452
453
|
const getGitHubVariables = () => {
|
|
453
454
|
if (!process.env.GITHUB_MINIMUM_FOLLOWERS ||
|
|
454
455
|
!process.env.GITHUB_MINIMUM_FOLLOWING ||
|
|
455
|
-
!process.env.GITHUB_MINIMUM_PUBLIC_REPOS
|
|
456
|
+
!process.env.GITHUB_MINIMUM_PUBLIC_REPOS ||
|
|
457
|
+
!process.env.GITHUB_MINIMUM_AGE)
|
|
456
458
|
logAndThrowError(COMMON_ERRORS.CM_WRONG_CONFIGURATION);
|
|
457
459
|
return {
|
|
458
460
|
minimumFollowers: Number(process.env.GITHUB_MINIMUM_FOLLOWERS),
|
|
459
461
|
minimumFollowing: Number(process.env.GITHUB_MINIMUM_FOLLOWING),
|
|
460
|
-
minimumPublicRepos: Number(process.env.GITHUB_MINIMUM_PUBLIC_REPOS)
|
|
462
|
+
minimumPublicRepos: Number(process.env.GITHUB_MINIMUM_PUBLIC_REPOS),
|
|
463
|
+
minimumAge: Number(process.env.GITHUB_MINIMUM_AGE)
|
|
461
464
|
};
|
|
462
465
|
};
|
|
463
466
|
/**
|
|
@@ -467,7 +470,7 @@ const getGitHubVariables = () => {
|
|
|
467
470
|
const getAWSVariables = () => {
|
|
468
471
|
if (!process.env.AWS_ACCESS_KEY_ID ||
|
|
469
472
|
!process.env.AWS_SECRET_ACCESS_KEY ||
|
|
470
|
-
!process.env.
|
|
473
|
+
!process.env.AWS_INSTANCE_PROFILE_ARN ||
|
|
471
474
|
!process.env.AWS_AMI_ID ||
|
|
472
475
|
!process.env.AWS_SNS_TOPIC_ARN)
|
|
473
476
|
logAndThrowError(COMMON_ERRORS.CM_WRONG_CONFIGURATION);
|
|
@@ -475,7 +478,7 @@ const getAWSVariables = () => {
|
|
|
475
478
|
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
476
479
|
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
|
477
480
|
region: process.env.AWS_REGION || "eu-central-1",
|
|
478
|
-
|
|
481
|
+
instanceProfileArn: process.env.AWS_INSTANCE_PROFILE_ARN,
|
|
479
482
|
amiId: process.env.AWS_AMI_ID,
|
|
480
483
|
snsTopic: process.env.AWS_SNS_TOPIC_ARN
|
|
481
484
|
};
|
|
@@ -557,12 +560,14 @@ const registerAuthUser = functions__namespace
|
|
|
557
560
|
const vars = getGitHubVariables();
|
|
558
561
|
// this return true or false
|
|
559
562
|
try {
|
|
560
|
-
const { reputable, avatarUrl: avatarURL } = await actions.githubReputation(user.providerData[0].uid, vars.minimumFollowing, vars.minimumFollowers, vars.minimumPublicRepos);
|
|
563
|
+
const { reputable, avatarUrl: avatarURL } = await actions.githubReputation(user.providerData[0].uid, vars.minimumFollowing, vars.minimumFollowers, vars.minimumPublicRepos, vars.minimumAge);
|
|
561
564
|
if (!reputable) {
|
|
562
565
|
// Delete user
|
|
563
566
|
await auth.deleteUser(user.uid);
|
|
564
567
|
// Throw error
|
|
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
|
|
568
|
+
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
|
|
569
|
+
? user.uid
|
|
570
|
+
: 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.`));
|
|
566
571
|
}
|
|
567
572
|
// store locally
|
|
568
573
|
avatarUrl = avatarURL;
|
|
@@ -577,7 +582,7 @@ const registerAuthUser = functions__namespace
|
|
|
577
582
|
}
|
|
578
583
|
// Set document (nb. we refer to providerData[0] because we use Github OAuth provider only).
|
|
579
584
|
// In future releases we might want to loop through the providerData array as we support
|
|
580
|
-
// more providers.
|
|
585
|
+
// more providers.
|
|
581
586
|
await userRef.set({
|
|
582
587
|
name: encodedDisplayName,
|
|
583
588
|
encodedDisplayName,
|
|
@@ -593,7 +598,7 @@ const registerAuthUser = functions__namespace
|
|
|
593
598
|
// we want to create a new collection for the users to store the avatars
|
|
594
599
|
const avatarRef = firestore.collection(actions.commonTerms.collections.avatars.name).doc(uid);
|
|
595
600
|
await avatarRef.set({
|
|
596
|
-
avatarUrl: avatarUrl || ""
|
|
601
|
+
avatarUrl: avatarUrl || ""
|
|
597
602
|
});
|
|
598
603
|
printLog(`Authenticated user document with identifier ${uid} has been correctly stored`, LogLevel.DEBUG);
|
|
599
604
|
printLog(`Authenticated user avatar with identifier ${uid} has been correctly stored`, LogLevel.DEBUG);
|
|
@@ -733,7 +738,7 @@ const setupCeremony = functions__namespace
|
|
|
733
738
|
// Check if using the VM approach for contribution verification.
|
|
734
739
|
if (circuit.verification.cfOrVm === "VM" /* CircuitContributionVerificationMechanism.VM */) {
|
|
735
740
|
// VM command to be run at the startup.
|
|
736
|
-
const startupCommand = actions.vmBootstrapCommand(bucketName);
|
|
741
|
+
const startupCommand = actions.vmBootstrapCommand(`${bucketName}/circuits/${circuit.name}`);
|
|
737
742
|
// Get EC2 client.
|
|
738
743
|
const ec2Client = await createEC2Client();
|
|
739
744
|
// Get AWS variables.
|
|
@@ -742,7 +747,8 @@ const setupCeremony = functions__namespace
|
|
|
742
747
|
const vmCommands = actions.vmDependenciesAndCacheArtifactsCommand(`${bucketName}/${circuit.files?.initialZkeyStoragePath}`, `${bucketName}/${circuit.files?.potStoragePath}`, snsTopic, region);
|
|
743
748
|
printLog(`Check VM dependencies and cache artifacts commands ${vmCommands.join("\n")}`, LogLevel.DEBUG);
|
|
744
749
|
// Upload the post-startup commands script file.
|
|
745
|
-
|
|
750
|
+
printLog(`Uploading VM post-startup commands script file ${actions.vmBootstrapScriptFilename}`, LogLevel.DEBUG);
|
|
751
|
+
await uploadFileToBucketNoFile(bucketName, `circuits/${circuit.name}/${actions.vmBootstrapScriptFilename}`, vmCommands.join("\n"));
|
|
746
752
|
// Compute the VM disk space requirement (in GB).
|
|
747
753
|
const vmDiskSize = actions.computeDiskSizeForVM(circuit.zKeySizeInBytes, circuit.metadata?.pot);
|
|
748
754
|
printLog(`Check VM startup commands ${startupCommand.join("\n")}`, LogLevel.DEBUG);
|
|
@@ -836,7 +842,7 @@ const finalizeCeremony = functions__namespace
|
|
|
836
842
|
// Get ceremony circuits.
|
|
837
843
|
const circuits = await getCeremonyCircuits(ceremonyId);
|
|
838
844
|
// Get final contribution for each circuit.
|
|
839
|
-
// nb. the `getFinalContributionDocument` checks the
|
|
845
|
+
// nb. the `getFinalContributionDocument` checks the existence of the final contribution document (if not present, throws).
|
|
840
846
|
// Therefore, we just need to call the method without taking any data to verify the pre-condition of having already computed
|
|
841
847
|
// the final contributions for each ceremony circuit.
|
|
842
848
|
for await (const circuit of circuits)
|
|
@@ -889,7 +895,7 @@ dotenv.config();
|
|
|
889
895
|
* @dev true when the participant can participate (1.A, 3.B, 1.D); otherwise false.
|
|
890
896
|
*/
|
|
891
897
|
const checkParticipantForCeremony = functions__namespace
|
|
892
|
-
.region(
|
|
898
|
+
.region("europe-west1")
|
|
893
899
|
.runWith({
|
|
894
900
|
memory: "512MB"
|
|
895
901
|
})
|
|
@@ -960,7 +966,7 @@ const checkParticipantForCeremony = functions__namespace
|
|
|
960
966
|
participantDoc.ref.update({
|
|
961
967
|
status: "EXHUMED" /* ParticipantStatus.EXHUMED */,
|
|
962
968
|
contributions,
|
|
963
|
-
tempContributionData: tempContributionData
|
|
969
|
+
tempContributionData: tempContributionData || firestore.FieldValue.delete(),
|
|
964
970
|
contributionStep: "DOWNLOADING" /* ParticipantContributionStep.DOWNLOADING */,
|
|
965
971
|
contributionStartedAt: 0,
|
|
966
972
|
verificationStartedAt: firestore.FieldValue.delete(),
|
|
@@ -993,7 +999,7 @@ const checkParticipantForCeremony = functions__namespace
|
|
|
993
999
|
* 2) the participant has just finished the contribution for a circuit (contributionProgress != 0 && status = CONTRIBUTED && contributionStep = COMPLETED).
|
|
994
1000
|
*/
|
|
995
1001
|
const progressToNextCircuitForContribution = functions__namespace
|
|
996
|
-
.region(
|
|
1002
|
+
.region("europe-west1")
|
|
997
1003
|
.runWith({
|
|
998
1004
|
memory: "512MB"
|
|
999
1005
|
})
|
|
@@ -1040,7 +1046,7 @@ const progressToNextCircuitForContribution = functions__namespace
|
|
|
1040
1046
|
* 5) Completed contribution computation and verification.
|
|
1041
1047
|
*/
|
|
1042
1048
|
const progressToNextContributionStep = functions__namespace
|
|
1043
|
-
.region(
|
|
1049
|
+
.region("europe-west1")
|
|
1044
1050
|
.runWith({
|
|
1045
1051
|
memory: "512MB"
|
|
1046
1052
|
})
|
|
@@ -1091,7 +1097,7 @@ const progressToNextContributionStep = functions__namespace
|
|
|
1091
1097
|
* @dev enable the current contributor to resume a contribution from where it had left off.
|
|
1092
1098
|
*/
|
|
1093
1099
|
const permanentlyStoreCurrentContributionTimeAndHash = functions__namespace
|
|
1094
|
-
.region(
|
|
1100
|
+
.region("europe-west1")
|
|
1095
1101
|
.runWith({
|
|
1096
1102
|
memory: "512MB"
|
|
1097
1103
|
})
|
|
@@ -1133,7 +1139,7 @@ const permanentlyStoreCurrentContributionTimeAndHash = functions__namespace
|
|
|
1133
1139
|
* @dev enable the current contributor to resume a multi-part upload from where it had left off.
|
|
1134
1140
|
*/
|
|
1135
1141
|
const temporaryStoreCurrentContributionMultiPartUploadId = functions__namespace
|
|
1136
|
-
.region(
|
|
1142
|
+
.region("europe-west1")
|
|
1137
1143
|
.runWith({
|
|
1138
1144
|
memory: "512MB"
|
|
1139
1145
|
})
|
|
@@ -1171,7 +1177,7 @@ const temporaryStoreCurrentContributionMultiPartUploadId = functions__namespace
|
|
|
1171
1177
|
* @dev enable the current contributor to resume a multi-part upload from where it had left off.
|
|
1172
1178
|
*/
|
|
1173
1179
|
const temporaryStoreCurrentContributionUploadedChunkData = functions__namespace
|
|
1174
|
-
.region(
|
|
1180
|
+
.region("europe-west1")
|
|
1175
1181
|
.runWith({
|
|
1176
1182
|
memory: "512MB"
|
|
1177
1183
|
})
|
|
@@ -1213,7 +1219,7 @@ const temporaryStoreCurrentContributionUploadedChunkData = functions__namespace
|
|
|
1213
1219
|
* contributed to every selected ceremony circuits (= DONE).
|
|
1214
1220
|
*/
|
|
1215
1221
|
const checkAndPrepareCoordinatorForFinalization = functions__namespace
|
|
1216
|
-
.region(
|
|
1222
|
+
.region("europe-west1")
|
|
1217
1223
|
.runWith({
|
|
1218
1224
|
memory: "512MB"
|
|
1219
1225
|
})
|
|
@@ -1365,54 +1371,74 @@ const coordinate = async (participant, circuit, isSingleParticipantCoordination,
|
|
|
1365
1371
|
* Wait until the command has completed its execution inside the VM.
|
|
1366
1372
|
* @dev this method implements a custom interval to check 5 times after 1 minute if the command execution
|
|
1367
1373
|
* has been completed or not by calling the `retrieveCommandStatus` method.
|
|
1368
|
-
* @param {any} resolve the promise.
|
|
1369
|
-
* @param {any} reject the promise.
|
|
1370
1374
|
* @param {SSMClient} ssm the SSM client.
|
|
1371
1375
|
* @param {string} vmInstanceId the unique identifier of the VM instance.
|
|
1372
1376
|
* @param {string} commandId the unique identifier of the VM command.
|
|
1373
1377
|
* @returns <Promise<void>> true when the command execution succeed; otherwise false.
|
|
1374
1378
|
*/
|
|
1375
|
-
const waitForVMCommandExecution = (
|
|
1376
|
-
const
|
|
1379
|
+
const waitForVMCommandExecution = (ssm, vmInstanceId, commandId) => new Promise((resolve, reject) => {
|
|
1380
|
+
const poll = async () => {
|
|
1377
1381
|
try {
|
|
1378
1382
|
// Get command status.
|
|
1379
1383
|
const cmdStatus = await actions.retrieveCommandStatus(ssm, vmInstanceId, commandId);
|
|
1380
1384
|
printLog(`Checking command ${commandId} status => ${cmdStatus}`, LogLevel.DEBUG);
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1385
|
+
let error;
|
|
1386
|
+
switch (cmdStatus) {
|
|
1387
|
+
case clientSsm.CommandInvocationStatus.CANCELLING:
|
|
1388
|
+
case clientSsm.CommandInvocationStatus.CANCELLED: {
|
|
1389
|
+
error = SPECIFIC_ERRORS.SE_VM_CANCELLED_COMMAND_EXECUTION;
|
|
1390
|
+
break;
|
|
1391
|
+
}
|
|
1392
|
+
case clientSsm.CommandInvocationStatus.DELAYED: {
|
|
1393
|
+
error = SPECIFIC_ERRORS.SE_VM_DELAYED_COMMAND_EXECUTION;
|
|
1394
|
+
break;
|
|
1395
|
+
}
|
|
1396
|
+
case clientSsm.CommandInvocationStatus.FAILED: {
|
|
1397
|
+
error = SPECIFIC_ERRORS.SE_VM_FAILED_COMMAND_EXECUTION;
|
|
1398
|
+
break;
|
|
1399
|
+
}
|
|
1400
|
+
case clientSsm.CommandInvocationStatus.TIMED_OUT: {
|
|
1401
|
+
error = SPECIFIC_ERRORS.SE_VM_TIMEDOUT_COMMAND_EXECUTION;
|
|
1402
|
+
break;
|
|
1403
|
+
}
|
|
1404
|
+
case clientSsm.CommandInvocationStatus.IN_PROGRESS:
|
|
1405
|
+
case clientSsm.CommandInvocationStatus.PENDING: {
|
|
1406
|
+
// wait a minute and poll again
|
|
1407
|
+
setTimeout(poll, 60000);
|
|
1408
|
+
return;
|
|
1409
|
+
}
|
|
1410
|
+
case clientSsm.CommandInvocationStatus.SUCCESS: {
|
|
1411
|
+
printLog(`Command ${commandId} successfully completed`, LogLevel.DEBUG);
|
|
1412
|
+
// Resolve the promise.
|
|
1413
|
+
resolve();
|
|
1414
|
+
return;
|
|
1415
|
+
}
|
|
1416
|
+
default: {
|
|
1417
|
+
logAndThrowError(SPECIFIC_ERRORS.SE_VM_UNKNOWN_COMMAND_STATUS);
|
|
1418
|
+
}
|
|
1397
1419
|
}
|
|
1398
|
-
|
|
1399
|
-
logAndThrowError(
|
|
1400
|
-
reject();
|
|
1420
|
+
if (error) {
|
|
1421
|
+
logAndThrowError(error);
|
|
1401
1422
|
}
|
|
1402
1423
|
}
|
|
1403
1424
|
catch (error) {
|
|
1404
1425
|
printLog(`Invalid command ${commandId} execution`, LogLevel.DEBUG);
|
|
1426
|
+
const ec2 = await createEC2Client();
|
|
1427
|
+
// if it errors out, let's just log it as a warning so the coordinator is aware
|
|
1428
|
+
try {
|
|
1429
|
+
await actions.stopEC2Instance(ec2, vmInstanceId);
|
|
1430
|
+
}
|
|
1431
|
+
catch (error) {
|
|
1432
|
+
printLog(`Error while stopping VM instance ${vmInstanceId} - Error ${error}`, LogLevel.WARN);
|
|
1433
|
+
}
|
|
1405
1434
|
if (!error.toString().includes(commandId))
|
|
1406
1435
|
logAndThrowError(COMMON_ERRORS.CM_INVALID_COMMAND_EXECUTION);
|
|
1407
1436
|
// Reject the promise.
|
|
1408
1437
|
reject();
|
|
1409
1438
|
}
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
}
|
|
1414
|
-
}, 60000); // 1 minute.
|
|
1415
|
-
};
|
|
1439
|
+
};
|
|
1440
|
+
setTimeout(poll, 60000);
|
|
1441
|
+
});
|
|
1416
1442
|
/**
|
|
1417
1443
|
* This method is used to coordinate the waiting queues of ceremony circuits.
|
|
1418
1444
|
* @dev this cloud function is triggered whenever an update of a document related to a participant of a ceremony occurs.
|
|
@@ -1433,7 +1459,7 @@ const waitForVMCommandExecution = (resolve, reject, ssm, vmInstanceId, commandId
|
|
|
1433
1459
|
* - Just completed a contribution or all contributions for each circuit. If yes, coordinate (multi-participant scenario).
|
|
1434
1460
|
*/
|
|
1435
1461
|
const coordinateCeremonyParticipant = functionsV1__namespace
|
|
1436
|
-
.region(
|
|
1462
|
+
.region("europe-west1")
|
|
1437
1463
|
.runWith({
|
|
1438
1464
|
memory: "512MB"
|
|
1439
1465
|
})
|
|
@@ -1504,11 +1530,9 @@ const checkIfVMRunning = async (ec2, vmInstanceId, attempts = 5) => {
|
|
|
1504
1530
|
const isVMRunning = await actions.checkIfRunning(ec2, vmInstanceId);
|
|
1505
1531
|
if (!isVMRunning) {
|
|
1506
1532
|
printLog(`VM not running, ${attempts - 1} attempts remaining. Retrying in 1 minute...`, LogLevel.DEBUG);
|
|
1507
|
-
return
|
|
1508
|
-
}
|
|
1509
|
-
else {
|
|
1510
|
-
return true;
|
|
1533
|
+
return checkIfVMRunning(ec2, vmInstanceId, attempts - 1);
|
|
1511
1534
|
}
|
|
1535
|
+
return true;
|
|
1512
1536
|
};
|
|
1513
1537
|
/**
|
|
1514
1538
|
* Verify the contribution of a participant computed while contributing to a specific circuit of a ceremony.
|
|
@@ -1536,7 +1560,7 @@ const checkIfVMRunning = async (ec2, vmInstanceId, attempts = 5) => {
|
|
|
1536
1560
|
* 1.A.4.C.1) If true, update circuit waiting for queue and average timings accordingly to contribution verification results;
|
|
1537
1561
|
* 2) Send all updates atomically to the Firestore database.
|
|
1538
1562
|
*/
|
|
1539
|
-
const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB", timeoutSeconds: 3600, region:
|
|
1563
|
+
const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB", timeoutSeconds: 3600, region: "europe-west1" }, async (request) => {
|
|
1540
1564
|
if (!request.auth || (!request.auth.token.participant && !request.auth.token.coordinator))
|
|
1541
1565
|
logAndThrowError(SPECIFIC_ERRORS.SE_AUTH_NO_CURRENT_AUTH_USER);
|
|
1542
1566
|
if (!request.data.ceremonyId ||
|
|
@@ -1647,8 +1671,6 @@ const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB"
|
|
|
1647
1671
|
lastZkeyBlake2bHash = match.at(0);
|
|
1648
1672
|
// re upload the formatted verification transcript
|
|
1649
1673
|
await uploadFileToBucket(bucketName, verificationTranscriptStoragePathAndFilename, verificationTranscriptTemporaryLocalPath, true);
|
|
1650
|
-
// Stop VM instance.
|
|
1651
|
-
await actions.stopEC2Instance(ec2, vmInstanceId);
|
|
1652
1674
|
}
|
|
1653
1675
|
else {
|
|
1654
1676
|
// Upload verification transcript.
|
|
@@ -1709,6 +1731,18 @@ const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB"
|
|
|
1709
1731
|
lastUpdated: getCurrentServerTimestampInMillis()
|
|
1710
1732
|
});
|
|
1711
1733
|
}
|
|
1734
|
+
// Stop VM instance
|
|
1735
|
+
if (isUsingVM) {
|
|
1736
|
+
// using try and catch as the VM stopping function can throw
|
|
1737
|
+
// however we want to continue without stopping as the
|
|
1738
|
+
// verification was valid, and inform the coordinator
|
|
1739
|
+
try {
|
|
1740
|
+
await actions.stopEC2Instance(ec2, vmInstanceId);
|
|
1741
|
+
}
|
|
1742
|
+
catch (error) {
|
|
1743
|
+
printLog(`Error while stopping VM instance ${vmInstanceId} - Error ${error}`, LogLevel.WARN);
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1712
1746
|
// Step (1.A.4.C)
|
|
1713
1747
|
if (!isFinalizing) {
|
|
1714
1748
|
// Step (1.A.4.C.1)
|
|
@@ -1723,7 +1757,7 @@ const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB"
|
|
|
1723
1757
|
const newAvgVerifyCloudFunctionTime = avgVerifyCloudFunctionTime > 0
|
|
1724
1758
|
? (avgVerifyCloudFunctionTime + verifyCloudFunctionTime) / 2
|
|
1725
1759
|
: verifyCloudFunctionTime;
|
|
1726
|
-
// Prepare tx to update circuit average contribution/verification time.
|
|
1760
|
+
// Prepare tx to update circuit average contribution/verification time.
|
|
1727
1761
|
const updatedCircuitDoc = await getDocumentById(actions.getCircuitsCollectionPath(ceremonyId), circuitId);
|
|
1728
1762
|
const { waitingQueue: updatedWaitingQueue } = updatedCircuitDoc.data();
|
|
1729
1763
|
/// @dev this must happen only for valid contributions.
|
|
@@ -1773,7 +1807,7 @@ const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB"
|
|
|
1773
1807
|
commandId = await actions.runCommandUsingSSM(ssm, vmInstanceId, verificationCommand);
|
|
1774
1808
|
printLog(`Starting the execution of command ${commandId}`, LogLevel.DEBUG);
|
|
1775
1809
|
// Step (1.A.3.3).
|
|
1776
|
-
return
|
|
1810
|
+
return waitForVMCommandExecution(ssm, vmInstanceId, commandId)
|
|
1777
1811
|
.then(async () => {
|
|
1778
1812
|
// Command execution successfully completed.
|
|
1779
1813
|
printLog(`Command ${commandId} execution has been successfully completed`, LogLevel.DEBUG);
|
|
@@ -1785,40 +1819,38 @@ const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB"
|
|
|
1785
1819
|
logAndThrowError(COMMON_ERRORS.CM_INVALID_COMMAND_EXECUTION);
|
|
1786
1820
|
});
|
|
1787
1821
|
}
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
fs.unlinkSync(lastZkeyTempFilePath);
|
|
1816
|
-
}
|
|
1817
|
-
catch (error) {
|
|
1818
|
-
printLog(`Error while unlinking temporary files - Error ${error}`, LogLevel.WARN);
|
|
1819
|
-
}
|
|
1820
|
-
await completeVerification();
|
|
1822
|
+
// CF approach.
|
|
1823
|
+
printLog(`CF mechanism`, LogLevel.DEBUG);
|
|
1824
|
+
const potStoragePath = actions.getPotStorageFilePath(files.potFilename);
|
|
1825
|
+
const firstZkeyStoragePath = actions.getZkeyStorageFilePath(prefix, `${prefix}_${actions.genesisZkeyIndex}.zkey`);
|
|
1826
|
+
// Prepare temporary file paths.
|
|
1827
|
+
// (nb. these are needed to download the necessary artifacts for verification from AWS S3).
|
|
1828
|
+
verificationTranscriptTemporaryLocalPath = createTemporaryLocalPath(verificationTranscriptCompleteFilename);
|
|
1829
|
+
const potTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}.pot`);
|
|
1830
|
+
const firstZkeyTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}_genesis.zkey`);
|
|
1831
|
+
const lastZkeyTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}_last.zkey`);
|
|
1832
|
+
// Create and populate transcript.
|
|
1833
|
+
const transcriptLogger = actions.createCustomLoggerForFile(verificationTranscriptTemporaryLocalPath);
|
|
1834
|
+
transcriptLogger.info(`${isFinalizing ? `Final verification` : `Verification`} transcript for ${prefix} circuit Phase 2 contribution.\n${isFinalizing ? `Coordinator ` : `Contributor # ${Number(lastZkeyIndex)}`} (${contributorOrCoordinatorIdentifier})\n`);
|
|
1835
|
+
// Step (1.A.2).
|
|
1836
|
+
await downloadArtifactFromS3Bucket(bucketName, potStoragePath, potTempFilePath);
|
|
1837
|
+
await downloadArtifactFromS3Bucket(bucketName, firstZkeyStoragePath, firstZkeyTempFilePath);
|
|
1838
|
+
await downloadArtifactFromS3Bucket(bucketName, lastZkeyStoragePath, lastZkeyTempFilePath);
|
|
1839
|
+
// Step (1.A.4).
|
|
1840
|
+
isContributionValid = await snarkjs.zKey.verifyFromInit(firstZkeyTempFilePath, potTempFilePath, lastZkeyTempFilePath, transcriptLogger);
|
|
1841
|
+
// Compute contribution hash.
|
|
1842
|
+
lastZkeyBlake2bHash = await actions.blake512FromPath(lastZkeyTempFilePath);
|
|
1843
|
+
// Free resources by unlinking temporary folders.
|
|
1844
|
+
// Do not free-up verification transcript path here.
|
|
1845
|
+
try {
|
|
1846
|
+
fs.unlinkSync(potTempFilePath);
|
|
1847
|
+
fs.unlinkSync(firstZkeyTempFilePath);
|
|
1848
|
+
fs.unlinkSync(lastZkeyTempFilePath);
|
|
1821
1849
|
}
|
|
1850
|
+
catch (error) {
|
|
1851
|
+
printLog(`Error while unlinking temporary files - Error ${error}`, LogLevel.WARN);
|
|
1852
|
+
}
|
|
1853
|
+
await completeVerification();
|
|
1822
1854
|
}
|
|
1823
1855
|
});
|
|
1824
1856
|
/**
|
|
@@ -1827,7 +1859,7 @@ const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB"
|
|
|
1827
1859
|
* this does not happen if the participant is actually the coordinator who is finalizing the ceremony.
|
|
1828
1860
|
*/
|
|
1829
1861
|
const refreshParticipantAfterContributionVerification = functionsV1__namespace
|
|
1830
|
-
.region(
|
|
1862
|
+
.region("europe-west1")
|
|
1831
1863
|
.runWith({
|
|
1832
1864
|
memory: "512MB"
|
|
1833
1865
|
})
|
|
@@ -1888,7 +1920,7 @@ const refreshParticipantAfterContributionVerification = functionsV1__namespace
|
|
|
1888
1920
|
* and verification key extracted from the circuit final contribution (as part of the ceremony finalization process).
|
|
1889
1921
|
*/
|
|
1890
1922
|
const finalizeCircuit = functionsV1__namespace
|
|
1891
|
-
.region(
|
|
1923
|
+
.region("europe-west1")
|
|
1892
1924
|
.runWith({
|
|
1893
1925
|
memory: "512MB"
|
|
1894
1926
|
})
|
|
@@ -2085,8 +2117,10 @@ const createBucket = functions__namespace
|
|
|
2085
2117
|
CORSConfiguration: {
|
|
2086
2118
|
CORSRules: [
|
|
2087
2119
|
{
|
|
2088
|
-
AllowedMethods: ["GET"],
|
|
2089
|
-
AllowedOrigins: ["*"]
|
|
2120
|
+
AllowedMethods: ["GET", "PUT"],
|
|
2121
|
+
AllowedOrigins: ["*"],
|
|
2122
|
+
ExposeHeaders: ["ETag", "Content-Length"],
|
|
2123
|
+
AllowedHeaders: ["*"]
|
|
2090
2124
|
}
|
|
2091
2125
|
]
|
|
2092
2126
|
}
|
|
@@ -2263,7 +2297,8 @@ const startMultiPartUpload = functions__namespace
|
|
|
2263
2297
|
const generatePreSignedUrlsParts = functions__namespace
|
|
2264
2298
|
.region("europe-west1")
|
|
2265
2299
|
.runWith({
|
|
2266
|
-
memory: "512MB"
|
|
2300
|
+
memory: "512MB",
|
|
2301
|
+
timeoutSeconds: 300
|
|
2267
2302
|
})
|
|
2268
2303
|
.https.onCall(async (data, context) => {
|
|
2269
2304
|
if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
|
|
@@ -2413,7 +2448,7 @@ const checkAndRemoveBlockingContributor = functions__namespace
|
|
|
2413
2448
|
// Get ceremony circuits.
|
|
2414
2449
|
const circuits = await getCeremonyCircuits(ceremony.id);
|
|
2415
2450
|
// Extract ceremony data.
|
|
2416
|
-
const { timeoutMechanismType, penalty } = ceremony.data();
|
|
2451
|
+
const { timeoutType: timeoutMechanismType, penalty } = ceremony.data();
|
|
2417
2452
|
for (const circuit of circuits) {
|
|
2418
2453
|
if (!circuit.data())
|
|
2419
2454
|
// Do not use `logAndThrowError` method to avoid the function to exit before checking every ceremony.
|
|
@@ -2563,7 +2598,8 @@ const resumeContributionAfterTimeoutExpiration = functions__namespace
|
|
|
2563
2598
|
if (status === "EXHUMED" /* ParticipantStatus.EXHUMED */)
|
|
2564
2599
|
await participantDoc.ref.update({
|
|
2565
2600
|
status: "READY" /* ParticipantStatus.READY */,
|
|
2566
|
-
lastUpdated: getCurrentServerTimestampInMillis()
|
|
2601
|
+
lastUpdated: getCurrentServerTimestampInMillis(),
|
|
2602
|
+
tempContributionData: {}
|
|
2567
2603
|
});
|
|
2568
2604
|
else
|
|
2569
2605
|
logAndThrowError(SPECIFIC_ERRORS.SE_CONTRIBUTE_CANNOT_PROGRESS_TO_NEXT_CIRCUIT);
|