@devtion/backend 0.0.0-9c50f66 → 0.0.0-9d46256
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 +674 -356
- package/dist/src/functions/index.mjs +676 -360
- package/dist/types/functions/bandada.d.ts +4 -0
- package/dist/types/functions/bandada.d.ts.map +1 -0
- package/dist/types/functions/ceremony.d.ts.map +1 -1
- package/dist/types/functions/circuit.d.ts.map +1 -1
- package/dist/types/functions/index.d.ts +2 -0
- package/dist/types/functions/index.d.ts.map +1 -1
- package/dist/types/functions/siwe.d.ts +4 -0
- package/dist/types/functions/siwe.d.ts.map +1 -0
- 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 +3 -1
- package/dist/types/lib/errors.d.ts.map +1 -1
- package/dist/types/lib/services.d.ts +7 -0
- package/dist/types/lib/services.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 +57 -1
- package/dist/types/types/index.d.ts.map +1 -1
- package/package.json +4 -3
- package/src/functions/bandada.ts +154 -0
- package/src/functions/ceremony.ts +19 -10
- package/src/functions/circuit.ts +444 -391
- package/src/functions/index.ts +2 -0
- package/src/functions/participant.ts +16 -16
- package/src/functions/siwe.ts +77 -0
- package/src/functions/storage.ts +13 -9
- package/src/functions/timeout.ts +17 -15
- package/src/functions/user.ts +23 -13
- package/src/lib/errors.ts +11 -1
- package/src/lib/services.ts +36 -0
- package/src/lib/utils.ts +11 -9
- package/src/types/declarations.d.ts +1 -0
- package/src/types/index.ts +61 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module @p0tion/backend
|
|
3
|
-
* @version 1.
|
|
3
|
+
* @version 1.2.8
|
|
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,
|
|
12
|
+
import { getCircuitsCollectionPath, getTimeoutsCollectionPath, commonTerms, finalContributionIndex, getContributionsCollectionPath, githubReputation, getBucketName, vmBootstrapCommand, vmDependenciesAndCacheArtifactsCommand, vmBootstrapScriptFilename, computeDiskSizeForVM, createEC2Instance, getParticipantsCollectionPath, terminateEC2Instance, formatZkeyIndex, getTranscriptStorageFilePath, getZkeyStorageFilePath, retrieveCommandOutput, blake512FromPath, stopEC2Instance, startEC2Instance, vmContributionVerificationCommand, runCommandUsingSSM, getPotStorageFilePath, genesisZkeyIndex, createCustomLoggerForFile, getVerificationKeyStorageFilePath, getVerifierContractStorageFilePath, computeSHA256ToHex, checkIfRunning, verificationKeyAcronym, verifierSmartContractAcronym, retrieveCommandStatus } from '@p0tion/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,16 +19,19 @@ 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 } from 'timers/promises';
|
|
22
|
+
import { setTimeout as setTimeout$1 } from 'timers/promises';
|
|
23
23
|
import fetch from '@adobe/node-fetch-retry';
|
|
24
24
|
import path from 'path';
|
|
25
25
|
import os from 'os';
|
|
26
26
|
import { SSMClient, CommandInvocationStatus } from '@aws-sdk/client-ssm';
|
|
27
27
|
import { EC2Client } from '@aws-sdk/client-ec2';
|
|
28
|
+
import ethers from 'ethers';
|
|
28
29
|
import * as functionsV1 from 'firebase-functions/v1';
|
|
29
30
|
import * as functionsV2 from 'firebase-functions/v2';
|
|
30
31
|
import { Timer } from 'timer-node';
|
|
31
|
-
import { zKey } from 'snarkjs';
|
|
32
|
+
import { zKey, groth16 } from 'snarkjs';
|
|
33
|
+
import { ApiSdk } from '@bandada/api-sdk';
|
|
34
|
+
import { getAuth } from 'firebase-admin/auth';
|
|
32
35
|
|
|
33
36
|
/**
|
|
34
37
|
* Log levels.
|
|
@@ -49,7 +52,7 @@ var LogLevel;
|
|
|
49
52
|
* @notice the set of Firebase Functions status codes. The codes are the same at the
|
|
50
53
|
* ones exposed by {@link https://github.com/grpc/grpc/blob/master/doc/statuscodes.md | gRPC}.
|
|
51
54
|
* @param errorCode <FunctionsErrorCode> - the set of possible error codes.
|
|
52
|
-
* @param message <string> - the error
|
|
55
|
+
* @param message <string> - the error message.
|
|
53
56
|
* @param [details] <string> - the details of the error (optional).
|
|
54
57
|
* @returns <HttpsError>
|
|
55
58
|
*/
|
|
@@ -121,7 +124,9 @@ const SPECIFIC_ERRORS = {
|
|
|
121
124
|
SE_VM_FAILED_COMMAND_EXECUTION: makeError("failed-precondition", "VM command execution failed", "Please, contact the coordinator if this error persists."),
|
|
122
125
|
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."),
|
|
123
126
|
SE_VM_CANCELLED_COMMAND_EXECUTION: makeError("cancelled", "VM command execution has been cancelled", "Please, contact the coordinator if this error persists."),
|
|
124
|
-
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.")
|
|
127
|
+
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."),
|
|
128
|
+
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."),
|
|
129
|
+
WRONG_BUCKET_NAME: makeError("invalid-argument", "The provided bucket name is not valid.", "Bucket names must be between 3 and 63 characters long, can only contain lowercase letters, numbers, and hyphens, and must start and end with a letter or number.")
|
|
125
130
|
};
|
|
126
131
|
/**
|
|
127
132
|
* A set of common errors.
|
|
@@ -140,6 +145,8 @@ const COMMON_ERRORS = {
|
|
|
140
145
|
CM_INVALID_COMMAND_EXECUTION: makeError("unknown", "There was an error while executing the command on the VM", "Please, contact the coordinator if the error persists.")
|
|
141
146
|
};
|
|
142
147
|
|
|
148
|
+
dotenv.config();
|
|
149
|
+
let provider;
|
|
143
150
|
/**
|
|
144
151
|
* Return a configured and connected instance of the AWS S3 client.
|
|
145
152
|
* @dev this method check and utilize the environment variables to configure the connection
|
|
@@ -162,6 +169,36 @@ const getS3Client = async () => {
|
|
|
162
169
|
region: process.env.AWS_REGION
|
|
163
170
|
});
|
|
164
171
|
};
|
|
172
|
+
/**
|
|
173
|
+
* Returns a Prvider, connected via a configured JSON URL or else
|
|
174
|
+
* the ethers.js default provider, using configured API keys.
|
|
175
|
+
* @returns <ethers.providers.Provider> An Eth node provider
|
|
176
|
+
*/
|
|
177
|
+
const setEthProvider = () => {
|
|
178
|
+
if (provider)
|
|
179
|
+
return provider;
|
|
180
|
+
console.log(`setting new provider`);
|
|
181
|
+
// Use JSON URL if defined
|
|
182
|
+
// if ((hardhat as any).ethers) {
|
|
183
|
+
// console.log(`using hardhat.ethers provider`)
|
|
184
|
+
// provider = (hardhat as any).ethers.provider
|
|
185
|
+
// } else
|
|
186
|
+
if (process.env.ETH_PROVIDER_JSON_URL) {
|
|
187
|
+
console.log(`JSON URL provider at ${process.env.ETH_PROVIDER_JSON_URL}`);
|
|
188
|
+
provider = new ethers.providers.JsonRpcProvider({
|
|
189
|
+
url: process.env.ETH_PROVIDER_JSON_URL,
|
|
190
|
+
skipFetchSetup: true
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
// Otherwise, connect the default provider with ALchemy, Infura, or both
|
|
195
|
+
provider = ethers.providers.getDefaultProvider("homestead", {
|
|
196
|
+
alchemy: process.env.ETH_PROVIDER_ALCHEMY_API_KEY,
|
|
197
|
+
infura: process.env.ETH_PROVIDER_INFURA_API_KEY
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
return provider;
|
|
201
|
+
};
|
|
165
202
|
|
|
166
203
|
dotenv.config();
|
|
167
204
|
/**
|
|
@@ -191,7 +228,7 @@ const getCurrentServerTimestampInMillis = () => Timestamp.now().toMillis();
|
|
|
191
228
|
* Interrupt the current execution for a specified amount of time.
|
|
192
229
|
* @param ms <number> - the amount of time expressed in milliseconds.
|
|
193
230
|
*/
|
|
194
|
-
const sleep = async (ms) => setTimeout(ms);
|
|
231
|
+
const sleep = async (ms) => setTimeout$1(ms);
|
|
195
232
|
/**
|
|
196
233
|
* Query for ceremony circuits.
|
|
197
234
|
* @notice the order by sequence position is fundamental to maintain parallelism among contributions for different circuits.
|
|
@@ -264,7 +301,7 @@ const queryOpenedCeremonies = async () => {
|
|
|
264
301
|
const getCircuitDocumentByPosition = async (ceremonyId, sequencePosition) => {
|
|
265
302
|
// Query for all ceremony circuits.
|
|
266
303
|
const circuits = await getCeremonyCircuits(ceremonyId);
|
|
267
|
-
// Apply a filter using the sequence
|
|
304
|
+
// Apply a filter using the sequence position.
|
|
268
305
|
const matchedCircuits = circuits.filter((circuit) => circuit.data().sequencePosition === sequencePosition);
|
|
269
306
|
if (matchedCircuits.length !== 1)
|
|
270
307
|
logAndThrowError(COMMON_ERRORS.CM_NO_CIRCUIT_FOR_GIVEN_SEQUENCE_POSITION);
|
|
@@ -305,7 +342,7 @@ const downloadArtifactFromS3Bucket = async (bucketName, objectKey, localFilePath
|
|
|
305
342
|
const writeStream = createWriteStream(localFilePath);
|
|
306
343
|
const streamPipeline = promisify(pipeline);
|
|
307
344
|
await streamPipeline(response.body, writeStream);
|
|
308
|
-
writeStream.on(
|
|
345
|
+
writeStream.on("finish", () => {
|
|
309
346
|
writeStream.end();
|
|
310
347
|
});
|
|
311
348
|
};
|
|
@@ -429,12 +466,14 @@ const htmlEncodeCircuitData = (circuitDocument) => ({
|
|
|
429
466
|
const getGitHubVariables = () => {
|
|
430
467
|
if (!process.env.GITHUB_MINIMUM_FOLLOWERS ||
|
|
431
468
|
!process.env.GITHUB_MINIMUM_FOLLOWING ||
|
|
432
|
-
!process.env.GITHUB_MINIMUM_PUBLIC_REPOS
|
|
469
|
+
!process.env.GITHUB_MINIMUM_PUBLIC_REPOS ||
|
|
470
|
+
!process.env.GITHUB_MINIMUM_AGE)
|
|
433
471
|
logAndThrowError(COMMON_ERRORS.CM_WRONG_CONFIGURATION);
|
|
434
472
|
return {
|
|
435
473
|
minimumFollowers: Number(process.env.GITHUB_MINIMUM_FOLLOWERS),
|
|
436
474
|
minimumFollowing: Number(process.env.GITHUB_MINIMUM_FOLLOWING),
|
|
437
|
-
minimumPublicRepos: Number(process.env.GITHUB_MINIMUM_PUBLIC_REPOS)
|
|
475
|
+
minimumPublicRepos: Number(process.env.GITHUB_MINIMUM_PUBLIC_REPOS),
|
|
476
|
+
minimumAge: Number(process.env.GITHUB_MINIMUM_AGE)
|
|
438
477
|
};
|
|
439
478
|
};
|
|
440
479
|
/**
|
|
@@ -444,7 +483,7 @@ const getGitHubVariables = () => {
|
|
|
444
483
|
const getAWSVariables = () => {
|
|
445
484
|
if (!process.env.AWS_ACCESS_KEY_ID ||
|
|
446
485
|
!process.env.AWS_SECRET_ACCESS_KEY ||
|
|
447
|
-
!process.env.
|
|
486
|
+
!process.env.AWS_INSTANCE_PROFILE_ARN ||
|
|
448
487
|
!process.env.AWS_AMI_ID ||
|
|
449
488
|
!process.env.AWS_SNS_TOPIC_ARN)
|
|
450
489
|
logAndThrowError(COMMON_ERRORS.CM_WRONG_CONFIGURATION);
|
|
@@ -452,7 +491,7 @@ const getAWSVariables = () => {
|
|
|
452
491
|
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
453
492
|
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
|
454
493
|
region: process.env.AWS_REGION || "eu-central-1",
|
|
455
|
-
|
|
494
|
+
instanceProfileArn: process.env.AWS_INSTANCE_PROFILE_ARN,
|
|
456
495
|
amiId: process.env.AWS_AMI_ID,
|
|
457
496
|
snsTopic: process.env.AWS_SNS_TOPIC_ARN
|
|
458
497
|
};
|
|
@@ -498,7 +537,7 @@ dotenv.config();
|
|
|
498
537
|
const registerAuthUser = functions
|
|
499
538
|
.region("europe-west1")
|
|
500
539
|
.runWith({
|
|
501
|
-
memory: "
|
|
540
|
+
memory: "1GB"
|
|
502
541
|
})
|
|
503
542
|
.auth.user()
|
|
504
543
|
.onCreate(async (user) => {
|
|
@@ -530,16 +569,18 @@ const registerAuthUser = functions
|
|
|
530
569
|
email === process.env.CUSTOM_CLAIMS_COORDINATOR_EMAIL_ADDRESS_OR_DOMAIN)) {
|
|
531
570
|
const auth = admin.auth();
|
|
532
571
|
// if provider == github.com let's use our functions to check the user's reputation
|
|
533
|
-
if (user.providerData[0].providerId === "github.com") {
|
|
572
|
+
if (user.providerData.length > 0 && user.providerData[0].providerId === "github.com") {
|
|
534
573
|
const vars = getGitHubVariables();
|
|
535
574
|
// this return true or false
|
|
536
575
|
try {
|
|
537
|
-
const { reputable, avatarUrl: avatarURL } = await githubReputation(user.providerData[0].uid, vars.minimumFollowing, vars.minimumFollowers, vars.minimumPublicRepos);
|
|
576
|
+
const { reputable, avatarUrl: avatarURL } = await githubReputation(user.providerData[0].uid, vars.minimumFollowing, vars.minimumFollowers, vars.minimumPublicRepos, vars.minimumAge);
|
|
538
577
|
if (!reputable) {
|
|
539
578
|
// Delete user
|
|
540
579
|
await auth.deleteUser(user.uid);
|
|
541
580
|
// Throw error
|
|
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
|
|
581
|
+
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
|
|
582
|
+
? user.uid
|
|
583
|
+
: 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.`));
|
|
543
584
|
}
|
|
544
585
|
// store locally
|
|
545
586
|
avatarUrl = avatarURL;
|
|
@@ -554,13 +595,13 @@ const registerAuthUser = functions
|
|
|
554
595
|
}
|
|
555
596
|
// Set document (nb. we refer to providerData[0] because we use Github OAuth provider only).
|
|
556
597
|
// In future releases we might want to loop through the providerData array as we support
|
|
557
|
-
// more providers.
|
|
598
|
+
// more providers.
|
|
558
599
|
await userRef.set({
|
|
559
600
|
name: encodedDisplayName,
|
|
560
601
|
encodedDisplayName,
|
|
561
602
|
// Metadata.
|
|
562
603
|
creationTime,
|
|
563
|
-
lastSignInTime,
|
|
604
|
+
lastSignInTime: lastSignInTime || creationTime,
|
|
564
605
|
// Optional.
|
|
565
606
|
email: email || "",
|
|
566
607
|
emailVerified: emailVerified || false,
|
|
@@ -570,7 +611,7 @@ const registerAuthUser = functions
|
|
|
570
611
|
// we want to create a new collection for the users to store the avatars
|
|
571
612
|
const avatarRef = firestore.collection(commonTerms.collections.avatars.name).doc(uid);
|
|
572
613
|
await avatarRef.set({
|
|
573
|
-
avatarUrl: avatarUrl || ""
|
|
614
|
+
avatarUrl: avatarUrl || ""
|
|
574
615
|
});
|
|
575
616
|
printLog(`Authenticated user document with identifier ${uid} has been correctly stored`, LogLevel.DEBUG);
|
|
576
617
|
printLog(`Authenticated user avatar with identifier ${uid} has been correctly stored`, LogLevel.DEBUG);
|
|
@@ -583,7 +624,7 @@ const registerAuthUser = functions
|
|
|
583
624
|
const processSignUpWithCustomClaims = functions
|
|
584
625
|
.region("europe-west1")
|
|
585
626
|
.runWith({
|
|
586
|
-
memory: "
|
|
627
|
+
memory: "1GB"
|
|
587
628
|
})
|
|
588
629
|
.auth.user()
|
|
589
630
|
.onCreate(async (user) => {
|
|
@@ -624,7 +665,7 @@ dotenv.config();
|
|
|
624
665
|
const startCeremony = functions
|
|
625
666
|
.region("europe-west1")
|
|
626
667
|
.runWith({
|
|
627
|
-
memory: "
|
|
668
|
+
memory: "1GB"
|
|
628
669
|
})
|
|
629
670
|
.pubsub.schedule(`every 30 minutes`)
|
|
630
671
|
.onRun(async () => {
|
|
@@ -646,7 +687,7 @@ const startCeremony = functions
|
|
|
646
687
|
const stopCeremony = functions
|
|
647
688
|
.region("europe-west1")
|
|
648
689
|
.runWith({
|
|
649
|
-
memory: "
|
|
690
|
+
memory: "1GB"
|
|
650
691
|
})
|
|
651
692
|
.pubsub.schedule(`every 30 minutes`)
|
|
652
693
|
.onRun(async () => {
|
|
@@ -668,7 +709,7 @@ const stopCeremony = functions
|
|
|
668
709
|
const setupCeremony = functions
|
|
669
710
|
.region("europe-west1")
|
|
670
711
|
.runWith({
|
|
671
|
-
memory: "
|
|
712
|
+
memory: "1GB"
|
|
672
713
|
})
|
|
673
714
|
.https.onCall(async (data, context) => {
|
|
674
715
|
// Check if the user has the coordinator claim.
|
|
@@ -706,11 +747,13 @@ const setupCeremony = functions
|
|
|
706
747
|
// The VM unique identifier (if any).
|
|
707
748
|
let vmInstanceId = "";
|
|
708
749
|
// Get a new circuit document.
|
|
709
|
-
const
|
|
750
|
+
const ccp = getCircuitsCollectionPath(ceremonyDoc.ref.id);
|
|
751
|
+
printLog(`CircuitsCollectionPath = ${ccp}`, LogLevel.DEBUG);
|
|
752
|
+
const circuitDoc = await firestore.collection(ccp).doc().get();
|
|
710
753
|
// Check if using the VM approach for contribution verification.
|
|
711
754
|
if (circuit.verification.cfOrVm === "VM" /* CircuitContributionVerificationMechanism.VM */) {
|
|
712
755
|
// VM command to be run at the startup.
|
|
713
|
-
const startupCommand = vmBootstrapCommand(bucketName);
|
|
756
|
+
const startupCommand = vmBootstrapCommand(`${bucketName}/circuits/${circuit.name}`);
|
|
714
757
|
// Get EC2 client.
|
|
715
758
|
const ec2Client = await createEC2Client();
|
|
716
759
|
// Get AWS variables.
|
|
@@ -719,7 +762,8 @@ const setupCeremony = functions
|
|
|
719
762
|
const vmCommands = vmDependenciesAndCacheArtifactsCommand(`${bucketName}/${circuit.files?.initialZkeyStoragePath}`, `${bucketName}/${circuit.files?.potStoragePath}`, snsTopic, region);
|
|
720
763
|
printLog(`Check VM dependencies and cache artifacts commands ${vmCommands.join("\n")}`, LogLevel.DEBUG);
|
|
721
764
|
// Upload the post-startup commands script file.
|
|
722
|
-
|
|
765
|
+
printLog(`Uploading VM post-startup commands script file ${vmBootstrapScriptFilename}`, LogLevel.DEBUG);
|
|
766
|
+
await uploadFileToBucketNoFile(bucketName, `circuits/${circuit.name}/${vmBootstrapScriptFilename}`, vmCommands.join("\n"));
|
|
723
767
|
// Compute the VM disk space requirement (in GB).
|
|
724
768
|
const vmDiskSize = computeDiskSizeForVM(circuit.zKeySizeInBytes, circuit.metadata?.pot);
|
|
725
769
|
printLog(`Check VM startup commands ${startupCommand.join("\n")}`, LogLevel.DEBUG);
|
|
@@ -742,12 +786,14 @@ const setupCeremony = functions
|
|
|
742
786
|
}
|
|
743
787
|
// Encode circuit data.
|
|
744
788
|
const encodedCircuit = htmlEncodeCircuitData(circuit);
|
|
789
|
+
printLog(`writing circuit data...`, LogLevel.DEBUG);
|
|
745
790
|
// Prepare tx to write circuit data.
|
|
746
791
|
batch.create(circuitDoc.ref, {
|
|
747
792
|
...encodedCircuit,
|
|
748
793
|
lastUpdated: getCurrentServerTimestampInMillis()
|
|
749
794
|
});
|
|
750
795
|
}
|
|
796
|
+
printLog(`Done handling circuits...`, LogLevel.DEBUG);
|
|
751
797
|
// Send txs in a batch (to avoid race conditions).
|
|
752
798
|
await batch.commit();
|
|
753
799
|
printLog(`Setup completed for ceremony ${ceremonyDoc.id}`, LogLevel.DEBUG);
|
|
@@ -760,7 +806,7 @@ const setupCeremony = functions
|
|
|
760
806
|
const initEmptyWaitingQueueForCircuit = functions
|
|
761
807
|
.region("europe-west1")
|
|
762
808
|
.runWith({
|
|
763
|
-
memory: "
|
|
809
|
+
memory: "1GB"
|
|
764
810
|
})
|
|
765
811
|
.firestore.document(`/${commonTerms.collections.ceremonies.name}/{ceremony}/${commonTerms.collections.circuits.name}/{circuit}`)
|
|
766
812
|
.onCreate(async (doc) => {
|
|
@@ -792,7 +838,7 @@ const initEmptyWaitingQueueForCircuit = functions
|
|
|
792
838
|
const finalizeCeremony = functions
|
|
793
839
|
.region("europe-west1")
|
|
794
840
|
.runWith({
|
|
795
|
-
memory: "
|
|
841
|
+
memory: "1GB"
|
|
796
842
|
})
|
|
797
843
|
.https.onCall(async (data, context) => {
|
|
798
844
|
if (!context.auth || !context.auth.token.coordinator)
|
|
@@ -813,7 +859,7 @@ const finalizeCeremony = functions
|
|
|
813
859
|
// Get ceremony circuits.
|
|
814
860
|
const circuits = await getCeremonyCircuits(ceremonyId);
|
|
815
861
|
// Get final contribution for each circuit.
|
|
816
|
-
// nb. the `getFinalContributionDocument` checks the
|
|
862
|
+
// nb. the `getFinalContributionDocument` checks the existence of the final contribution document (if not present, throws).
|
|
817
863
|
// Therefore, we just need to call the method without taking any data to verify the pre-condition of having already computed
|
|
818
864
|
// the final contributions for each ceremony circuit.
|
|
819
865
|
for await (const circuit of circuits)
|
|
@@ -866,9 +912,9 @@ dotenv.config();
|
|
|
866
912
|
* @dev true when the participant can participate (1.A, 3.B, 1.D); otherwise false.
|
|
867
913
|
*/
|
|
868
914
|
const checkParticipantForCeremony = functions
|
|
869
|
-
.region(
|
|
915
|
+
.region("europe-west1")
|
|
870
916
|
.runWith({
|
|
871
|
-
memory: "
|
|
917
|
+
memory: "1GB"
|
|
872
918
|
})
|
|
873
919
|
.https.onCall(async (data, context) => {
|
|
874
920
|
if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
|
|
@@ -937,7 +983,7 @@ const checkParticipantForCeremony = functions
|
|
|
937
983
|
participantDoc.ref.update({
|
|
938
984
|
status: "EXHUMED" /* ParticipantStatus.EXHUMED */,
|
|
939
985
|
contributions,
|
|
940
|
-
tempContributionData: tempContributionData
|
|
986
|
+
tempContributionData: tempContributionData || FieldValue.delete(),
|
|
941
987
|
contributionStep: "DOWNLOADING" /* ParticipantContributionStep.DOWNLOADING */,
|
|
942
988
|
contributionStartedAt: 0,
|
|
943
989
|
verificationStartedAt: FieldValue.delete(),
|
|
@@ -970,9 +1016,9 @@ const checkParticipantForCeremony = functions
|
|
|
970
1016
|
* 2) the participant has just finished the contribution for a circuit (contributionProgress != 0 && status = CONTRIBUTED && contributionStep = COMPLETED).
|
|
971
1017
|
*/
|
|
972
1018
|
const progressToNextCircuitForContribution = functions
|
|
973
|
-
.region(
|
|
1019
|
+
.region("europe-west1")
|
|
974
1020
|
.runWith({
|
|
975
|
-
memory: "
|
|
1021
|
+
memory: "1GB"
|
|
976
1022
|
})
|
|
977
1023
|
.https.onCall(async (data, context) => {
|
|
978
1024
|
if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
|
|
@@ -1017,9 +1063,9 @@ const progressToNextCircuitForContribution = functions
|
|
|
1017
1063
|
* 5) Completed contribution computation and verification.
|
|
1018
1064
|
*/
|
|
1019
1065
|
const progressToNextContributionStep = functions
|
|
1020
|
-
.region(
|
|
1066
|
+
.region("europe-west1")
|
|
1021
1067
|
.runWith({
|
|
1022
|
-
memory: "
|
|
1068
|
+
memory: "1GB"
|
|
1023
1069
|
})
|
|
1024
1070
|
.https.onCall(async (data, context) => {
|
|
1025
1071
|
if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
|
|
@@ -1068,9 +1114,9 @@ const progressToNextContributionStep = functions
|
|
|
1068
1114
|
* @dev enable the current contributor to resume a contribution from where it had left off.
|
|
1069
1115
|
*/
|
|
1070
1116
|
const permanentlyStoreCurrentContributionTimeAndHash = functions
|
|
1071
|
-
.region(
|
|
1117
|
+
.region("europe-west1")
|
|
1072
1118
|
.runWith({
|
|
1073
|
-
memory: "
|
|
1119
|
+
memory: "1GB"
|
|
1074
1120
|
})
|
|
1075
1121
|
.https.onCall(async (data, context) => {
|
|
1076
1122
|
if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
|
|
@@ -1110,9 +1156,9 @@ const permanentlyStoreCurrentContributionTimeAndHash = functions
|
|
|
1110
1156
|
* @dev enable the current contributor to resume a multi-part upload from where it had left off.
|
|
1111
1157
|
*/
|
|
1112
1158
|
const temporaryStoreCurrentContributionMultiPartUploadId = functions
|
|
1113
|
-
.region(
|
|
1159
|
+
.region("europe-west1")
|
|
1114
1160
|
.runWith({
|
|
1115
|
-
memory: "
|
|
1161
|
+
memory: "1GB"
|
|
1116
1162
|
})
|
|
1117
1163
|
.https.onCall(async (data, context) => {
|
|
1118
1164
|
if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
|
|
@@ -1148,9 +1194,9 @@ const temporaryStoreCurrentContributionMultiPartUploadId = functions
|
|
|
1148
1194
|
* @dev enable the current contributor to resume a multi-part upload from where it had left off.
|
|
1149
1195
|
*/
|
|
1150
1196
|
const temporaryStoreCurrentContributionUploadedChunkData = functions
|
|
1151
|
-
.region(
|
|
1197
|
+
.region("europe-west1")
|
|
1152
1198
|
.runWith({
|
|
1153
|
-
memory: "
|
|
1199
|
+
memory: "1GB"
|
|
1154
1200
|
})
|
|
1155
1201
|
.https.onCall(async (data, context) => {
|
|
1156
1202
|
if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
|
|
@@ -1190,9 +1236,9 @@ const temporaryStoreCurrentContributionUploadedChunkData = functions
|
|
|
1190
1236
|
* contributed to every selected ceremony circuits (= DONE).
|
|
1191
1237
|
*/
|
|
1192
1238
|
const checkAndPrepareCoordinatorForFinalization = functions
|
|
1193
|
-
.region(
|
|
1239
|
+
.region("europe-west1")
|
|
1194
1240
|
.runWith({
|
|
1195
|
-
memory: "
|
|
1241
|
+
memory: "1GB"
|
|
1196
1242
|
})
|
|
1197
1243
|
.https.onCall(async (data, context) => {
|
|
1198
1244
|
if (!context.auth || !context.auth.token.coordinator)
|
|
@@ -1269,7 +1315,7 @@ const coordinate = async (participant, circuit, isSingleParticipantCoordination,
|
|
|
1269
1315
|
if (isSingleParticipantCoordination) {
|
|
1270
1316
|
// Scenario (A).
|
|
1271
1317
|
if (emptyWaitingQueue) {
|
|
1272
|
-
printLog(`Coordinate - executing scenario A - emptyWaitingQueue`, LogLevel.
|
|
1318
|
+
printLog(`Coordinate - executing scenario A - emptyWaitingQueue`, LogLevel.INFO);
|
|
1273
1319
|
// Update.
|
|
1274
1320
|
newCurrentContributorId = participant.id;
|
|
1275
1321
|
newParticipantStatus = "CONTRIBUTING" /* ParticipantStatus.CONTRIBUTING */;
|
|
@@ -1278,14 +1324,14 @@ const coordinate = async (participant, circuit, isSingleParticipantCoordination,
|
|
|
1278
1324
|
}
|
|
1279
1325
|
// Scenario (A).
|
|
1280
1326
|
else if (participantResumingAfterTimeoutExpiration) {
|
|
1281
|
-
printLog(`Coordinate - executing scenario A - single - participantResumingAfterTimeoutExpiration`, LogLevel.
|
|
1327
|
+
printLog(`Coordinate - executing scenario A - single - participantResumingAfterTimeoutExpiration`, LogLevel.INFO);
|
|
1282
1328
|
newParticipantStatus = "CONTRIBUTING" /* ParticipantStatus.CONTRIBUTING */;
|
|
1283
1329
|
newContributionStep = "DOWNLOADING" /* ParticipantContributionStep.DOWNLOADING */;
|
|
1284
1330
|
newCurrentContributorId = participant.id;
|
|
1285
1331
|
}
|
|
1286
1332
|
// Scenario (B).
|
|
1287
1333
|
else if (participantIsNotCurrentContributor) {
|
|
1288
|
-
printLog(`Coordinate - executing scenario B - single - participantIsNotCurrentContributor`, LogLevel.
|
|
1334
|
+
printLog(`Coordinate - executing scenario B - single - participantIsNotCurrentContributor`, LogLevel.INFO);
|
|
1289
1335
|
newCurrentContributorId = currentContributor;
|
|
1290
1336
|
newParticipantStatus = "WAITING" /* ParticipantStatus.WAITING */;
|
|
1291
1337
|
newContributors.push(participant.id);
|
|
@@ -1304,7 +1350,7 @@ const coordinate = async (participant, circuit, isSingleParticipantCoordination,
|
|
|
1304
1350
|
});
|
|
1305
1351
|
}
|
|
1306
1352
|
else if (participantIsCurrentContributor && participantCompletedOneOrAllContributions && !!ceremonyId) {
|
|
1307
|
-
printLog(`Coordinate - executing scenario C - multi - participantIsCurrentContributor && participantCompletedOneOrAllContributions`, LogLevel.
|
|
1353
|
+
printLog(`Coordinate - executing scenario C - multi - participantIsCurrentContributor && participantCompletedOneOrAllContributions`, LogLevel.INFO);
|
|
1308
1354
|
newParticipantStatus = "CONTRIBUTING" /* ParticipantStatus.CONTRIBUTING */;
|
|
1309
1355
|
newContributionStep = "DOWNLOADING" /* ParticipantContributionStep.DOWNLOADING */;
|
|
1310
1356
|
// Remove from waiting queue of circuit X.
|
|
@@ -1322,7 +1368,7 @@ const coordinate = async (participant, circuit, isSingleParticipantCoordination,
|
|
|
1322
1368
|
contributionStartedAt: getCurrentServerTimestampInMillis(),
|
|
1323
1369
|
lastUpdated: getCurrentServerTimestampInMillis()
|
|
1324
1370
|
});
|
|
1325
|
-
printLog(`Participant ${newCurrentContributorId} is the new current contributor for circuit ${circuit.id}`, LogLevel.
|
|
1371
|
+
printLog(`Participant ${newCurrentContributorId} is the new current contributor for circuit ${circuit.id}`, LogLevel.INFO);
|
|
1326
1372
|
}
|
|
1327
1373
|
}
|
|
1328
1374
|
// Prepare tx - must be done for all Scenarios.
|
|
@@ -1342,54 +1388,74 @@ const coordinate = async (participant, circuit, isSingleParticipantCoordination,
|
|
|
1342
1388
|
* Wait until the command has completed its execution inside the VM.
|
|
1343
1389
|
* @dev this method implements a custom interval to check 5 times after 1 minute if the command execution
|
|
1344
1390
|
* has been completed or not by calling the `retrieveCommandStatus` method.
|
|
1345
|
-
* @param {any} resolve the promise.
|
|
1346
|
-
* @param {any} reject the promise.
|
|
1347
1391
|
* @param {SSMClient} ssm the SSM client.
|
|
1348
1392
|
* @param {string} vmInstanceId the unique identifier of the VM instance.
|
|
1349
1393
|
* @param {string} commandId the unique identifier of the VM command.
|
|
1350
1394
|
* @returns <Promise<void>> true when the command execution succeed; otherwise false.
|
|
1351
1395
|
*/
|
|
1352
|
-
const waitForVMCommandExecution = (
|
|
1353
|
-
const
|
|
1396
|
+
const waitForVMCommandExecution = (ssm, vmInstanceId, commandId) => new Promise((resolve, reject) => {
|
|
1397
|
+
const poll = async () => {
|
|
1354
1398
|
try {
|
|
1355
1399
|
// Get command status.
|
|
1356
1400
|
const cmdStatus = await retrieveCommandStatus(ssm, vmInstanceId, commandId);
|
|
1357
1401
|
printLog(`Checking command ${commandId} status => ${cmdStatus}`, LogLevel.DEBUG);
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1402
|
+
let error;
|
|
1403
|
+
switch (cmdStatus) {
|
|
1404
|
+
case CommandInvocationStatus.CANCELLING:
|
|
1405
|
+
case CommandInvocationStatus.CANCELLED: {
|
|
1406
|
+
error = SPECIFIC_ERRORS.SE_VM_CANCELLED_COMMAND_EXECUTION;
|
|
1407
|
+
break;
|
|
1408
|
+
}
|
|
1409
|
+
case CommandInvocationStatus.DELAYED: {
|
|
1410
|
+
error = SPECIFIC_ERRORS.SE_VM_DELAYED_COMMAND_EXECUTION;
|
|
1411
|
+
break;
|
|
1412
|
+
}
|
|
1413
|
+
case CommandInvocationStatus.FAILED: {
|
|
1414
|
+
error = SPECIFIC_ERRORS.SE_VM_FAILED_COMMAND_EXECUTION;
|
|
1415
|
+
break;
|
|
1416
|
+
}
|
|
1417
|
+
case CommandInvocationStatus.TIMED_OUT: {
|
|
1418
|
+
error = SPECIFIC_ERRORS.SE_VM_TIMEDOUT_COMMAND_EXECUTION;
|
|
1419
|
+
break;
|
|
1420
|
+
}
|
|
1421
|
+
case CommandInvocationStatus.IN_PROGRESS:
|
|
1422
|
+
case CommandInvocationStatus.PENDING: {
|
|
1423
|
+
// wait a minute and poll again
|
|
1424
|
+
setTimeout(poll, 60000);
|
|
1425
|
+
return;
|
|
1426
|
+
}
|
|
1427
|
+
case CommandInvocationStatus.SUCCESS: {
|
|
1428
|
+
printLog(`Command ${commandId} successfully completed`, LogLevel.DEBUG);
|
|
1429
|
+
// Resolve the promise.
|
|
1430
|
+
resolve();
|
|
1431
|
+
return;
|
|
1432
|
+
}
|
|
1433
|
+
default: {
|
|
1434
|
+
logAndThrowError(SPECIFIC_ERRORS.SE_VM_UNKNOWN_COMMAND_STATUS);
|
|
1435
|
+
}
|
|
1374
1436
|
}
|
|
1375
|
-
|
|
1376
|
-
logAndThrowError(
|
|
1377
|
-
reject();
|
|
1437
|
+
if (error) {
|
|
1438
|
+
logAndThrowError(error);
|
|
1378
1439
|
}
|
|
1379
1440
|
}
|
|
1380
1441
|
catch (error) {
|
|
1381
1442
|
printLog(`Invalid command ${commandId} execution`, LogLevel.DEBUG);
|
|
1443
|
+
const ec2 = await createEC2Client();
|
|
1444
|
+
// if it errors out, let's just log it as a warning so the coordinator is aware
|
|
1445
|
+
try {
|
|
1446
|
+
await stopEC2Instance(ec2, vmInstanceId);
|
|
1447
|
+
}
|
|
1448
|
+
catch (stopError) {
|
|
1449
|
+
printLog(`Error while stopping VM instance ${vmInstanceId} - Error ${stopError}`, LogLevel.WARN);
|
|
1450
|
+
}
|
|
1382
1451
|
if (!error.toString().includes(commandId))
|
|
1383
1452
|
logAndThrowError(COMMON_ERRORS.CM_INVALID_COMMAND_EXECUTION);
|
|
1384
1453
|
// Reject the promise.
|
|
1385
1454
|
reject();
|
|
1386
1455
|
}
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
}
|
|
1391
|
-
}, 60000); // 1 minute.
|
|
1392
|
-
};
|
|
1456
|
+
};
|
|
1457
|
+
setTimeout(poll, 60000);
|
|
1458
|
+
});
|
|
1393
1459
|
/**
|
|
1394
1460
|
* This method is used to coordinate the waiting queues of ceremony circuits.
|
|
1395
1461
|
* @dev this cloud function is triggered whenever an update of a document related to a participant of a ceremony occurs.
|
|
@@ -1410,9 +1476,9 @@ const waitForVMCommandExecution = (resolve, reject, ssm, vmInstanceId, commandId
|
|
|
1410
1476
|
* - Just completed a contribution or all contributions for each circuit. If yes, coordinate (multi-participant scenario).
|
|
1411
1477
|
*/
|
|
1412
1478
|
const coordinateCeremonyParticipant = functionsV1
|
|
1413
|
-
.region(
|
|
1479
|
+
.region("europe-west1")
|
|
1414
1480
|
.runWith({
|
|
1415
|
-
memory: "
|
|
1481
|
+
memory: "1GB"
|
|
1416
1482
|
})
|
|
1417
1483
|
.firestore.document(`${commonTerms.collections.ceremonies.name}/{ceremonyId}/${commonTerms.collections.participants.name}/{participantId}`)
|
|
1418
1484
|
.onUpdate(async (participantChanges) => {
|
|
@@ -1428,8 +1494,8 @@ const coordinateCeremonyParticipant = functionsV1
|
|
|
1428
1494
|
// Extract data.
|
|
1429
1495
|
const { contributionProgress: prevContributionProgress, status: prevStatus, contributionStep: prevContributionStep } = exParticipant.data();
|
|
1430
1496
|
const { contributionProgress: changedContributionProgress, status: changedStatus, contributionStep: changedContributionStep } = changedParticipant.data();
|
|
1431
|
-
printLog(`Coordinate participant ${exParticipant.id} for ceremony ${ceremonyId}`, LogLevel.
|
|
1432
|
-
printLog(`Participant status: ${prevStatus} => ${changedStatus} - Participant contribution step: ${prevContributionStep} => ${changedContributionStep}`, LogLevel.
|
|
1497
|
+
printLog(`Coordinate participant ${exParticipant.id} for ceremony ${ceremonyId}`, LogLevel.INFO);
|
|
1498
|
+
printLog(`Participant status: ${prevStatus} => ${changedStatus} - Participant contribution step: ${prevContributionStep} => ${changedContributionStep}`, LogLevel.INFO);
|
|
1433
1499
|
// Define pre-conditions.
|
|
1434
1500
|
const participantReadyToContribute = changedStatus === "READY" /* ParticipantStatus.READY */;
|
|
1435
1501
|
const participantReadyForFirstContribution = participantReadyToContribute && prevContributionProgress === 0;
|
|
@@ -1439,8 +1505,8 @@ const coordinateCeremonyParticipant = functionsV1
|
|
|
1439
1505
|
prevContributionProgress !== 0;
|
|
1440
1506
|
const participantCompletedEveryCircuitContribution = changedStatus === "DONE" /* ParticipantStatus.DONE */ && prevStatus !== "DONE" /* ParticipantStatus.DONE */;
|
|
1441
1507
|
const participantCompletedContribution = prevContributionProgress === changedContributionProgress &&
|
|
1442
|
-
prevStatus === "CONTRIBUTING" /* ParticipantStatus.CONTRIBUTING */
|
|
1443
|
-
|
|
1508
|
+
(prevStatus === "CONTRIBUTING" /* ParticipantStatus.CONTRIBUTING */ ||
|
|
1509
|
+
prevContributionStep === "VERIFYING" /* ParticipantContributionStep.VERIFYING */) &&
|
|
1444
1510
|
changedStatus === "CONTRIBUTED" /* ParticipantStatus.CONTRIBUTED */ &&
|
|
1445
1511
|
changedContributionStep === "COMPLETED" /* ParticipantContributionStep.COMPLETED */;
|
|
1446
1512
|
// Step (2).
|
|
@@ -1448,7 +1514,7 @@ const coordinateCeremonyParticipant = functionsV1
|
|
|
1448
1514
|
participantResumingContributionAfterTimeout ||
|
|
1449
1515
|
participantReadyForNextContribution) {
|
|
1450
1516
|
// Step (2.A).
|
|
1451
|
-
printLog(`Participant is ready for first contribution (${participantReadyForFirstContribution}) or for the next contribution (${participantReadyForNextContribution}) or is resuming after a timeout expiration (${participantResumingContributionAfterTimeout})`, LogLevel.
|
|
1517
|
+
printLog(`Participant is ready for first contribution (${participantReadyForFirstContribution}) or for the next contribution (${participantReadyForNextContribution}) or is resuming after a timeout expiration (${participantResumingContributionAfterTimeout})`, LogLevel.INFO);
|
|
1452
1518
|
// Get the circuit.
|
|
1453
1519
|
const circuit = await getCircuitDocumentByPosition(ceremonyId, changedContributionProgress);
|
|
1454
1520
|
// Coordinate.
|
|
@@ -1457,7 +1523,7 @@ const coordinateCeremonyParticipant = functionsV1
|
|
|
1457
1523
|
}
|
|
1458
1524
|
else if (participantCompletedContribution || participantCompletedEveryCircuitContribution) {
|
|
1459
1525
|
// Step (2.B).
|
|
1460
|
-
printLog(`Participant completed a contribution (${participantCompletedContribution}) or every contribution for each circuit (${participantCompletedEveryCircuitContribution})`, LogLevel.
|
|
1526
|
+
printLog(`Participant completed a contribution (${participantCompletedContribution}) or every contribution for each circuit (${participantCompletedEveryCircuitContribution})`, LogLevel.INFO);
|
|
1461
1527
|
// Get the circuit.
|
|
1462
1528
|
const circuit = await getCircuitDocumentByPosition(ceremonyId, prevContributionProgress);
|
|
1463
1529
|
// Coordinate.
|
|
@@ -1481,11 +1547,9 @@ const checkIfVMRunning = async (ec2, vmInstanceId, attempts = 5) => {
|
|
|
1481
1547
|
const isVMRunning = await checkIfRunning(ec2, vmInstanceId);
|
|
1482
1548
|
if (!isVMRunning) {
|
|
1483
1549
|
printLog(`VM not running, ${attempts - 1} attempts remaining. Retrying in 1 minute...`, LogLevel.DEBUG);
|
|
1484
|
-
return
|
|
1485
|
-
}
|
|
1486
|
-
else {
|
|
1487
|
-
return true;
|
|
1550
|
+
return checkIfVMRunning(ec2, vmInstanceId, attempts - 1);
|
|
1488
1551
|
}
|
|
1552
|
+
return true;
|
|
1489
1553
|
};
|
|
1490
1554
|
/**
|
|
1491
1555
|
* Verify the contribution of a participant computed while contributing to a specific circuit of a ceremony.
|
|
@@ -1513,266 +1577,296 @@ const checkIfVMRunning = async (ec2, vmInstanceId, attempts = 5) => {
|
|
|
1513
1577
|
* 1.A.4.C.1) If true, update circuit waiting for queue and average timings accordingly to contribution verification results;
|
|
1514
1578
|
* 2) Send all updates atomically to the Firestore database.
|
|
1515
1579
|
*/
|
|
1516
|
-
const verifycontribution = functionsV2.https.onCall({ memory: "
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
!request.data.
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
!process.env.
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
//
|
|
1587
|
-
|
|
1588
|
-
verificationTaskTimer.
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
// Download from bucket.
|
|
1595
|
-
// nb. the transcript MUST be uploaded from the VM by verification commands.
|
|
1596
|
-
await downloadArtifactFromS3Bucket(bucketName, verificationTranscriptStoragePathAndFilename, verificationTranscriptTemporaryLocalPath);
|
|
1597
|
-
// Read the verification trascript and validate data by checking for core info ("ZKey Ok!").
|
|
1598
|
-
const content = fs.readFileSync(verificationTranscriptTemporaryLocalPath, "utf-8");
|
|
1599
|
-
if (content.includes("ZKey Ok!"))
|
|
1600
|
-
isContributionValid = true;
|
|
1601
|
-
// If the contribution is valid, then format and store the trascript.
|
|
1602
|
-
if (isContributionValid) {
|
|
1603
|
-
// eslint-disable-next-line no-control-regex
|
|
1604
|
-
const updated = content.replace(/\x1b[[0-9;]*m/g, "");
|
|
1605
|
-
fs.writeFileSync(verificationTranscriptTemporaryLocalPath, updated);
|
|
1580
|
+
const verifycontribution = functionsV2.https.onCall({ memory: "32GiB", timeoutSeconds: 3600, region: "europe-west1", cpu: 8 }, async (request) => {
|
|
1581
|
+
try {
|
|
1582
|
+
if (!request.auth || (!request.auth.token.participant && !request.auth.token.coordinator))
|
|
1583
|
+
logAndThrowError(SPECIFIC_ERRORS.SE_AUTH_NO_CURRENT_AUTH_USER);
|
|
1584
|
+
if (!request.data.ceremonyId ||
|
|
1585
|
+
!request.data.circuitId ||
|
|
1586
|
+
!request.data.contributorOrCoordinatorIdentifier ||
|
|
1587
|
+
!request.data.bucketName)
|
|
1588
|
+
logAndThrowError(COMMON_ERRORS.CM_MISSING_OR_WRONG_INPUT_DATA);
|
|
1589
|
+
if (!process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_NAME ||
|
|
1590
|
+
!process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_VERSION ||
|
|
1591
|
+
!process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_COMMIT_HASH)
|
|
1592
|
+
logAndThrowError(COMMON_ERRORS.CM_WRONG_CONFIGURATION);
|
|
1593
|
+
const BUCKET_NAME_REGEX = /^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$/;
|
|
1594
|
+
if (!BUCKET_NAME_REGEX.test(request.data.bucketName))
|
|
1595
|
+
logAndThrowError(SPECIFIC_ERRORS.WRONG_BUCKET_NAME);
|
|
1596
|
+
// Step (0).
|
|
1597
|
+
// Prepare and start timer.
|
|
1598
|
+
const verifyContributionTimer = new Timer({ label: commonTerms.cloudFunctionsNames.verifyContribution });
|
|
1599
|
+
verifyContributionTimer.start();
|
|
1600
|
+
// Get DB.
|
|
1601
|
+
const firestore = admin.firestore();
|
|
1602
|
+
// Prepare batch of txs.
|
|
1603
|
+
const batch = firestore.batch();
|
|
1604
|
+
// Extract data.
|
|
1605
|
+
const { ceremonyId, circuitId, contributorOrCoordinatorIdentifier, bucketName } = request.data;
|
|
1606
|
+
const userId = request.auth?.uid;
|
|
1607
|
+
// Look for the ceremony, circuit and participant document.
|
|
1608
|
+
const ceremonyDoc = await getDocumentById(commonTerms.collections.ceremonies.name, ceremonyId);
|
|
1609
|
+
const circuitDoc = await getDocumentById(getCircuitsCollectionPath(ceremonyId), circuitId);
|
|
1610
|
+
const participantDoc = await getDocumentById(getParticipantsCollectionPath(ceremonyId), userId);
|
|
1611
|
+
if (!ceremonyDoc.data() || !circuitDoc.data() || !participantDoc.data())
|
|
1612
|
+
logAndThrowError(COMMON_ERRORS.CM_INEXISTENT_DOCUMENT_DATA);
|
|
1613
|
+
// Extract documents data.
|
|
1614
|
+
const { state } = ceremonyDoc.data();
|
|
1615
|
+
const { status, contributions, verificationStartedAt, contributionStartedAt } = participantDoc.data();
|
|
1616
|
+
const { waitingQueue, prefix, avgTimings, verification, files } = circuitDoc.data();
|
|
1617
|
+
const { completedContributions, failedContributions } = waitingQueue;
|
|
1618
|
+
const { contributionComputation: avgContributionComputationTime, fullContribution: avgFullContributionTime, verifyCloudFunction: avgVerifyCloudFunctionTime } = avgTimings;
|
|
1619
|
+
const { cfOrVm, vm } = verification;
|
|
1620
|
+
// we might not have it if the circuit is not using VM.
|
|
1621
|
+
let vmInstanceId = "";
|
|
1622
|
+
if (vm)
|
|
1623
|
+
vmInstanceId = vm.vmInstanceId;
|
|
1624
|
+
// Define pre-conditions.
|
|
1625
|
+
const isFinalizing = state === "CLOSED" /* CeremonyState.CLOSED */ && request.auth && request.auth.token.coordinator; // true only when the coordinator verifies the final contributions.
|
|
1626
|
+
const isContributing = status === "CONTRIBUTING" /* ParticipantStatus.CONTRIBUTING */;
|
|
1627
|
+
const isUsingVM = cfOrVm === "VM" /* CircuitContributionVerificationMechanism.VM */ && !!vmInstanceId;
|
|
1628
|
+
// Prepare state.
|
|
1629
|
+
let isContributionValid = false;
|
|
1630
|
+
let verifyCloudFunctionExecutionTime = 0; // time spent while executing the verify contribution cloud function.
|
|
1631
|
+
let verifyCloudFunctionTime = 0; // time spent while executing the core business logic of this cloud function.
|
|
1632
|
+
let fullContributionTime = 0; // time spent while doing non-verification contributions tasks (download, compute, upload).
|
|
1633
|
+
let contributionComputationTime = 0; // time spent while computing the contribution.
|
|
1634
|
+
let lastZkeyBlake2bHash = ""; // the Blake2B hash of the last zKey.
|
|
1635
|
+
let verificationTranscriptTemporaryLocalPath = ""; // the local temporary path for the verification transcript.
|
|
1636
|
+
let transcriptBlake2bHash = ""; // the Blake2B hash of the verification transcript.
|
|
1637
|
+
let commandId = ""; // the unique identifier of the VM command.
|
|
1638
|
+
// Derive necessary data.
|
|
1639
|
+
const lastZkeyIndex = formatZkeyIndex(completedContributions + 1);
|
|
1640
|
+
const verificationTranscriptCompleteFilename = `${prefix}_${isFinalizing
|
|
1641
|
+
? `${contributorOrCoordinatorIdentifier}_${finalContributionIndex}_verification_transcript.log`
|
|
1642
|
+
: `${lastZkeyIndex}_${contributorOrCoordinatorIdentifier}_verification_transcript.log`}`;
|
|
1643
|
+
const lastZkeyFilename = `${prefix}_${isFinalizing ? finalContributionIndex : lastZkeyIndex}.zkey`;
|
|
1644
|
+
// Prepare state for VM verification (if needed).
|
|
1645
|
+
const ec2 = await createEC2Client();
|
|
1646
|
+
const ssm = await createSSMClient();
|
|
1647
|
+
// Step (1.A.1).
|
|
1648
|
+
// Get storage paths.
|
|
1649
|
+
const verificationTranscriptStoragePathAndFilename = getTranscriptStorageFilePath(prefix, verificationTranscriptCompleteFilename);
|
|
1650
|
+
// the zKey storage path is required to be sent to the VM api
|
|
1651
|
+
const lastZkeyStoragePath = getZkeyStorageFilePath(prefix, `${prefix}_${isFinalizing ? finalContributionIndex : lastZkeyIndex}.zkey`);
|
|
1652
|
+
const verificationTaskTimer = new Timer({ label: `${ceremonyId}-${circuitId}-${participantDoc.id}` });
|
|
1653
|
+
const dumpLog = async (path) => {
|
|
1654
|
+
printLog(`transcript >>>>>>`, LogLevel.DEBUG);
|
|
1655
|
+
try {
|
|
1656
|
+
const data = await fs.promises.readFile(path, "utf8");
|
|
1657
|
+
printLog(data, LogLevel.DEBUG);
|
|
1606
1658
|
}
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
.
|
|
1613
|
-
.
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
// Sleep ~3 seconds to wait for verification transcription.
|
|
1617
|
-
await sleep(3000);
|
|
1618
|
-
// Step (1.A.4.A.1).
|
|
1659
|
+
catch (readError) {
|
|
1660
|
+
printLog(readError, LogLevel.ERROR);
|
|
1661
|
+
}
|
|
1662
|
+
};
|
|
1663
|
+
const completeVerification = async () => {
|
|
1664
|
+
// Stop verification task timer.
|
|
1665
|
+
printLog("Completing verification", LogLevel.DEBUG);
|
|
1666
|
+
verificationTaskTimer.stop();
|
|
1667
|
+
verifyCloudFunctionExecutionTime = verificationTaskTimer.ms();
|
|
1619
1668
|
if (isUsingVM) {
|
|
1620
|
-
//
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1669
|
+
// Create temporary path.
|
|
1670
|
+
verificationTranscriptTemporaryLocalPath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}.log`);
|
|
1671
|
+
await sleep(1000); // wait 1s for file creation.
|
|
1672
|
+
// Download from bucket.
|
|
1673
|
+
// nb. the transcript MUST be uploaded from the VM by verification commands.
|
|
1674
|
+
await downloadArtifactFromS3Bucket(bucketName, verificationTranscriptStoragePathAndFilename, verificationTranscriptTemporaryLocalPath);
|
|
1675
|
+
// Read the verification trascript and validate data by checking for core info ("ZKey Ok!").
|
|
1676
|
+
const content = fs.readFileSync(verificationTranscriptTemporaryLocalPath, "utf-8");
|
|
1677
|
+
if (content.includes("ZKey Ok!"))
|
|
1678
|
+
isContributionValid = true;
|
|
1679
|
+
// If the contribution is valid, then format and store the trascript.
|
|
1680
|
+
if (isContributionValid) {
|
|
1681
|
+
// eslint-disable-next-line no-control-regex
|
|
1682
|
+
const updated = content.replace(/\x1b[[0-9;]*m/g, "");
|
|
1683
|
+
fs.writeFileSync(verificationTranscriptTemporaryLocalPath, updated);
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
printLog(`The contribution has been verified - Result ${isContributionValid}`, LogLevel.DEBUG);
|
|
1687
|
+
// Create a new contribution document.
|
|
1688
|
+
const contributionDoc = await firestore
|
|
1689
|
+
.collection(getContributionsCollectionPath(ceremonyId, circuitId))
|
|
1690
|
+
.doc()
|
|
1691
|
+
.get();
|
|
1692
|
+
// Step (1.A.4).
|
|
1693
|
+
if (isContributionValid) {
|
|
1694
|
+
// Sleep ~3 seconds to wait for verification transcription.
|
|
1695
|
+
await sleep(3000);
|
|
1696
|
+
// Step (1.A.4.A.1).
|
|
1697
|
+
if (isUsingVM) {
|
|
1698
|
+
// Retrieve the contribution hash from the command output.
|
|
1699
|
+
lastZkeyBlake2bHash = await retrieveCommandOutput(ssm, vmInstanceId, commandId);
|
|
1700
|
+
const hashRegex = /[a-fA-F0-9]{64}/;
|
|
1701
|
+
const match = lastZkeyBlake2bHash.match(hashRegex);
|
|
1702
|
+
lastZkeyBlake2bHash = match.at(0);
|
|
1703
|
+
// re upload the formatted verification transcript
|
|
1704
|
+
await uploadFileToBucket(bucketName, verificationTranscriptStoragePathAndFilename, verificationTranscriptTemporaryLocalPath, true);
|
|
1705
|
+
}
|
|
1706
|
+
else {
|
|
1707
|
+
// Upload verification transcript.
|
|
1708
|
+
/// nb. do not use multi-part upload here due to small file size.
|
|
1709
|
+
await uploadFileToBucket(bucketName, verificationTranscriptStoragePathAndFilename, verificationTranscriptTemporaryLocalPath, true);
|
|
1710
|
+
}
|
|
1711
|
+
// Compute verification transcript hash.
|
|
1712
|
+
transcriptBlake2bHash = await blake512FromPath(verificationTranscriptTemporaryLocalPath);
|
|
1713
|
+
// Free resources by unlinking transcript temporary file.
|
|
1714
|
+
fs.unlinkSync(verificationTranscriptTemporaryLocalPath);
|
|
1715
|
+
// Filter participant contributions to find the data related to the one verified.
|
|
1716
|
+
const participantContributions = contributions.filter((contribution) => !!contribution.hash && !!contribution.computationTime && !contribution.doc);
|
|
1717
|
+
/// @dev (there must be only one contribution with an empty 'doc' field).
|
|
1718
|
+
if (participantContributions.length !== 1)
|
|
1719
|
+
logAndThrowError(SPECIFIC_ERRORS.SE_VERIFICATION_NO_PARTICIPANT_CONTRIBUTION_DATA);
|
|
1720
|
+
// Get contribution computation time.
|
|
1721
|
+
contributionComputationTime = contributions.at(0).computationTime;
|
|
1722
|
+
// Step (1.A.4.A.2).
|
|
1723
|
+
batch.create(contributionDoc.ref, {
|
|
1724
|
+
participantId: participantDoc.id,
|
|
1725
|
+
contributionComputationTime,
|
|
1726
|
+
verificationComputationTime: verifyCloudFunctionExecutionTime,
|
|
1727
|
+
zkeyIndex: isFinalizing ? finalContributionIndex : lastZkeyIndex,
|
|
1728
|
+
files: {
|
|
1729
|
+
transcriptFilename: verificationTranscriptCompleteFilename,
|
|
1730
|
+
lastZkeyFilename,
|
|
1731
|
+
transcriptStoragePath: verificationTranscriptStoragePathAndFilename,
|
|
1732
|
+
lastZkeyStoragePath,
|
|
1733
|
+
transcriptBlake2bHash,
|
|
1734
|
+
lastZkeyBlake2bHash
|
|
1735
|
+
},
|
|
1736
|
+
verificationSoftware: {
|
|
1737
|
+
name: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_NAME),
|
|
1738
|
+
version: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_VERSION),
|
|
1739
|
+
commitHash: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_COMMIT_HASH)
|
|
1740
|
+
},
|
|
1741
|
+
valid: isContributionValid,
|
|
1742
|
+
lastUpdated: getCurrentServerTimestampInMillis()
|
|
1743
|
+
});
|
|
1744
|
+
verifyContributionTimer.stop();
|
|
1745
|
+
verifyCloudFunctionTime = verifyContributionTimer.ms();
|
|
1629
1746
|
}
|
|
1630
1747
|
else {
|
|
1631
|
-
//
|
|
1632
|
-
|
|
1633
|
-
await
|
|
1748
|
+
// Step (1.A.4.B).
|
|
1749
|
+
// Free-up storage by deleting invalid contribution.
|
|
1750
|
+
await deleteObject(bucketName, lastZkeyStoragePath);
|
|
1751
|
+
// Step (1.A.4.B.1).
|
|
1752
|
+
batch.create(contributionDoc.ref, {
|
|
1753
|
+
participantId: participantDoc.id,
|
|
1754
|
+
verificationComputationTime: verifyCloudFunctionExecutionTime,
|
|
1755
|
+
zkeyIndex: isFinalizing ? finalContributionIndex : lastZkeyIndex,
|
|
1756
|
+
verificationSoftware: {
|
|
1757
|
+
name: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_NAME),
|
|
1758
|
+
version: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_VERSION),
|
|
1759
|
+
commitHash: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_COMMIT_HASH)
|
|
1760
|
+
},
|
|
1761
|
+
valid: isContributionValid,
|
|
1762
|
+
lastUpdated: getCurrentServerTimestampInMillis()
|
|
1763
|
+
});
|
|
1764
|
+
}
|
|
1765
|
+
// Stop VM instance
|
|
1766
|
+
if (isUsingVM) {
|
|
1767
|
+
// using try and catch as the VM stopping function can throw
|
|
1768
|
+
// however we want to continue without stopping as the
|
|
1769
|
+
// verification was valid, and inform the coordinator
|
|
1770
|
+
try {
|
|
1771
|
+
await stopEC2Instance(ec2, vmInstanceId);
|
|
1772
|
+
}
|
|
1773
|
+
catch (error) {
|
|
1774
|
+
printLog(`Error while stopping VM instance ${vmInstanceId} - Error ${error}`, LogLevel.WARN);
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
// Step (1.A.4.C)
|
|
1778
|
+
if (!isFinalizing) {
|
|
1779
|
+
// Step (1.A.4.C.1)
|
|
1780
|
+
// Compute new average contribution/verification time.
|
|
1781
|
+
fullContributionTime = Number(verificationStartedAt) - Number(contributionStartedAt);
|
|
1782
|
+
const newAvgContributionComputationTime = avgContributionComputationTime > 0
|
|
1783
|
+
? (avgContributionComputationTime + contributionComputationTime) / 2
|
|
1784
|
+
: contributionComputationTime;
|
|
1785
|
+
const newAvgFullContributionTime = avgFullContributionTime > 0
|
|
1786
|
+
? (avgFullContributionTime + fullContributionTime) / 2
|
|
1787
|
+
: fullContributionTime;
|
|
1788
|
+
const newAvgVerifyCloudFunctionTime = avgVerifyCloudFunctionTime > 0
|
|
1789
|
+
? (avgVerifyCloudFunctionTime + verifyCloudFunctionTime) / 2
|
|
1790
|
+
: verifyCloudFunctionTime;
|
|
1791
|
+
// Prepare tx to update circuit average contribution/verification time.
|
|
1792
|
+
const updatedCircuitDoc = await getDocumentById(getCircuitsCollectionPath(ceremonyId), circuitId);
|
|
1793
|
+
const { waitingQueue: updatedWaitingQueue } = updatedCircuitDoc.data();
|
|
1794
|
+
/// @dev this must happen only for valid contributions.
|
|
1795
|
+
batch.update(circuitDoc.ref, {
|
|
1796
|
+
avgTimings: {
|
|
1797
|
+
contributionComputation: isContributionValid
|
|
1798
|
+
? newAvgContributionComputationTime
|
|
1799
|
+
: avgContributionComputationTime,
|
|
1800
|
+
fullContribution: isContributionValid
|
|
1801
|
+
? newAvgFullContributionTime
|
|
1802
|
+
: avgFullContributionTime,
|
|
1803
|
+
verifyCloudFunction: isContributionValid
|
|
1804
|
+
? newAvgVerifyCloudFunctionTime
|
|
1805
|
+
: avgVerifyCloudFunctionTime
|
|
1806
|
+
},
|
|
1807
|
+
waitingQueue: {
|
|
1808
|
+
...updatedWaitingQueue,
|
|
1809
|
+
completedContributions: isContributionValid
|
|
1810
|
+
? completedContributions + 1
|
|
1811
|
+
: completedContributions,
|
|
1812
|
+
failedContributions: isContributionValid ? failedContributions : failedContributions + 1
|
|
1813
|
+
},
|
|
1814
|
+
lastUpdated: getCurrentServerTimestampInMillis()
|
|
1815
|
+
});
|
|
1816
|
+
}
|
|
1817
|
+
// Step (2).
|
|
1818
|
+
await batch.commit();
|
|
1819
|
+
printLog(`The contribution #${isFinalizing ? finalContributionIndex : lastZkeyIndex} of circuit ${circuitId} (ceremony ${ceremonyId}) has been verified as ${isContributionValid ? "valid" : "invalid"} for the participant ${participantDoc.id}`, LogLevel.INFO);
|
|
1820
|
+
};
|
|
1821
|
+
// Step (1).
|
|
1822
|
+
if (isContributing || isFinalizing) {
|
|
1823
|
+
// Prepare timer.
|
|
1824
|
+
verificationTaskTimer.start();
|
|
1825
|
+
// Step (1.A.3.0).
|
|
1826
|
+
if (isUsingVM) {
|
|
1827
|
+
printLog(`Starting the VM mechanism`, LogLevel.DEBUG);
|
|
1828
|
+
// Prepare for VM execution.
|
|
1829
|
+
let isVMRunning = false; // true when the VM is up, otherwise false.
|
|
1830
|
+
// Step (1.A.3.1).
|
|
1831
|
+
await startEC2Instance(ec2, vmInstanceId);
|
|
1832
|
+
await sleep(60000); // nb. wait for VM startup (1 mins + retry).
|
|
1833
|
+
// Check if the startup is running.
|
|
1834
|
+
isVMRunning = await checkIfVMRunning(ec2, vmInstanceId);
|
|
1835
|
+
printLog(`VM running: ${isVMRunning}`, LogLevel.DEBUG);
|
|
1836
|
+
// Step (1.A.3.2).
|
|
1837
|
+
// Prepare.
|
|
1838
|
+
const verificationCommand = vmContributionVerificationCommand(bucketName, lastZkeyStoragePath, verificationTranscriptStoragePathAndFilename);
|
|
1839
|
+
// Run.
|
|
1840
|
+
commandId = await runCommandUsingSSM(ssm, vmInstanceId, verificationCommand);
|
|
1841
|
+
printLog(`Starting the execution of command ${commandId}`, LogLevel.DEBUG);
|
|
1842
|
+
// Step (1.A.3.3).
|
|
1843
|
+
return await waitForVMCommandExecution(ssm, vmInstanceId, commandId)
|
|
1844
|
+
.then(async () => {
|
|
1845
|
+
// Command execution successfully completed.
|
|
1846
|
+
printLog(`Command ${commandId} execution has been successfully completed`, LogLevel.DEBUG);
|
|
1847
|
+
await completeVerification();
|
|
1848
|
+
})
|
|
1849
|
+
.catch((error) => {
|
|
1850
|
+
// Command execution aborted.
|
|
1851
|
+
printLog(`Command ${commandId} execution has been aborted - Error ${error}`, LogLevel.WARN);
|
|
1852
|
+
logAndThrowError(COMMON_ERRORS.CM_INVALID_COMMAND_EXECUTION);
|
|
1853
|
+
});
|
|
1634
1854
|
}
|
|
1635
|
-
// Compute verification transcript hash.
|
|
1636
|
-
transcriptBlake2bHash = await blake512FromPath(verificationTranscriptTemporaryLocalPath);
|
|
1637
|
-
// Free resources by unlinking transcript temporary file.
|
|
1638
|
-
fs.unlinkSync(verificationTranscriptTemporaryLocalPath);
|
|
1639
|
-
// Filter participant contributions to find the data related to the one verified.
|
|
1640
|
-
const participantContributions = contributions.filter((contribution) => !!contribution.hash && !!contribution.computationTime && !contribution.doc);
|
|
1641
|
-
/// @dev (there must be only one contribution with an empty 'doc' field).
|
|
1642
|
-
if (participantContributions.length !== 1)
|
|
1643
|
-
logAndThrowError(SPECIFIC_ERRORS.SE_VERIFICATION_NO_PARTICIPANT_CONTRIBUTION_DATA);
|
|
1644
|
-
// Get contribution computation time.
|
|
1645
|
-
contributionComputationTime = contributions.at(0).computationTime;
|
|
1646
|
-
// Step (1.A.4.A.2).
|
|
1647
|
-
batch.create(contributionDoc.ref, {
|
|
1648
|
-
participantId: participantDoc.id,
|
|
1649
|
-
contributionComputationTime,
|
|
1650
|
-
verificationComputationTime: verifyCloudFunctionExecutionTime,
|
|
1651
|
-
zkeyIndex: isFinalizing ? finalContributionIndex : lastZkeyIndex,
|
|
1652
|
-
files: {
|
|
1653
|
-
transcriptFilename: verificationTranscriptCompleteFilename,
|
|
1654
|
-
lastZkeyFilename,
|
|
1655
|
-
transcriptStoragePath: verificationTranscriptStoragePathAndFilename,
|
|
1656
|
-
lastZkeyStoragePath,
|
|
1657
|
-
transcriptBlake2bHash,
|
|
1658
|
-
lastZkeyBlake2bHash
|
|
1659
|
-
},
|
|
1660
|
-
verificationSoftware: {
|
|
1661
|
-
name: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_NAME),
|
|
1662
|
-
version: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_VERSION),
|
|
1663
|
-
commitHash: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_COMMIT_HASH)
|
|
1664
|
-
},
|
|
1665
|
-
valid: isContributionValid,
|
|
1666
|
-
lastUpdated: getCurrentServerTimestampInMillis()
|
|
1667
|
-
});
|
|
1668
|
-
verifyContributionTimer.stop();
|
|
1669
|
-
verifyCloudFunctionTime = verifyContributionTimer.ms();
|
|
1670
|
-
}
|
|
1671
|
-
else {
|
|
1672
|
-
// Step (1.A.4.B).
|
|
1673
|
-
// Free-up storage by deleting invalid contribution.
|
|
1674
|
-
await deleteObject(bucketName, lastZkeyStoragePath);
|
|
1675
|
-
// Step (1.A.4.B.1).
|
|
1676
|
-
batch.create(contributionDoc.ref, {
|
|
1677
|
-
participantId: participantDoc.id,
|
|
1678
|
-
verificationComputationTime: verifyCloudFunctionExecutionTime,
|
|
1679
|
-
zkeyIndex: isFinalizing ? finalContributionIndex : lastZkeyIndex,
|
|
1680
|
-
verificationSoftware: {
|
|
1681
|
-
name: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_NAME),
|
|
1682
|
-
version: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_VERSION),
|
|
1683
|
-
commitHash: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_COMMIT_HASH)
|
|
1684
|
-
},
|
|
1685
|
-
valid: isContributionValid,
|
|
1686
|
-
lastUpdated: getCurrentServerTimestampInMillis()
|
|
1687
|
-
});
|
|
1688
|
-
}
|
|
1689
|
-
// Step (1.A.4.C)
|
|
1690
|
-
if (!isFinalizing) {
|
|
1691
|
-
// Step (1.A.4.C.1)
|
|
1692
|
-
// Compute new average contribution/verification time.
|
|
1693
|
-
fullContributionTime = Number(verificationStartedAt) - Number(contributionStartedAt);
|
|
1694
|
-
const newAvgContributionComputationTime = avgContributionComputationTime > 0
|
|
1695
|
-
? (avgContributionComputationTime + contributionComputationTime) / 2
|
|
1696
|
-
: contributionComputationTime;
|
|
1697
|
-
const newAvgFullContributionTime = avgFullContributionTime > 0
|
|
1698
|
-
? (avgFullContributionTime + fullContributionTime) / 2
|
|
1699
|
-
: fullContributionTime;
|
|
1700
|
-
const newAvgVerifyCloudFunctionTime = avgVerifyCloudFunctionTime > 0
|
|
1701
|
-
? (avgVerifyCloudFunctionTime + verifyCloudFunctionTime) / 2
|
|
1702
|
-
: verifyCloudFunctionTime;
|
|
1703
|
-
// Prepare tx to update circuit average contribution/verification time.
|
|
1704
|
-
const updatedCircuitDoc = await getDocumentById(getCircuitsCollectionPath(ceremonyId), circuitId);
|
|
1705
|
-
const { waitingQueue: updatedWaitingQueue } = updatedCircuitDoc.data();
|
|
1706
|
-
/// @dev this must happen only for valid contributions.
|
|
1707
|
-
batch.update(circuitDoc.ref, {
|
|
1708
|
-
avgTimings: {
|
|
1709
|
-
contributionComputation: isContributionValid
|
|
1710
|
-
? newAvgContributionComputationTime
|
|
1711
|
-
: avgContributionComputationTime,
|
|
1712
|
-
fullContribution: isContributionValid ? newAvgFullContributionTime : avgFullContributionTime,
|
|
1713
|
-
verifyCloudFunction: isContributionValid
|
|
1714
|
-
? newAvgVerifyCloudFunctionTime
|
|
1715
|
-
: avgVerifyCloudFunctionTime
|
|
1716
|
-
},
|
|
1717
|
-
waitingQueue: {
|
|
1718
|
-
...updatedWaitingQueue,
|
|
1719
|
-
completedContributions: isContributionValid
|
|
1720
|
-
? completedContributions + 1
|
|
1721
|
-
: completedContributions,
|
|
1722
|
-
failedContributions: isContributionValid ? failedContributions : failedContributions + 1
|
|
1723
|
-
},
|
|
1724
|
-
lastUpdated: getCurrentServerTimestampInMillis()
|
|
1725
|
-
});
|
|
1726
|
-
}
|
|
1727
|
-
// Step (2).
|
|
1728
|
-
await batch.commit();
|
|
1729
|
-
printLog(`The contribution #${isFinalizing ? finalContributionIndex : lastZkeyIndex} of circuit ${circuitId} (ceremony ${ceremonyId}) has been verified as ${isContributionValid ? "valid" : "invalid"} for the participant ${participantDoc.id}`, LogLevel.DEBUG);
|
|
1730
|
-
};
|
|
1731
|
-
// Step (1).
|
|
1732
|
-
if (isContributing || isFinalizing) {
|
|
1733
|
-
// Prepare timer.
|
|
1734
|
-
verificationTaskTimer.start();
|
|
1735
|
-
// Step (1.A.3.0).
|
|
1736
|
-
if (isUsingVM) {
|
|
1737
|
-
printLog(`Starting the VM mechanism`, LogLevel.DEBUG);
|
|
1738
|
-
// Prepare for VM execution.
|
|
1739
|
-
let isVMRunning = false; // true when the VM is up, otherwise false.
|
|
1740
|
-
// Step (1.A.3.1).
|
|
1741
|
-
await startEC2Instance(ec2, vmInstanceId);
|
|
1742
|
-
await sleep(60000); // nb. wait for VM startup (1 mins + retry).
|
|
1743
|
-
// Check if the startup is running.
|
|
1744
|
-
isVMRunning = await checkIfVMRunning(ec2, vmInstanceId);
|
|
1745
|
-
printLog(`VM running: ${isVMRunning}`, LogLevel.DEBUG);
|
|
1746
|
-
// Step (1.A.3.2).
|
|
1747
|
-
// Prepare.
|
|
1748
|
-
const verificationCommand = vmContributionVerificationCommand(bucketName, lastZkeyStoragePath, verificationTranscriptStoragePathAndFilename);
|
|
1749
|
-
// Run.
|
|
1750
|
-
commandId = await runCommandUsingSSM(ssm, vmInstanceId, verificationCommand);
|
|
1751
|
-
printLog(`Starting the execution of command ${commandId}`, LogLevel.DEBUG);
|
|
1752
|
-
// Step (1.A.3.3).
|
|
1753
|
-
return new Promise((resolve, reject) => waitForVMCommandExecution(resolve, reject, ssm, vmInstanceId, commandId))
|
|
1754
|
-
.then(async () => {
|
|
1755
|
-
// Command execution successfully completed.
|
|
1756
|
-
printLog(`Command ${commandId} execution has been successfully completed`, LogLevel.DEBUG);
|
|
1757
|
-
await completeVerification();
|
|
1758
|
-
})
|
|
1759
|
-
.catch((error) => {
|
|
1760
|
-
// Command execution aborted.
|
|
1761
|
-
printLog(`Command ${commandId} execution has been aborted - Error ${error}`, LogLevel.DEBUG);
|
|
1762
|
-
logAndThrowError(COMMON_ERRORS.CM_INVALID_COMMAND_EXECUTION);
|
|
1763
|
-
});
|
|
1764
|
-
}
|
|
1765
|
-
else {
|
|
1766
1855
|
// CF approach.
|
|
1767
1856
|
printLog(`CF mechanism`, LogLevel.DEBUG);
|
|
1768
1857
|
const potStoragePath = getPotStorageFilePath(files.potFilename);
|
|
1769
1858
|
const firstZkeyStoragePath = getZkeyStorageFilePath(prefix, `${prefix}_${genesisZkeyIndex}.zkey`);
|
|
1859
|
+
printLog(`pot file: ${potStoragePath}`, LogLevel.DEBUG);
|
|
1860
|
+
printLog(`zkey file: ${firstZkeyStoragePath}`, LogLevel.DEBUG);
|
|
1770
1861
|
// Prepare temporary file paths.
|
|
1771
1862
|
// (nb. these are needed to download the necessary artifacts for verification from AWS S3).
|
|
1772
1863
|
verificationTranscriptTemporaryLocalPath = createTemporaryLocalPath(verificationTranscriptCompleteFilename);
|
|
1773
1864
|
const potTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}.pot`);
|
|
1774
1865
|
const firstZkeyTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}_genesis.zkey`);
|
|
1775
1866
|
const lastZkeyTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}_last.zkey`);
|
|
1867
|
+
printLog(`pot file: ${potTempFilePath}`, LogLevel.DEBUG);
|
|
1868
|
+
printLog(`firstZkey file: ${firstZkeyTempFilePath}`, LogLevel.DEBUG);
|
|
1869
|
+
printLog(`last zkey file: ${lastZkeyTempFilePath}`, LogLevel.DEBUG);
|
|
1776
1870
|
// Create and populate transcript.
|
|
1777
1871
|
const transcriptLogger = createCustomLoggerForFile(verificationTranscriptTemporaryLocalPath);
|
|
1778
1872
|
transcriptLogger.info(`${isFinalizing ? `Final verification` : `Verification`} transcript for ${prefix} circuit Phase 2 contribution.\n${isFinalizing ? `Coordinator ` : `Contributor # ${Number(lastZkeyIndex)}`} (${contributorOrCoordinatorIdentifier})\n`);
|
|
@@ -1782,6 +1876,7 @@ const verifycontribution = functionsV2.https.onCall({ memory: "16GiB", timeoutSe
|
|
|
1782
1876
|
await downloadArtifactFromS3Bucket(bucketName, lastZkeyStoragePath, lastZkeyTempFilePath);
|
|
1783
1877
|
// Step (1.A.4).
|
|
1784
1878
|
isContributionValid = await zKey.verifyFromInit(firstZkeyTempFilePath, potTempFilePath, lastZkeyTempFilePath, transcriptLogger);
|
|
1879
|
+
await dumpLog(verificationTranscriptTemporaryLocalPath);
|
|
1785
1880
|
// Compute contribution hash.
|
|
1786
1881
|
lastZkeyBlake2bHash = await blake512FromPath(lastZkeyTempFilePath);
|
|
1787
1882
|
// Free resources by unlinking temporary folders.
|
|
@@ -1796,6 +1891,11 @@ const verifycontribution = functionsV2.https.onCall({ memory: "16GiB", timeoutSe
|
|
|
1796
1891
|
}
|
|
1797
1892
|
await completeVerification();
|
|
1798
1893
|
}
|
|
1894
|
+
return null;
|
|
1895
|
+
}
|
|
1896
|
+
catch (error) {
|
|
1897
|
+
logAndThrowError(makeError("unknown", error));
|
|
1898
|
+
return null;
|
|
1799
1899
|
}
|
|
1800
1900
|
});
|
|
1801
1901
|
/**
|
|
@@ -1804,9 +1904,9 @@ const verifycontribution = functionsV2.https.onCall({ memory: "16GiB", timeoutSe
|
|
|
1804
1904
|
* this does not happen if the participant is actually the coordinator who is finalizing the ceremony.
|
|
1805
1905
|
*/
|
|
1806
1906
|
const refreshParticipantAfterContributionVerification = functionsV1
|
|
1807
|
-
.region(
|
|
1907
|
+
.region("europe-west1")
|
|
1808
1908
|
.runWith({
|
|
1809
|
-
memory: "
|
|
1909
|
+
memory: "1GB"
|
|
1810
1910
|
})
|
|
1811
1911
|
.firestore.document(`/${commonTerms.collections.ceremonies.name}/{ceremony}/${commonTerms.collections.circuits.name}/{circuit}/${commonTerms.collections.contributions.name}/{contributions}`)
|
|
1812
1912
|
.onCreate(async (createdContribution) => {
|
|
@@ -1857,7 +1957,7 @@ const refreshParticipantAfterContributionVerification = functionsV1
|
|
|
1857
1957
|
lastUpdated: getCurrentServerTimestampInMillis()
|
|
1858
1958
|
});
|
|
1859
1959
|
await batch.commit();
|
|
1860
|
-
printLog(`Participant ${participantId} refreshed after contribution ${createdContribution.id} - The participant was finalizing the ceremony ${isFinalizing}`, LogLevel.
|
|
1960
|
+
printLog(`Participant ${participantId} refreshed after contribution ${createdContribution.id} - The participant was finalizing the ceremony? ${isFinalizing}`, LogLevel.INFO);
|
|
1861
1961
|
});
|
|
1862
1962
|
/**
|
|
1863
1963
|
* Finalize the ceremony circuit.
|
|
@@ -1865,9 +1965,9 @@ const refreshParticipantAfterContributionVerification = functionsV1
|
|
|
1865
1965
|
* and verification key extracted from the circuit final contribution (as part of the ceremony finalization process).
|
|
1866
1966
|
*/
|
|
1867
1967
|
const finalizeCircuit = functionsV1
|
|
1868
|
-
.region(
|
|
1968
|
+
.region("europe-west1")
|
|
1869
1969
|
.runWith({
|
|
1870
|
-
memory: "
|
|
1970
|
+
memory: "1GB"
|
|
1871
1971
|
})
|
|
1872
1972
|
.https.onCall(async (data, context) => {
|
|
1873
1973
|
if (!context.auth || !context.auth.token.coordinator)
|
|
@@ -2011,7 +2111,7 @@ const checkIfBucketIsDedicatedToCeremony = async (bucketName) => {
|
|
|
2011
2111
|
const createBucket = functions
|
|
2012
2112
|
.region("europe-west1")
|
|
2013
2113
|
.runWith({
|
|
2014
|
-
memory: "
|
|
2114
|
+
memory: "1GB"
|
|
2015
2115
|
})
|
|
2016
2116
|
.https.onCall(async (data, context) => {
|
|
2017
2117
|
// Check if the user has the coordinator claim.
|
|
@@ -2022,6 +2122,7 @@ const createBucket = functions
|
|
|
2022
2122
|
// Connect to S3 client.
|
|
2023
2123
|
const S3 = await getS3Client();
|
|
2024
2124
|
try {
|
|
2125
|
+
printLog(`Creating AWS S3 bucket ${data.bucketName} ...`, LogLevel.LOG);
|
|
2025
2126
|
// Try to get information about the bucket.
|
|
2026
2127
|
await S3.send(new HeadBucketCommand({ Bucket: data.bucketName }));
|
|
2027
2128
|
// If the command succeeded, the bucket exists, throw an error.
|
|
@@ -2062,8 +2163,10 @@ const createBucket = functions
|
|
|
2062
2163
|
CORSConfiguration: {
|
|
2063
2164
|
CORSRules: [
|
|
2064
2165
|
{
|
|
2065
|
-
AllowedMethods: ["GET"],
|
|
2066
|
-
AllowedOrigins: ["*"]
|
|
2166
|
+
AllowedMethods: ["GET", "PUT"],
|
|
2167
|
+
AllowedOrigins: ["*"],
|
|
2168
|
+
ExposeHeaders: ["ETag", "Content-Length"],
|
|
2169
|
+
AllowedHeaders: ["*"]
|
|
2067
2170
|
}
|
|
2068
2171
|
]
|
|
2069
2172
|
}
|
|
@@ -2099,7 +2202,7 @@ const createBucket = functions
|
|
|
2099
2202
|
const checkIfObjectExist = functions
|
|
2100
2203
|
.region("europe-west1")
|
|
2101
2204
|
.runWith({
|
|
2102
|
-
memory: "
|
|
2205
|
+
memory: "1GB"
|
|
2103
2206
|
})
|
|
2104
2207
|
.https.onCall(async (data, context) => {
|
|
2105
2208
|
// Check if the user has the coordinator claim.
|
|
@@ -2145,7 +2248,7 @@ const checkIfObjectExist = functions
|
|
|
2145
2248
|
const generateGetObjectPreSignedUrl = functions
|
|
2146
2249
|
.region("europe-west1")
|
|
2147
2250
|
.runWith({
|
|
2148
|
-
memory: "
|
|
2251
|
+
memory: "1GB"
|
|
2149
2252
|
})
|
|
2150
2253
|
.https.onCall(async (data, context) => {
|
|
2151
2254
|
if (!context.auth)
|
|
@@ -2185,7 +2288,7 @@ const generateGetObjectPreSignedUrl = functions
|
|
|
2185
2288
|
const startMultiPartUpload = functions
|
|
2186
2289
|
.region("europe-west1")
|
|
2187
2290
|
.runWith({
|
|
2188
|
-
memory: "
|
|
2291
|
+
memory: "2GB"
|
|
2189
2292
|
})
|
|
2190
2293
|
.https.onCall(async (data, context) => {
|
|
2191
2294
|
if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
|
|
@@ -2240,7 +2343,8 @@ const startMultiPartUpload = functions
|
|
|
2240
2343
|
const generatePreSignedUrlsParts = functions
|
|
2241
2344
|
.region("europe-west1")
|
|
2242
2345
|
.runWith({
|
|
2243
|
-
memory: "
|
|
2346
|
+
memory: "1GB",
|
|
2347
|
+
timeoutSeconds: 300
|
|
2244
2348
|
})
|
|
2245
2349
|
.https.onCall(async (data, context) => {
|
|
2246
2350
|
if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
|
|
@@ -2300,7 +2404,7 @@ const generatePreSignedUrlsParts = functions
|
|
|
2300
2404
|
const completeMultiPartUpload = functions
|
|
2301
2405
|
.region("europe-west1")
|
|
2302
2406
|
.runWith({
|
|
2303
|
-
memory: "
|
|
2407
|
+
memory: "2GB"
|
|
2304
2408
|
})
|
|
2305
2409
|
.https.onCall(async (data, context) => {
|
|
2306
2410
|
if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
|
|
@@ -2349,6 +2453,216 @@ const completeMultiPartUpload = functions
|
|
|
2349
2453
|
}
|
|
2350
2454
|
});
|
|
2351
2455
|
|
|
2456
|
+
dotenv.config();
|
|
2457
|
+
const { BANDADA_API_URL, BANDADA_GROUP_ID } = process.env;
|
|
2458
|
+
const bandadaApi = new ApiSdk(BANDADA_API_URL);
|
|
2459
|
+
const bandadaValidateProof = functions
|
|
2460
|
+
.region("europe-west1")
|
|
2461
|
+
.runWith({
|
|
2462
|
+
memory: "1GB"
|
|
2463
|
+
})
|
|
2464
|
+
.https.onCall(async (data) => {
|
|
2465
|
+
const VKEY_DATA = {
|
|
2466
|
+
protocol: "groth16",
|
|
2467
|
+
curve: "bn128",
|
|
2468
|
+
nPublic: 3,
|
|
2469
|
+
vk_alpha_1: [
|
|
2470
|
+
"20491192805390485299153009773594534940189261866228447918068658471970481763042",
|
|
2471
|
+
"9383485363053290200918347156157836566562967994039712273449902621266178545958",
|
|
2472
|
+
"1"
|
|
2473
|
+
],
|
|
2474
|
+
vk_beta_2: [
|
|
2475
|
+
[
|
|
2476
|
+
"6375614351688725206403948262868962793625744043794305715222011528459656738731",
|
|
2477
|
+
"4252822878758300859123897981450591353533073413197771768651442665752259397132"
|
|
2478
|
+
],
|
|
2479
|
+
[
|
|
2480
|
+
"10505242626370262277552901082094356697409835680220590971873171140371331206856",
|
|
2481
|
+
"21847035105528745403288232691147584728191162732299865338377159692350059136679"
|
|
2482
|
+
],
|
|
2483
|
+
["1", "0"]
|
|
2484
|
+
],
|
|
2485
|
+
vk_gamma_2: [
|
|
2486
|
+
[
|
|
2487
|
+
"10857046999023057135944570762232829481370756359578518086990519993285655852781",
|
|
2488
|
+
"11559732032986387107991004021392285783925812861821192530917403151452391805634"
|
|
2489
|
+
],
|
|
2490
|
+
[
|
|
2491
|
+
"8495653923123431417604973247489272438418190587263600148770280649306958101930",
|
|
2492
|
+
"4082367875863433681332203403145435568316851327593401208105741076214120093531"
|
|
2493
|
+
],
|
|
2494
|
+
["1", "0"]
|
|
2495
|
+
],
|
|
2496
|
+
vk_delta_2: [
|
|
2497
|
+
[
|
|
2498
|
+
"3697618915467790705869942236922063775466274665053173890632463796679068973252",
|
|
2499
|
+
"14948341351907992175709156460547989243732741534604949238422596319735704165658"
|
|
2500
|
+
],
|
|
2501
|
+
[
|
|
2502
|
+
"3028459181652799888716942141752307629938889957960373621898607910203491239368",
|
|
2503
|
+
"11380736494786911280692284374675752681598754560757720296073023058533044108340"
|
|
2504
|
+
],
|
|
2505
|
+
["1", "0"]
|
|
2506
|
+
],
|
|
2507
|
+
vk_alphabeta_12: [
|
|
2508
|
+
[
|
|
2509
|
+
[
|
|
2510
|
+
"2029413683389138792403550203267699914886160938906632433982220835551125967885",
|
|
2511
|
+
"21072700047562757817161031222997517981543347628379360635925549008442030252106"
|
|
2512
|
+
],
|
|
2513
|
+
[
|
|
2514
|
+
"5940354580057074848093997050200682056184807770593307860589430076672439820312",
|
|
2515
|
+
"12156638873931618554171829126792193045421052652279363021382169897324752428276"
|
|
2516
|
+
],
|
|
2517
|
+
[
|
|
2518
|
+
"7898200236362823042373859371574133993780991612861777490112507062703164551277",
|
|
2519
|
+
"7074218545237549455313236346927434013100842096812539264420499035217050630853"
|
|
2520
|
+
]
|
|
2521
|
+
],
|
|
2522
|
+
[
|
|
2523
|
+
[
|
|
2524
|
+
"7077479683546002997211712695946002074877511277312570035766170199895071832130",
|
|
2525
|
+
"10093483419865920389913245021038182291233451549023025229112148274109565435465"
|
|
2526
|
+
],
|
|
2527
|
+
[
|
|
2528
|
+
"4595479056700221319381530156280926371456704509942304414423590385166031118820",
|
|
2529
|
+
"19831328484489333784475432780421641293929726139240675179672856274388269393268"
|
|
2530
|
+
],
|
|
2531
|
+
[
|
|
2532
|
+
"11934129596455521040620786944827826205713621633706285934057045369193958244500",
|
|
2533
|
+
"8037395052364110730298837004334506829870972346962140206007064471173334027475"
|
|
2534
|
+
]
|
|
2535
|
+
]
|
|
2536
|
+
],
|
|
2537
|
+
IC: [
|
|
2538
|
+
[
|
|
2539
|
+
"12951059800758687233303204819298121944551181861362200875212570257618182506154",
|
|
2540
|
+
"5751958719396509176593242305268064754837298673622815112953832050159760501392",
|
|
2541
|
+
"1"
|
|
2542
|
+
],
|
|
2543
|
+
[
|
|
2544
|
+
"9561588427935871983444704959674198910445823619407211599507208879011862515257",
|
|
2545
|
+
"14576201570478094842467636169770180675293504492823217349086195663150934064643",
|
|
2546
|
+
"1"
|
|
2547
|
+
],
|
|
2548
|
+
[
|
|
2549
|
+
"4811967233483727873912563574622036989372099129165459921963463310078093941559",
|
|
2550
|
+
"1874883809855039536107616044787862082553628089593740724610117059083415551067",
|
|
2551
|
+
"1"
|
|
2552
|
+
],
|
|
2553
|
+
[
|
|
2554
|
+
"12252730267779308452229639835051322390696643456253768618882001876621526827161",
|
|
2555
|
+
"7899194018737016222260328309937800777948677569409898603827268776967707173231",
|
|
2556
|
+
"1"
|
|
2557
|
+
]
|
|
2558
|
+
]
|
|
2559
|
+
};
|
|
2560
|
+
if (!BANDADA_GROUP_ID)
|
|
2561
|
+
throw new Error("BANDADA_GROUP_ID is not defined in .env");
|
|
2562
|
+
const { proof, publicSignals } = data;
|
|
2563
|
+
const isCorrect = groth16.verify(VKEY_DATA, publicSignals, proof);
|
|
2564
|
+
if (!isCorrect)
|
|
2565
|
+
return {
|
|
2566
|
+
valid: false,
|
|
2567
|
+
message: "Invalid proof",
|
|
2568
|
+
token: ""
|
|
2569
|
+
};
|
|
2570
|
+
const commitment = data.publicSignals[1];
|
|
2571
|
+
const isMember = await bandadaApi.isGroupMember(BANDADA_GROUP_ID, commitment);
|
|
2572
|
+
if (!isMember)
|
|
2573
|
+
return {
|
|
2574
|
+
valid: false,
|
|
2575
|
+
message: "Not a member of the group",
|
|
2576
|
+
token: ""
|
|
2577
|
+
};
|
|
2578
|
+
const auth = getAuth();
|
|
2579
|
+
try {
|
|
2580
|
+
await admin.auth().createUser({
|
|
2581
|
+
uid: commitment
|
|
2582
|
+
});
|
|
2583
|
+
}
|
|
2584
|
+
catch (error) {
|
|
2585
|
+
// if user already exist then just pass
|
|
2586
|
+
if (error.code !== "auth/uid-already-exists") {
|
|
2587
|
+
throw new Error(error);
|
|
2588
|
+
}
|
|
2589
|
+
}
|
|
2590
|
+
const token = await auth.createCustomToken(commitment);
|
|
2591
|
+
return {
|
|
2592
|
+
valid: true,
|
|
2593
|
+
message: "Valid proof and group member",
|
|
2594
|
+
token
|
|
2595
|
+
};
|
|
2596
|
+
});
|
|
2597
|
+
|
|
2598
|
+
dotenv.config();
|
|
2599
|
+
const checkNonceOfSIWEAddress = functions
|
|
2600
|
+
.region("europe-west1")
|
|
2601
|
+
.runWith({ memory: "1GB" })
|
|
2602
|
+
.https.onCall(async (data) => {
|
|
2603
|
+
try {
|
|
2604
|
+
const { auth0Token } = data;
|
|
2605
|
+
const result = (await fetch(`${process.env.AUTH0_APPLICATION_URL}/userinfo`, {
|
|
2606
|
+
method: "GET",
|
|
2607
|
+
headers: {
|
|
2608
|
+
"content-type": "application/json",
|
|
2609
|
+
authorization: `Bearer ${auth0Token}`
|
|
2610
|
+
}
|
|
2611
|
+
}).then((_res) => _res.json()));
|
|
2612
|
+
if (!result.sub) {
|
|
2613
|
+
return {
|
|
2614
|
+
valid: false,
|
|
2615
|
+
message: "No user detected. Please check device flow token"
|
|
2616
|
+
};
|
|
2617
|
+
}
|
|
2618
|
+
const auth = getAuth();
|
|
2619
|
+
// check nonce
|
|
2620
|
+
const parts = result.sub.split("|");
|
|
2621
|
+
const address = decodeURIComponent(parts[2]).split(":")[2];
|
|
2622
|
+
const minimumNonce = Number(process.env.ETH_MINIMUM_NONCE);
|
|
2623
|
+
const nonceBlockHeight = "latest"; // process.env.ETH_NONCE_BLOCK_HEIGHT
|
|
2624
|
+
// look up nonce for address @block
|
|
2625
|
+
let nonceOk = true;
|
|
2626
|
+
if (minimumNonce > 0) {
|
|
2627
|
+
const provider = setEthProvider();
|
|
2628
|
+
console.log(`got provider - block # ${await provider.getBlockNumber()}`);
|
|
2629
|
+
const nonce = await provider.getTransactionCount(address, nonceBlockHeight);
|
|
2630
|
+
console.log(`nonce ${nonce}`);
|
|
2631
|
+
nonceOk = nonce >= minimumNonce;
|
|
2632
|
+
}
|
|
2633
|
+
console.log(`checking nonce ${nonceOk}`);
|
|
2634
|
+
if (!nonceOk) {
|
|
2635
|
+
return {
|
|
2636
|
+
valid: false,
|
|
2637
|
+
message: "Eth address does not meet the nonce requirements"
|
|
2638
|
+
};
|
|
2639
|
+
}
|
|
2640
|
+
try {
|
|
2641
|
+
await admin.auth().createUser({
|
|
2642
|
+
displayName: address,
|
|
2643
|
+
uid: address
|
|
2644
|
+
});
|
|
2645
|
+
}
|
|
2646
|
+
catch (error) {
|
|
2647
|
+
// if user already exist then just pass
|
|
2648
|
+
if (error.code !== "auth/uid-already-exists") {
|
|
2649
|
+
throw new Error(error);
|
|
2650
|
+
}
|
|
2651
|
+
}
|
|
2652
|
+
const token = await auth.createCustomToken(address);
|
|
2653
|
+
return {
|
|
2654
|
+
valid: true,
|
|
2655
|
+
token
|
|
2656
|
+
};
|
|
2657
|
+
}
|
|
2658
|
+
catch (error) {
|
|
2659
|
+
return {
|
|
2660
|
+
valid: false,
|
|
2661
|
+
message: `Something went wrong ${error}`
|
|
2662
|
+
};
|
|
2663
|
+
}
|
|
2664
|
+
});
|
|
2665
|
+
|
|
2352
2666
|
dotenv.config();
|
|
2353
2667
|
/**
|
|
2354
2668
|
* Check and remove the current contributor if it doesn't complete the contribution on the specified amount of time.
|
|
@@ -2371,7 +2685,7 @@ dotenv.config();
|
|
|
2371
2685
|
const checkAndRemoveBlockingContributor = functions
|
|
2372
2686
|
.region("europe-west1")
|
|
2373
2687
|
.runWith({
|
|
2374
|
-
memory: "
|
|
2688
|
+
memory: "1GB"
|
|
2375
2689
|
})
|
|
2376
2690
|
.pubsub.schedule("every 1 minutes")
|
|
2377
2691
|
.onRun(async () => {
|
|
@@ -2390,7 +2704,7 @@ const checkAndRemoveBlockingContributor = functions
|
|
|
2390
2704
|
// Get ceremony circuits.
|
|
2391
2705
|
const circuits = await getCeremonyCircuits(ceremony.id);
|
|
2392
2706
|
// Extract ceremony data.
|
|
2393
|
-
const { timeoutMechanismType, penalty } = ceremony.data();
|
|
2707
|
+
const { timeoutType: timeoutMechanismType, penalty } = ceremony.data();
|
|
2394
2708
|
for (const circuit of circuits) {
|
|
2395
2709
|
if (!circuit.data())
|
|
2396
2710
|
// Do not use `logAndThrowError` method to avoid the function to exit before checking every ceremony.
|
|
@@ -2403,7 +2717,7 @@ const checkAndRemoveBlockingContributor = functions
|
|
|
2403
2717
|
// Case (A).
|
|
2404
2718
|
if (!currentContributor)
|
|
2405
2719
|
// Do not use `logAndThrowError` method to avoid the function to exit before checking every ceremony.
|
|
2406
|
-
printLog(`No current contributor for circuit ${circuit.id} - ceremony ${ceremony.id}`, LogLevel.
|
|
2720
|
+
printLog(`No current contributor for circuit ${circuit.id} - ceremony ${ceremony.id}`, LogLevel.DEBUG);
|
|
2407
2721
|
else if (avgFullContribution === 0 &&
|
|
2408
2722
|
avgContributionComputation === 0 &&
|
|
2409
2723
|
avgVerifyCloudFunction === 0 &&
|
|
@@ -2429,7 +2743,7 @@ const checkAndRemoveBlockingContributor = functions
|
|
|
2429
2743
|
? Number(contributionStartedAt) +
|
|
2430
2744
|
Number(avgFullContribution) +
|
|
2431
2745
|
Number(timeoutDynamicThreshold)
|
|
2432
|
-
: Number(contributionStartedAt) + Number(fixedTimeWindow) * 60000; // * 60000 = convert minutes to millis.
|
|
2746
|
+
: (Number(contributionStartedAt) + Number(fixedTimeWindow)) * 60000; // * 60000 = convert minutes to millis.
|
|
2433
2747
|
// Case (D).
|
|
2434
2748
|
const timeoutExpirationDateInMsForVerificationCloudFunction = contributionStep === "VERIFYING" /* ParticipantContributionStep.VERIFYING */ &&
|
|
2435
2749
|
!!verificationStartedAt
|
|
@@ -2440,17 +2754,18 @@ const checkAndRemoveBlockingContributor = functions
|
|
|
2440
2754
|
if (timeoutExpirationDateInMsForBlockingContributor < currentServerTimestamp &&
|
|
2441
2755
|
(contributionStep === "DOWNLOADING" /* ParticipantContributionStep.DOWNLOADING */ ||
|
|
2442
2756
|
contributionStep === "COMPUTING" /* ParticipantContributionStep.COMPUTING */ ||
|
|
2443
|
-
contributionStep === "UPLOADING" /* ParticipantContributionStep.UPLOADING */
|
|
2757
|
+
contributionStep === "UPLOADING" /* ParticipantContributionStep.UPLOADING */ ||
|
|
2758
|
+
contributionStep === "COMPLETED" /* ParticipantContributionStep.COMPLETED */))
|
|
2444
2759
|
timeoutType = "BLOCKING_CONTRIBUTION" /* TimeoutType.BLOCKING_CONTRIBUTION */;
|
|
2445
2760
|
if (timeoutExpirationDateInMsForVerificationCloudFunction > 0 &&
|
|
2446
2761
|
timeoutExpirationDateInMsForVerificationCloudFunction < currentServerTimestamp &&
|
|
2447
2762
|
contributionStep === "VERIFYING" /* ParticipantContributionStep.VERIFYING */)
|
|
2448
2763
|
timeoutType = "BLOCKING_CLOUD_FUNCTION" /* TimeoutType.BLOCKING_CLOUD_FUNCTION */;
|
|
2449
|
-
printLog(`${timeoutType} detected for circuit ${circuit.id} - ceremony ${ceremony.id}`, LogLevel.DEBUG);
|
|
2450
2764
|
if (!timeoutType)
|
|
2451
2765
|
// Do not use `logAndThrowError` method to avoid the function to exit before checking every ceremony.
|
|
2452
|
-
printLog(`No timeout for circuit ${circuit.id} - ceremony ${ceremony.id}`, LogLevel.
|
|
2766
|
+
printLog(`No timeout for circuit ${circuit.id} - ceremony ${ceremony.id}`, LogLevel.DEBUG);
|
|
2453
2767
|
else {
|
|
2768
|
+
printLog(`${timeoutType} detected for circuit ${circuit.id} - ceremony ${ceremony.id}`, LogLevel.WARN);
|
|
2454
2769
|
// Case (E).
|
|
2455
2770
|
let nextCurrentContributorId = "";
|
|
2456
2771
|
// Prepare Firestore batch of txs.
|
|
@@ -2501,7 +2816,7 @@ const checkAndRemoveBlockingContributor = functions
|
|
|
2501
2816
|
});
|
|
2502
2817
|
// Send atomic update for Firestore.
|
|
2503
2818
|
await batch.commit();
|
|
2504
|
-
printLog(`The contributor ${participant.id} has been identified as potential blocking contributor. A timeout of type ${timeoutType} has been triggered w/ a penalty of ${timeoutPenaltyInMs} ms`, LogLevel.
|
|
2819
|
+
printLog(`The contributor ${participant.id} has been identified as potential blocking contributor. A timeout of type ${timeoutType} has been triggered w/ a penalty of ${timeoutPenaltyInMs} ms`, LogLevel.WARN);
|
|
2505
2820
|
}
|
|
2506
2821
|
}
|
|
2507
2822
|
}
|
|
@@ -2517,7 +2832,7 @@ const checkAndRemoveBlockingContributor = functions
|
|
|
2517
2832
|
const resumeContributionAfterTimeoutExpiration = functions
|
|
2518
2833
|
.region("europe-west1")
|
|
2519
2834
|
.runWith({
|
|
2520
|
-
memory: "
|
|
2835
|
+
memory: "1GB"
|
|
2521
2836
|
})
|
|
2522
2837
|
.https.onCall(async (data, context) => {
|
|
2523
2838
|
if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
|
|
@@ -2540,7 +2855,8 @@ const resumeContributionAfterTimeoutExpiration = functions
|
|
|
2540
2855
|
if (status === "EXHUMED" /* ParticipantStatus.EXHUMED */)
|
|
2541
2856
|
await participantDoc.ref.update({
|
|
2542
2857
|
status: "READY" /* ParticipantStatus.READY */,
|
|
2543
|
-
lastUpdated: getCurrentServerTimestampInMillis()
|
|
2858
|
+
lastUpdated: getCurrentServerTimestampInMillis(),
|
|
2859
|
+
tempContributionData: {}
|
|
2544
2860
|
});
|
|
2545
2861
|
else
|
|
2546
2862
|
logAndThrowError(SPECIFIC_ERRORS.SE_CONTRIBUTE_CANNOT_PROGRESS_TO_NEXT_CIRCUIT);
|
|
@@ -2549,4 +2865,4 @@ const resumeContributionAfterTimeoutExpiration = functions
|
|
|
2549
2865
|
|
|
2550
2866
|
admin.initializeApp();
|
|
2551
2867
|
|
|
2552
|
-
export { checkAndPrepareCoordinatorForFinalization, checkAndRemoveBlockingContributor, checkIfObjectExist, checkParticipantForCeremony, completeMultiPartUpload, coordinateCeremonyParticipant, createBucket, finalizeCeremony, finalizeCircuit, generateGetObjectPreSignedUrl, generatePreSignedUrlsParts, initEmptyWaitingQueueForCircuit, permanentlyStoreCurrentContributionTimeAndHash, processSignUpWithCustomClaims, progressToNextCircuitForContribution, progressToNextContributionStep, refreshParticipantAfterContributionVerification, registerAuthUser, resumeContributionAfterTimeoutExpiration, setupCeremony, startCeremony, startMultiPartUpload, stopCeremony, temporaryStoreCurrentContributionMultiPartUploadId, temporaryStoreCurrentContributionUploadedChunkData, verifycontribution };
|
|
2868
|
+
export { bandadaValidateProof, checkAndPrepareCoordinatorForFinalization, checkAndRemoveBlockingContributor, checkIfObjectExist, checkNonceOfSIWEAddress, checkParticipantForCeremony, completeMultiPartUpload, coordinateCeremonyParticipant, createBucket, finalizeCeremony, finalizeCircuit, generateGetObjectPreSignedUrl, generatePreSignedUrlsParts, initEmptyWaitingQueueForCircuit, permanentlyStoreCurrentContributionTimeAndHash, processSignUpWithCustomClaims, progressToNextCircuitForContribution, progressToNextContributionStep, refreshParticipantAfterContributionVerification, registerAuthUser, resumeContributionAfterTimeoutExpiration, setupCeremony, startCeremony, startMultiPartUpload, stopCeremony, temporaryStoreCurrentContributionMultiPartUploadId, temporaryStoreCurrentContributionUploadedChunkData, verifycontribution };
|