@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
|
|
@@ -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');
|
|
@@ -27,10 +27,13 @@ var path = require('path');
|
|
|
27
27
|
var os = require('os');
|
|
28
28
|
var clientSsm = require('@aws-sdk/client-ssm');
|
|
29
29
|
var clientEc2 = require('@aws-sdk/client-ec2');
|
|
30
|
+
var ethers = require('ethers');
|
|
30
31
|
var functionsV1 = require('firebase-functions/v1');
|
|
31
32
|
var functionsV2 = require('firebase-functions/v2');
|
|
32
33
|
var timerNode = require('timer-node');
|
|
33
34
|
var snarkjs = require('snarkjs');
|
|
35
|
+
var apiSdk = require('@bandada/api-sdk');
|
|
36
|
+
var auth = require('firebase-admin/auth');
|
|
34
37
|
|
|
35
38
|
function _interopNamespaceDefault(e) {
|
|
36
39
|
var n = Object.create(null);
|
|
@@ -72,7 +75,7 @@ var LogLevel;
|
|
|
72
75
|
* @notice the set of Firebase Functions status codes. The codes are the same at the
|
|
73
76
|
* ones exposed by {@link https://github.com/grpc/grpc/blob/master/doc/statuscodes.md | gRPC}.
|
|
74
77
|
* @param errorCode <FunctionsErrorCode> - the set of possible error codes.
|
|
75
|
-
* @param message <string> - the error
|
|
78
|
+
* @param message <string> - the error message.
|
|
76
79
|
* @param [details] <string> - the details of the error (optional).
|
|
77
80
|
* @returns <HttpsError>
|
|
78
81
|
*/
|
|
@@ -144,7 +147,9 @@ const SPECIFIC_ERRORS = {
|
|
|
144
147
|
SE_VM_FAILED_COMMAND_EXECUTION: makeError("failed-precondition", "VM command execution failed", "Please, contact the coordinator if this error persists."),
|
|
145
148
|
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
149
|
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.")
|
|
150
|
+
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."),
|
|
151
|
+
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."),
|
|
152
|
+
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.")
|
|
148
153
|
};
|
|
149
154
|
/**
|
|
150
155
|
* A set of common errors.
|
|
@@ -163,6 +168,8 @@ const COMMON_ERRORS = {
|
|
|
163
168
|
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.")
|
|
164
169
|
};
|
|
165
170
|
|
|
171
|
+
dotenv.config();
|
|
172
|
+
let provider;
|
|
166
173
|
/**
|
|
167
174
|
* Return a configured and connected instance of the AWS S3 client.
|
|
168
175
|
* @dev this method check and utilize the environment variables to configure the connection
|
|
@@ -185,6 +192,36 @@ const getS3Client = async () => {
|
|
|
185
192
|
region: process.env.AWS_REGION
|
|
186
193
|
});
|
|
187
194
|
};
|
|
195
|
+
/**
|
|
196
|
+
* Returns a Prvider, connected via a configured JSON URL or else
|
|
197
|
+
* the ethers.js default provider, using configured API keys.
|
|
198
|
+
* @returns <ethers.providers.Provider> An Eth node provider
|
|
199
|
+
*/
|
|
200
|
+
const setEthProvider = () => {
|
|
201
|
+
if (provider)
|
|
202
|
+
return provider;
|
|
203
|
+
console.log(`setting new provider`);
|
|
204
|
+
// Use JSON URL if defined
|
|
205
|
+
// if ((hardhat as any).ethers) {
|
|
206
|
+
// console.log(`using hardhat.ethers provider`)
|
|
207
|
+
// provider = (hardhat as any).ethers.provider
|
|
208
|
+
// } else
|
|
209
|
+
if (process.env.ETH_PROVIDER_JSON_URL) {
|
|
210
|
+
console.log(`JSON URL provider at ${process.env.ETH_PROVIDER_JSON_URL}`);
|
|
211
|
+
provider = new ethers.providers.JsonRpcProvider({
|
|
212
|
+
url: process.env.ETH_PROVIDER_JSON_URL,
|
|
213
|
+
skipFetchSetup: true
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
// Otherwise, connect the default provider with ALchemy, Infura, or both
|
|
218
|
+
provider = ethers.providers.getDefaultProvider("homestead", {
|
|
219
|
+
alchemy: process.env.ETH_PROVIDER_ALCHEMY_API_KEY,
|
|
220
|
+
infura: process.env.ETH_PROVIDER_INFURA_API_KEY
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
return provider;
|
|
224
|
+
};
|
|
188
225
|
|
|
189
226
|
dotenv.config();
|
|
190
227
|
/**
|
|
@@ -287,7 +324,7 @@ const queryOpenedCeremonies = async () => {
|
|
|
287
324
|
const getCircuitDocumentByPosition = async (ceremonyId, sequencePosition) => {
|
|
288
325
|
// Query for all ceremony circuits.
|
|
289
326
|
const circuits = await getCeremonyCircuits(ceremonyId);
|
|
290
|
-
// Apply a filter using the sequence
|
|
327
|
+
// Apply a filter using the sequence position.
|
|
291
328
|
const matchedCircuits = circuits.filter((circuit) => circuit.data().sequencePosition === sequencePosition);
|
|
292
329
|
if (matchedCircuits.length !== 1)
|
|
293
330
|
logAndThrowError(COMMON_ERRORS.CM_NO_CIRCUIT_FOR_GIVEN_SEQUENCE_POSITION);
|
|
@@ -328,7 +365,7 @@ const downloadArtifactFromS3Bucket = async (bucketName, objectKey, localFilePath
|
|
|
328
365
|
const writeStream = node_fs.createWriteStream(localFilePath);
|
|
329
366
|
const streamPipeline = node_util.promisify(node_stream.pipeline);
|
|
330
367
|
await streamPipeline(response.body, writeStream);
|
|
331
|
-
writeStream.on(
|
|
368
|
+
writeStream.on("finish", () => {
|
|
332
369
|
writeStream.end();
|
|
333
370
|
});
|
|
334
371
|
};
|
|
@@ -452,12 +489,14 @@ const htmlEncodeCircuitData = (circuitDocument) => ({
|
|
|
452
489
|
const getGitHubVariables = () => {
|
|
453
490
|
if (!process.env.GITHUB_MINIMUM_FOLLOWERS ||
|
|
454
491
|
!process.env.GITHUB_MINIMUM_FOLLOWING ||
|
|
455
|
-
!process.env.GITHUB_MINIMUM_PUBLIC_REPOS
|
|
492
|
+
!process.env.GITHUB_MINIMUM_PUBLIC_REPOS ||
|
|
493
|
+
!process.env.GITHUB_MINIMUM_AGE)
|
|
456
494
|
logAndThrowError(COMMON_ERRORS.CM_WRONG_CONFIGURATION);
|
|
457
495
|
return {
|
|
458
496
|
minimumFollowers: Number(process.env.GITHUB_MINIMUM_FOLLOWERS),
|
|
459
497
|
minimumFollowing: Number(process.env.GITHUB_MINIMUM_FOLLOWING),
|
|
460
|
-
minimumPublicRepos: Number(process.env.GITHUB_MINIMUM_PUBLIC_REPOS)
|
|
498
|
+
minimumPublicRepos: Number(process.env.GITHUB_MINIMUM_PUBLIC_REPOS),
|
|
499
|
+
minimumAge: Number(process.env.GITHUB_MINIMUM_AGE)
|
|
461
500
|
};
|
|
462
501
|
};
|
|
463
502
|
/**
|
|
@@ -467,7 +506,7 @@ const getGitHubVariables = () => {
|
|
|
467
506
|
const getAWSVariables = () => {
|
|
468
507
|
if (!process.env.AWS_ACCESS_KEY_ID ||
|
|
469
508
|
!process.env.AWS_SECRET_ACCESS_KEY ||
|
|
470
|
-
!process.env.
|
|
509
|
+
!process.env.AWS_INSTANCE_PROFILE_ARN ||
|
|
471
510
|
!process.env.AWS_AMI_ID ||
|
|
472
511
|
!process.env.AWS_SNS_TOPIC_ARN)
|
|
473
512
|
logAndThrowError(COMMON_ERRORS.CM_WRONG_CONFIGURATION);
|
|
@@ -475,7 +514,7 @@ const getAWSVariables = () => {
|
|
|
475
514
|
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
476
515
|
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
|
477
516
|
region: process.env.AWS_REGION || "eu-central-1",
|
|
478
|
-
|
|
517
|
+
instanceProfileArn: process.env.AWS_INSTANCE_PROFILE_ARN,
|
|
479
518
|
amiId: process.env.AWS_AMI_ID,
|
|
480
519
|
snsTopic: process.env.AWS_SNS_TOPIC_ARN
|
|
481
520
|
};
|
|
@@ -521,7 +560,7 @@ dotenv.config();
|
|
|
521
560
|
const registerAuthUser = functions__namespace
|
|
522
561
|
.region("europe-west1")
|
|
523
562
|
.runWith({
|
|
524
|
-
memory: "
|
|
563
|
+
memory: "1GB"
|
|
525
564
|
})
|
|
526
565
|
.auth.user()
|
|
527
566
|
.onCreate(async (user) => {
|
|
@@ -553,16 +592,18 @@ const registerAuthUser = functions__namespace
|
|
|
553
592
|
email === process.env.CUSTOM_CLAIMS_COORDINATOR_EMAIL_ADDRESS_OR_DOMAIN)) {
|
|
554
593
|
const auth = admin.auth();
|
|
555
594
|
// if provider == github.com let's use our functions to check the user's reputation
|
|
556
|
-
if (user.providerData[0].providerId === "github.com") {
|
|
595
|
+
if (user.providerData.length > 0 && user.providerData[0].providerId === "github.com") {
|
|
557
596
|
const vars = getGitHubVariables();
|
|
558
597
|
// this return true or false
|
|
559
598
|
try {
|
|
560
|
-
const { reputable, avatarUrl: avatarURL } = await actions.githubReputation(user.providerData[0].uid, vars.minimumFollowing, vars.minimumFollowers, vars.minimumPublicRepos);
|
|
599
|
+
const { reputable, avatarUrl: avatarURL } = await actions.githubReputation(user.providerData[0].uid, vars.minimumFollowing, vars.minimumFollowers, vars.minimumPublicRepos, vars.minimumAge);
|
|
561
600
|
if (!reputable) {
|
|
562
601
|
// Delete user
|
|
563
602
|
await auth.deleteUser(user.uid);
|
|
564
603
|
// 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
|
|
604
|
+
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
|
|
605
|
+
? user.uid
|
|
606
|
+
: 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
607
|
}
|
|
567
608
|
// store locally
|
|
568
609
|
avatarUrl = avatarURL;
|
|
@@ -577,13 +618,13 @@ const registerAuthUser = functions__namespace
|
|
|
577
618
|
}
|
|
578
619
|
// Set document (nb. we refer to providerData[0] because we use Github OAuth provider only).
|
|
579
620
|
// In future releases we might want to loop through the providerData array as we support
|
|
580
|
-
// more providers.
|
|
621
|
+
// more providers.
|
|
581
622
|
await userRef.set({
|
|
582
623
|
name: encodedDisplayName,
|
|
583
624
|
encodedDisplayName,
|
|
584
625
|
// Metadata.
|
|
585
626
|
creationTime,
|
|
586
|
-
lastSignInTime,
|
|
627
|
+
lastSignInTime: lastSignInTime || creationTime,
|
|
587
628
|
// Optional.
|
|
588
629
|
email: email || "",
|
|
589
630
|
emailVerified: emailVerified || false,
|
|
@@ -593,7 +634,7 @@ const registerAuthUser = functions__namespace
|
|
|
593
634
|
// we want to create a new collection for the users to store the avatars
|
|
594
635
|
const avatarRef = firestore.collection(actions.commonTerms.collections.avatars.name).doc(uid);
|
|
595
636
|
await avatarRef.set({
|
|
596
|
-
avatarUrl: avatarUrl || ""
|
|
637
|
+
avatarUrl: avatarUrl || ""
|
|
597
638
|
});
|
|
598
639
|
printLog(`Authenticated user document with identifier ${uid} has been correctly stored`, LogLevel.DEBUG);
|
|
599
640
|
printLog(`Authenticated user avatar with identifier ${uid} has been correctly stored`, LogLevel.DEBUG);
|
|
@@ -606,7 +647,7 @@ const registerAuthUser = functions__namespace
|
|
|
606
647
|
const processSignUpWithCustomClaims = functions__namespace
|
|
607
648
|
.region("europe-west1")
|
|
608
649
|
.runWith({
|
|
609
|
-
memory: "
|
|
650
|
+
memory: "1GB"
|
|
610
651
|
})
|
|
611
652
|
.auth.user()
|
|
612
653
|
.onCreate(async (user) => {
|
|
@@ -647,7 +688,7 @@ dotenv.config();
|
|
|
647
688
|
const startCeremony = functions__namespace
|
|
648
689
|
.region("europe-west1")
|
|
649
690
|
.runWith({
|
|
650
|
-
memory: "
|
|
691
|
+
memory: "1GB"
|
|
651
692
|
})
|
|
652
693
|
.pubsub.schedule(`every 30 minutes`)
|
|
653
694
|
.onRun(async () => {
|
|
@@ -669,7 +710,7 @@ const startCeremony = functions__namespace
|
|
|
669
710
|
const stopCeremony = functions__namespace
|
|
670
711
|
.region("europe-west1")
|
|
671
712
|
.runWith({
|
|
672
|
-
memory: "
|
|
713
|
+
memory: "1GB"
|
|
673
714
|
})
|
|
674
715
|
.pubsub.schedule(`every 30 minutes`)
|
|
675
716
|
.onRun(async () => {
|
|
@@ -691,7 +732,7 @@ const stopCeremony = functions__namespace
|
|
|
691
732
|
const setupCeremony = functions__namespace
|
|
692
733
|
.region("europe-west1")
|
|
693
734
|
.runWith({
|
|
694
|
-
memory: "
|
|
735
|
+
memory: "1GB"
|
|
695
736
|
})
|
|
696
737
|
.https.onCall(async (data, context) => {
|
|
697
738
|
// Check if the user has the coordinator claim.
|
|
@@ -729,11 +770,13 @@ const setupCeremony = functions__namespace
|
|
|
729
770
|
// The VM unique identifier (if any).
|
|
730
771
|
let vmInstanceId = "";
|
|
731
772
|
// Get a new circuit document.
|
|
732
|
-
const
|
|
773
|
+
const ccp = actions.getCircuitsCollectionPath(ceremonyDoc.ref.id);
|
|
774
|
+
printLog(`CircuitsCollectionPath = ${ccp}`, LogLevel.DEBUG);
|
|
775
|
+
const circuitDoc = await firestore.collection(ccp).doc().get();
|
|
733
776
|
// Check if using the VM approach for contribution verification.
|
|
734
777
|
if (circuit.verification.cfOrVm === "VM" /* CircuitContributionVerificationMechanism.VM */) {
|
|
735
778
|
// VM command to be run at the startup.
|
|
736
|
-
const startupCommand = actions.vmBootstrapCommand(bucketName);
|
|
779
|
+
const startupCommand = actions.vmBootstrapCommand(`${bucketName}/circuits/${circuit.name}`);
|
|
737
780
|
// Get EC2 client.
|
|
738
781
|
const ec2Client = await createEC2Client();
|
|
739
782
|
// Get AWS variables.
|
|
@@ -742,7 +785,8 @@ const setupCeremony = functions__namespace
|
|
|
742
785
|
const vmCommands = actions.vmDependenciesAndCacheArtifactsCommand(`${bucketName}/${circuit.files?.initialZkeyStoragePath}`, `${bucketName}/${circuit.files?.potStoragePath}`, snsTopic, region);
|
|
743
786
|
printLog(`Check VM dependencies and cache artifacts commands ${vmCommands.join("\n")}`, LogLevel.DEBUG);
|
|
744
787
|
// Upload the post-startup commands script file.
|
|
745
|
-
|
|
788
|
+
printLog(`Uploading VM post-startup commands script file ${actions.vmBootstrapScriptFilename}`, LogLevel.DEBUG);
|
|
789
|
+
await uploadFileToBucketNoFile(bucketName, `circuits/${circuit.name}/${actions.vmBootstrapScriptFilename}`, vmCommands.join("\n"));
|
|
746
790
|
// Compute the VM disk space requirement (in GB).
|
|
747
791
|
const vmDiskSize = actions.computeDiskSizeForVM(circuit.zKeySizeInBytes, circuit.metadata?.pot);
|
|
748
792
|
printLog(`Check VM startup commands ${startupCommand.join("\n")}`, LogLevel.DEBUG);
|
|
@@ -765,12 +809,14 @@ const setupCeremony = functions__namespace
|
|
|
765
809
|
}
|
|
766
810
|
// Encode circuit data.
|
|
767
811
|
const encodedCircuit = htmlEncodeCircuitData(circuit);
|
|
812
|
+
printLog(`writing circuit data...`, LogLevel.DEBUG);
|
|
768
813
|
// Prepare tx to write circuit data.
|
|
769
814
|
batch.create(circuitDoc.ref, {
|
|
770
815
|
...encodedCircuit,
|
|
771
816
|
lastUpdated: getCurrentServerTimestampInMillis()
|
|
772
817
|
});
|
|
773
818
|
}
|
|
819
|
+
printLog(`Done handling circuits...`, LogLevel.DEBUG);
|
|
774
820
|
// Send txs in a batch (to avoid race conditions).
|
|
775
821
|
await batch.commit();
|
|
776
822
|
printLog(`Setup completed for ceremony ${ceremonyDoc.id}`, LogLevel.DEBUG);
|
|
@@ -783,7 +829,7 @@ const setupCeremony = functions__namespace
|
|
|
783
829
|
const initEmptyWaitingQueueForCircuit = functions__namespace
|
|
784
830
|
.region("europe-west1")
|
|
785
831
|
.runWith({
|
|
786
|
-
memory: "
|
|
832
|
+
memory: "1GB"
|
|
787
833
|
})
|
|
788
834
|
.firestore.document(`/${actions.commonTerms.collections.ceremonies.name}/{ceremony}/${actions.commonTerms.collections.circuits.name}/{circuit}`)
|
|
789
835
|
.onCreate(async (doc) => {
|
|
@@ -815,7 +861,7 @@ const initEmptyWaitingQueueForCircuit = functions__namespace
|
|
|
815
861
|
const finalizeCeremony = functions__namespace
|
|
816
862
|
.region("europe-west1")
|
|
817
863
|
.runWith({
|
|
818
|
-
memory: "
|
|
864
|
+
memory: "1GB"
|
|
819
865
|
})
|
|
820
866
|
.https.onCall(async (data, context) => {
|
|
821
867
|
if (!context.auth || !context.auth.token.coordinator)
|
|
@@ -836,7 +882,7 @@ const finalizeCeremony = functions__namespace
|
|
|
836
882
|
// Get ceremony circuits.
|
|
837
883
|
const circuits = await getCeremonyCircuits(ceremonyId);
|
|
838
884
|
// Get final contribution for each circuit.
|
|
839
|
-
// nb. the `getFinalContributionDocument` checks the
|
|
885
|
+
// nb. the `getFinalContributionDocument` checks the existence of the final contribution document (if not present, throws).
|
|
840
886
|
// Therefore, we just need to call the method without taking any data to verify the pre-condition of having already computed
|
|
841
887
|
// the final contributions for each ceremony circuit.
|
|
842
888
|
for await (const circuit of circuits)
|
|
@@ -889,9 +935,9 @@ dotenv.config();
|
|
|
889
935
|
* @dev true when the participant can participate (1.A, 3.B, 1.D); otherwise false.
|
|
890
936
|
*/
|
|
891
937
|
const checkParticipantForCeremony = functions__namespace
|
|
892
|
-
.region(
|
|
938
|
+
.region("europe-west1")
|
|
893
939
|
.runWith({
|
|
894
|
-
memory: "
|
|
940
|
+
memory: "1GB"
|
|
895
941
|
})
|
|
896
942
|
.https.onCall(async (data, context) => {
|
|
897
943
|
if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
|
|
@@ -960,7 +1006,7 @@ const checkParticipantForCeremony = functions__namespace
|
|
|
960
1006
|
participantDoc.ref.update({
|
|
961
1007
|
status: "EXHUMED" /* ParticipantStatus.EXHUMED */,
|
|
962
1008
|
contributions,
|
|
963
|
-
tempContributionData: tempContributionData
|
|
1009
|
+
tempContributionData: tempContributionData || firestore.FieldValue.delete(),
|
|
964
1010
|
contributionStep: "DOWNLOADING" /* ParticipantContributionStep.DOWNLOADING */,
|
|
965
1011
|
contributionStartedAt: 0,
|
|
966
1012
|
verificationStartedAt: firestore.FieldValue.delete(),
|
|
@@ -993,9 +1039,9 @@ const checkParticipantForCeremony = functions__namespace
|
|
|
993
1039
|
* 2) the participant has just finished the contribution for a circuit (contributionProgress != 0 && status = CONTRIBUTED && contributionStep = COMPLETED).
|
|
994
1040
|
*/
|
|
995
1041
|
const progressToNextCircuitForContribution = functions__namespace
|
|
996
|
-
.region(
|
|
1042
|
+
.region("europe-west1")
|
|
997
1043
|
.runWith({
|
|
998
|
-
memory: "
|
|
1044
|
+
memory: "1GB"
|
|
999
1045
|
})
|
|
1000
1046
|
.https.onCall(async (data, context) => {
|
|
1001
1047
|
if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
|
|
@@ -1040,9 +1086,9 @@ const progressToNextCircuitForContribution = functions__namespace
|
|
|
1040
1086
|
* 5) Completed contribution computation and verification.
|
|
1041
1087
|
*/
|
|
1042
1088
|
const progressToNextContributionStep = functions__namespace
|
|
1043
|
-
.region(
|
|
1089
|
+
.region("europe-west1")
|
|
1044
1090
|
.runWith({
|
|
1045
|
-
memory: "
|
|
1091
|
+
memory: "1GB"
|
|
1046
1092
|
})
|
|
1047
1093
|
.https.onCall(async (data, context) => {
|
|
1048
1094
|
if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
|
|
@@ -1091,9 +1137,9 @@ const progressToNextContributionStep = functions__namespace
|
|
|
1091
1137
|
* @dev enable the current contributor to resume a contribution from where it had left off.
|
|
1092
1138
|
*/
|
|
1093
1139
|
const permanentlyStoreCurrentContributionTimeAndHash = functions__namespace
|
|
1094
|
-
.region(
|
|
1140
|
+
.region("europe-west1")
|
|
1095
1141
|
.runWith({
|
|
1096
|
-
memory: "
|
|
1142
|
+
memory: "1GB"
|
|
1097
1143
|
})
|
|
1098
1144
|
.https.onCall(async (data, context) => {
|
|
1099
1145
|
if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
|
|
@@ -1133,9 +1179,9 @@ const permanentlyStoreCurrentContributionTimeAndHash = functions__namespace
|
|
|
1133
1179
|
* @dev enable the current contributor to resume a multi-part upload from where it had left off.
|
|
1134
1180
|
*/
|
|
1135
1181
|
const temporaryStoreCurrentContributionMultiPartUploadId = functions__namespace
|
|
1136
|
-
.region(
|
|
1182
|
+
.region("europe-west1")
|
|
1137
1183
|
.runWith({
|
|
1138
|
-
memory: "
|
|
1184
|
+
memory: "1GB"
|
|
1139
1185
|
})
|
|
1140
1186
|
.https.onCall(async (data, context) => {
|
|
1141
1187
|
if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
|
|
@@ -1171,9 +1217,9 @@ const temporaryStoreCurrentContributionMultiPartUploadId = functions__namespace
|
|
|
1171
1217
|
* @dev enable the current contributor to resume a multi-part upload from where it had left off.
|
|
1172
1218
|
*/
|
|
1173
1219
|
const temporaryStoreCurrentContributionUploadedChunkData = functions__namespace
|
|
1174
|
-
.region(
|
|
1220
|
+
.region("europe-west1")
|
|
1175
1221
|
.runWith({
|
|
1176
|
-
memory: "
|
|
1222
|
+
memory: "1GB"
|
|
1177
1223
|
})
|
|
1178
1224
|
.https.onCall(async (data, context) => {
|
|
1179
1225
|
if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
|
|
@@ -1213,9 +1259,9 @@ const temporaryStoreCurrentContributionUploadedChunkData = functions__namespace
|
|
|
1213
1259
|
* contributed to every selected ceremony circuits (= DONE).
|
|
1214
1260
|
*/
|
|
1215
1261
|
const checkAndPrepareCoordinatorForFinalization = functions__namespace
|
|
1216
|
-
.region(
|
|
1262
|
+
.region("europe-west1")
|
|
1217
1263
|
.runWith({
|
|
1218
|
-
memory: "
|
|
1264
|
+
memory: "1GB"
|
|
1219
1265
|
})
|
|
1220
1266
|
.https.onCall(async (data, context) => {
|
|
1221
1267
|
if (!context.auth || !context.auth.token.coordinator)
|
|
@@ -1292,7 +1338,7 @@ const coordinate = async (participant, circuit, isSingleParticipantCoordination,
|
|
|
1292
1338
|
if (isSingleParticipantCoordination) {
|
|
1293
1339
|
// Scenario (A).
|
|
1294
1340
|
if (emptyWaitingQueue) {
|
|
1295
|
-
printLog(`Coordinate - executing scenario A - emptyWaitingQueue`, LogLevel.
|
|
1341
|
+
printLog(`Coordinate - executing scenario A - emptyWaitingQueue`, LogLevel.INFO);
|
|
1296
1342
|
// Update.
|
|
1297
1343
|
newCurrentContributorId = participant.id;
|
|
1298
1344
|
newParticipantStatus = "CONTRIBUTING" /* ParticipantStatus.CONTRIBUTING */;
|
|
@@ -1301,14 +1347,14 @@ const coordinate = async (participant, circuit, isSingleParticipantCoordination,
|
|
|
1301
1347
|
}
|
|
1302
1348
|
// Scenario (A).
|
|
1303
1349
|
else if (participantResumingAfterTimeoutExpiration) {
|
|
1304
|
-
printLog(`Coordinate - executing scenario A - single - participantResumingAfterTimeoutExpiration`, LogLevel.
|
|
1350
|
+
printLog(`Coordinate - executing scenario A - single - participantResumingAfterTimeoutExpiration`, LogLevel.INFO);
|
|
1305
1351
|
newParticipantStatus = "CONTRIBUTING" /* ParticipantStatus.CONTRIBUTING */;
|
|
1306
1352
|
newContributionStep = "DOWNLOADING" /* ParticipantContributionStep.DOWNLOADING */;
|
|
1307
1353
|
newCurrentContributorId = participant.id;
|
|
1308
1354
|
}
|
|
1309
1355
|
// Scenario (B).
|
|
1310
1356
|
else if (participantIsNotCurrentContributor) {
|
|
1311
|
-
printLog(`Coordinate - executing scenario B - single - participantIsNotCurrentContributor`, LogLevel.
|
|
1357
|
+
printLog(`Coordinate - executing scenario B - single - participantIsNotCurrentContributor`, LogLevel.INFO);
|
|
1312
1358
|
newCurrentContributorId = currentContributor;
|
|
1313
1359
|
newParticipantStatus = "WAITING" /* ParticipantStatus.WAITING */;
|
|
1314
1360
|
newContributors.push(participant.id);
|
|
@@ -1327,7 +1373,7 @@ const coordinate = async (participant, circuit, isSingleParticipantCoordination,
|
|
|
1327
1373
|
});
|
|
1328
1374
|
}
|
|
1329
1375
|
else if (participantIsCurrentContributor && participantCompletedOneOrAllContributions && !!ceremonyId) {
|
|
1330
|
-
printLog(`Coordinate - executing scenario C - multi - participantIsCurrentContributor && participantCompletedOneOrAllContributions`, LogLevel.
|
|
1376
|
+
printLog(`Coordinate - executing scenario C - multi - participantIsCurrentContributor && participantCompletedOneOrAllContributions`, LogLevel.INFO);
|
|
1331
1377
|
newParticipantStatus = "CONTRIBUTING" /* ParticipantStatus.CONTRIBUTING */;
|
|
1332
1378
|
newContributionStep = "DOWNLOADING" /* ParticipantContributionStep.DOWNLOADING */;
|
|
1333
1379
|
// Remove from waiting queue of circuit X.
|
|
@@ -1345,7 +1391,7 @@ const coordinate = async (participant, circuit, isSingleParticipantCoordination,
|
|
|
1345
1391
|
contributionStartedAt: getCurrentServerTimestampInMillis(),
|
|
1346
1392
|
lastUpdated: getCurrentServerTimestampInMillis()
|
|
1347
1393
|
});
|
|
1348
|
-
printLog(`Participant ${newCurrentContributorId} is the new current contributor for circuit ${circuit.id}`, LogLevel.
|
|
1394
|
+
printLog(`Participant ${newCurrentContributorId} is the new current contributor for circuit ${circuit.id}`, LogLevel.INFO);
|
|
1349
1395
|
}
|
|
1350
1396
|
}
|
|
1351
1397
|
// Prepare tx - must be done for all Scenarios.
|
|
@@ -1365,54 +1411,74 @@ const coordinate = async (participant, circuit, isSingleParticipantCoordination,
|
|
|
1365
1411
|
* Wait until the command has completed its execution inside the VM.
|
|
1366
1412
|
* @dev this method implements a custom interval to check 5 times after 1 minute if the command execution
|
|
1367
1413
|
* has been completed or not by calling the `retrieveCommandStatus` method.
|
|
1368
|
-
* @param {any} resolve the promise.
|
|
1369
|
-
* @param {any} reject the promise.
|
|
1370
1414
|
* @param {SSMClient} ssm the SSM client.
|
|
1371
1415
|
* @param {string} vmInstanceId the unique identifier of the VM instance.
|
|
1372
1416
|
* @param {string} commandId the unique identifier of the VM command.
|
|
1373
1417
|
* @returns <Promise<void>> true when the command execution succeed; otherwise false.
|
|
1374
1418
|
*/
|
|
1375
|
-
const waitForVMCommandExecution = (
|
|
1376
|
-
const
|
|
1419
|
+
const waitForVMCommandExecution = (ssm, vmInstanceId, commandId) => new Promise((resolve, reject) => {
|
|
1420
|
+
const poll = async () => {
|
|
1377
1421
|
try {
|
|
1378
1422
|
// Get command status.
|
|
1379
1423
|
const cmdStatus = await actions.retrieveCommandStatus(ssm, vmInstanceId, commandId);
|
|
1380
1424
|
printLog(`Checking command ${commandId} status => ${cmdStatus}`, LogLevel.DEBUG);
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1425
|
+
let error;
|
|
1426
|
+
switch (cmdStatus) {
|
|
1427
|
+
case clientSsm.CommandInvocationStatus.CANCELLING:
|
|
1428
|
+
case clientSsm.CommandInvocationStatus.CANCELLED: {
|
|
1429
|
+
error = SPECIFIC_ERRORS.SE_VM_CANCELLED_COMMAND_EXECUTION;
|
|
1430
|
+
break;
|
|
1431
|
+
}
|
|
1432
|
+
case clientSsm.CommandInvocationStatus.DELAYED: {
|
|
1433
|
+
error = SPECIFIC_ERRORS.SE_VM_DELAYED_COMMAND_EXECUTION;
|
|
1434
|
+
break;
|
|
1435
|
+
}
|
|
1436
|
+
case clientSsm.CommandInvocationStatus.FAILED: {
|
|
1437
|
+
error = SPECIFIC_ERRORS.SE_VM_FAILED_COMMAND_EXECUTION;
|
|
1438
|
+
break;
|
|
1439
|
+
}
|
|
1440
|
+
case clientSsm.CommandInvocationStatus.TIMED_OUT: {
|
|
1441
|
+
error = SPECIFIC_ERRORS.SE_VM_TIMEDOUT_COMMAND_EXECUTION;
|
|
1442
|
+
break;
|
|
1443
|
+
}
|
|
1444
|
+
case clientSsm.CommandInvocationStatus.IN_PROGRESS:
|
|
1445
|
+
case clientSsm.CommandInvocationStatus.PENDING: {
|
|
1446
|
+
// wait a minute and poll again
|
|
1447
|
+
setTimeout(poll, 60000);
|
|
1448
|
+
return;
|
|
1449
|
+
}
|
|
1450
|
+
case clientSsm.CommandInvocationStatus.SUCCESS: {
|
|
1451
|
+
printLog(`Command ${commandId} successfully completed`, LogLevel.DEBUG);
|
|
1452
|
+
// Resolve the promise.
|
|
1453
|
+
resolve();
|
|
1454
|
+
return;
|
|
1455
|
+
}
|
|
1456
|
+
default: {
|
|
1457
|
+
logAndThrowError(SPECIFIC_ERRORS.SE_VM_UNKNOWN_COMMAND_STATUS);
|
|
1458
|
+
}
|
|
1397
1459
|
}
|
|
1398
|
-
|
|
1399
|
-
logAndThrowError(
|
|
1400
|
-
reject();
|
|
1460
|
+
if (error) {
|
|
1461
|
+
logAndThrowError(error);
|
|
1401
1462
|
}
|
|
1402
1463
|
}
|
|
1403
1464
|
catch (error) {
|
|
1404
1465
|
printLog(`Invalid command ${commandId} execution`, LogLevel.DEBUG);
|
|
1466
|
+
const ec2 = await createEC2Client();
|
|
1467
|
+
// if it errors out, let's just log it as a warning so the coordinator is aware
|
|
1468
|
+
try {
|
|
1469
|
+
await actions.stopEC2Instance(ec2, vmInstanceId);
|
|
1470
|
+
}
|
|
1471
|
+
catch (stopError) {
|
|
1472
|
+
printLog(`Error while stopping VM instance ${vmInstanceId} - Error ${stopError}`, LogLevel.WARN);
|
|
1473
|
+
}
|
|
1405
1474
|
if (!error.toString().includes(commandId))
|
|
1406
1475
|
logAndThrowError(COMMON_ERRORS.CM_INVALID_COMMAND_EXECUTION);
|
|
1407
1476
|
// Reject the promise.
|
|
1408
1477
|
reject();
|
|
1409
1478
|
}
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
}
|
|
1414
|
-
}, 60000); // 1 minute.
|
|
1415
|
-
};
|
|
1479
|
+
};
|
|
1480
|
+
setTimeout(poll, 60000);
|
|
1481
|
+
});
|
|
1416
1482
|
/**
|
|
1417
1483
|
* This method is used to coordinate the waiting queues of ceremony circuits.
|
|
1418
1484
|
* @dev this cloud function is triggered whenever an update of a document related to a participant of a ceremony occurs.
|
|
@@ -1433,9 +1499,9 @@ const waitForVMCommandExecution = (resolve, reject, ssm, vmInstanceId, commandId
|
|
|
1433
1499
|
* - Just completed a contribution or all contributions for each circuit. If yes, coordinate (multi-participant scenario).
|
|
1434
1500
|
*/
|
|
1435
1501
|
const coordinateCeremonyParticipant = functionsV1__namespace
|
|
1436
|
-
.region(
|
|
1502
|
+
.region("europe-west1")
|
|
1437
1503
|
.runWith({
|
|
1438
|
-
memory: "
|
|
1504
|
+
memory: "1GB"
|
|
1439
1505
|
})
|
|
1440
1506
|
.firestore.document(`${actions.commonTerms.collections.ceremonies.name}/{ceremonyId}/${actions.commonTerms.collections.participants.name}/{participantId}`)
|
|
1441
1507
|
.onUpdate(async (participantChanges) => {
|
|
@@ -1451,8 +1517,8 @@ const coordinateCeremonyParticipant = functionsV1__namespace
|
|
|
1451
1517
|
// Extract data.
|
|
1452
1518
|
const { contributionProgress: prevContributionProgress, status: prevStatus, contributionStep: prevContributionStep } = exParticipant.data();
|
|
1453
1519
|
const { contributionProgress: changedContributionProgress, status: changedStatus, contributionStep: changedContributionStep } = changedParticipant.data();
|
|
1454
|
-
printLog(`Coordinate participant ${exParticipant.id} for ceremony ${ceremonyId}`, LogLevel.
|
|
1455
|
-
printLog(`Participant status: ${prevStatus} => ${changedStatus} - Participant contribution step: ${prevContributionStep} => ${changedContributionStep}`, LogLevel.
|
|
1520
|
+
printLog(`Coordinate participant ${exParticipant.id} for ceremony ${ceremonyId}`, LogLevel.INFO);
|
|
1521
|
+
printLog(`Participant status: ${prevStatus} => ${changedStatus} - Participant contribution step: ${prevContributionStep} => ${changedContributionStep}`, LogLevel.INFO);
|
|
1456
1522
|
// Define pre-conditions.
|
|
1457
1523
|
const participantReadyToContribute = changedStatus === "READY" /* ParticipantStatus.READY */;
|
|
1458
1524
|
const participantReadyForFirstContribution = participantReadyToContribute && prevContributionProgress === 0;
|
|
@@ -1462,8 +1528,8 @@ const coordinateCeremonyParticipant = functionsV1__namespace
|
|
|
1462
1528
|
prevContributionProgress !== 0;
|
|
1463
1529
|
const participantCompletedEveryCircuitContribution = changedStatus === "DONE" /* ParticipantStatus.DONE */ && prevStatus !== "DONE" /* ParticipantStatus.DONE */;
|
|
1464
1530
|
const participantCompletedContribution = prevContributionProgress === changedContributionProgress &&
|
|
1465
|
-
prevStatus === "CONTRIBUTING" /* ParticipantStatus.CONTRIBUTING */
|
|
1466
|
-
|
|
1531
|
+
(prevStatus === "CONTRIBUTING" /* ParticipantStatus.CONTRIBUTING */ ||
|
|
1532
|
+
prevContributionStep === "VERIFYING" /* ParticipantContributionStep.VERIFYING */) &&
|
|
1467
1533
|
changedStatus === "CONTRIBUTED" /* ParticipantStatus.CONTRIBUTED */ &&
|
|
1468
1534
|
changedContributionStep === "COMPLETED" /* ParticipantContributionStep.COMPLETED */;
|
|
1469
1535
|
// Step (2).
|
|
@@ -1471,7 +1537,7 @@ const coordinateCeremonyParticipant = functionsV1__namespace
|
|
|
1471
1537
|
participantResumingContributionAfterTimeout ||
|
|
1472
1538
|
participantReadyForNextContribution) {
|
|
1473
1539
|
// Step (2.A).
|
|
1474
|
-
printLog(`Participant is ready for first contribution (${participantReadyForFirstContribution}) or for the next contribution (${participantReadyForNextContribution}) or is resuming after a timeout expiration (${participantResumingContributionAfterTimeout})`, LogLevel.
|
|
1540
|
+
printLog(`Participant is ready for first contribution (${participantReadyForFirstContribution}) or for the next contribution (${participantReadyForNextContribution}) or is resuming after a timeout expiration (${participantResumingContributionAfterTimeout})`, LogLevel.INFO);
|
|
1475
1541
|
// Get the circuit.
|
|
1476
1542
|
const circuit = await getCircuitDocumentByPosition(ceremonyId, changedContributionProgress);
|
|
1477
1543
|
// Coordinate.
|
|
@@ -1480,7 +1546,7 @@ const coordinateCeremonyParticipant = functionsV1__namespace
|
|
|
1480
1546
|
}
|
|
1481
1547
|
else if (participantCompletedContribution || participantCompletedEveryCircuitContribution) {
|
|
1482
1548
|
// Step (2.B).
|
|
1483
|
-
printLog(`Participant completed a contribution (${participantCompletedContribution}) or every contribution for each circuit (${participantCompletedEveryCircuitContribution})`, LogLevel.
|
|
1549
|
+
printLog(`Participant completed a contribution (${participantCompletedContribution}) or every contribution for each circuit (${participantCompletedEveryCircuitContribution})`, LogLevel.INFO);
|
|
1484
1550
|
// Get the circuit.
|
|
1485
1551
|
const circuit = await getCircuitDocumentByPosition(ceremonyId, prevContributionProgress);
|
|
1486
1552
|
// Coordinate.
|
|
@@ -1504,11 +1570,9 @@ const checkIfVMRunning = async (ec2, vmInstanceId, attempts = 5) => {
|
|
|
1504
1570
|
const isVMRunning = await actions.checkIfRunning(ec2, vmInstanceId);
|
|
1505
1571
|
if (!isVMRunning) {
|
|
1506
1572
|
printLog(`VM not running, ${attempts - 1} attempts remaining. Retrying in 1 minute...`, LogLevel.DEBUG);
|
|
1507
|
-
return
|
|
1508
|
-
}
|
|
1509
|
-
else {
|
|
1510
|
-
return true;
|
|
1573
|
+
return checkIfVMRunning(ec2, vmInstanceId, attempts - 1);
|
|
1511
1574
|
}
|
|
1575
|
+
return true;
|
|
1512
1576
|
};
|
|
1513
1577
|
/**
|
|
1514
1578
|
* Verify the contribution of a participant computed while contributing to a specific circuit of a ceremony.
|
|
@@ -1536,266 +1600,296 @@ const checkIfVMRunning = async (ec2, vmInstanceId, attempts = 5) => {
|
|
|
1536
1600
|
* 1.A.4.C.1) If true, update circuit waiting for queue and average timings accordingly to contribution verification results;
|
|
1537
1601
|
* 2) Send all updates atomically to the Firestore database.
|
|
1538
1602
|
*/
|
|
1539
|
-
const verifycontribution = functionsV2__namespace.https.onCall({ memory: "
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
!request.data.
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
!process.env.
|
|
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
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
//
|
|
1610
|
-
|
|
1611
|
-
verificationTaskTimer.
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
// Download from bucket.
|
|
1618
|
-
// nb. the transcript MUST be uploaded from the VM by verification commands.
|
|
1619
|
-
await downloadArtifactFromS3Bucket(bucketName, verificationTranscriptStoragePathAndFilename, verificationTranscriptTemporaryLocalPath);
|
|
1620
|
-
// Read the verification trascript and validate data by checking for core info ("ZKey Ok!").
|
|
1621
|
-
const content = fs.readFileSync(verificationTranscriptTemporaryLocalPath, "utf-8");
|
|
1622
|
-
if (content.includes("ZKey Ok!"))
|
|
1623
|
-
isContributionValid = true;
|
|
1624
|
-
// If the contribution is valid, then format and store the trascript.
|
|
1625
|
-
if (isContributionValid) {
|
|
1626
|
-
// eslint-disable-next-line no-control-regex
|
|
1627
|
-
const updated = content.replace(/\x1b[[0-9;]*m/g, "");
|
|
1628
|
-
fs.writeFileSync(verificationTranscriptTemporaryLocalPath, updated);
|
|
1603
|
+
const verifycontribution = functionsV2__namespace.https.onCall({ memory: "32GiB", timeoutSeconds: 3600, region: "europe-west1", cpu: 8 }, async (request) => {
|
|
1604
|
+
try {
|
|
1605
|
+
if (!request.auth || (!request.auth.token.participant && !request.auth.token.coordinator))
|
|
1606
|
+
logAndThrowError(SPECIFIC_ERRORS.SE_AUTH_NO_CURRENT_AUTH_USER);
|
|
1607
|
+
if (!request.data.ceremonyId ||
|
|
1608
|
+
!request.data.circuitId ||
|
|
1609
|
+
!request.data.contributorOrCoordinatorIdentifier ||
|
|
1610
|
+
!request.data.bucketName)
|
|
1611
|
+
logAndThrowError(COMMON_ERRORS.CM_MISSING_OR_WRONG_INPUT_DATA);
|
|
1612
|
+
if (!process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_NAME ||
|
|
1613
|
+
!process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_VERSION ||
|
|
1614
|
+
!process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_COMMIT_HASH)
|
|
1615
|
+
logAndThrowError(COMMON_ERRORS.CM_WRONG_CONFIGURATION);
|
|
1616
|
+
const BUCKET_NAME_REGEX = /^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$/;
|
|
1617
|
+
if (!BUCKET_NAME_REGEX.test(request.data.bucketName))
|
|
1618
|
+
logAndThrowError(SPECIFIC_ERRORS.WRONG_BUCKET_NAME);
|
|
1619
|
+
// Step (0).
|
|
1620
|
+
// Prepare and start timer.
|
|
1621
|
+
const verifyContributionTimer = new timerNode.Timer({ label: actions.commonTerms.cloudFunctionsNames.verifyContribution });
|
|
1622
|
+
verifyContributionTimer.start();
|
|
1623
|
+
// Get DB.
|
|
1624
|
+
const firestore = admin.firestore();
|
|
1625
|
+
// Prepare batch of txs.
|
|
1626
|
+
const batch = firestore.batch();
|
|
1627
|
+
// Extract data.
|
|
1628
|
+
const { ceremonyId, circuitId, contributorOrCoordinatorIdentifier, bucketName } = request.data;
|
|
1629
|
+
const userId = request.auth?.uid;
|
|
1630
|
+
// Look for the ceremony, circuit and participant document.
|
|
1631
|
+
const ceremonyDoc = await getDocumentById(actions.commonTerms.collections.ceremonies.name, ceremonyId);
|
|
1632
|
+
const circuitDoc = await getDocumentById(actions.getCircuitsCollectionPath(ceremonyId), circuitId);
|
|
1633
|
+
const participantDoc = await getDocumentById(actions.getParticipantsCollectionPath(ceremonyId), userId);
|
|
1634
|
+
if (!ceremonyDoc.data() || !circuitDoc.data() || !participantDoc.data())
|
|
1635
|
+
logAndThrowError(COMMON_ERRORS.CM_INEXISTENT_DOCUMENT_DATA);
|
|
1636
|
+
// Extract documents data.
|
|
1637
|
+
const { state } = ceremonyDoc.data();
|
|
1638
|
+
const { status, contributions, verificationStartedAt, contributionStartedAt } = participantDoc.data();
|
|
1639
|
+
const { waitingQueue, prefix, avgTimings, verification, files } = circuitDoc.data();
|
|
1640
|
+
const { completedContributions, failedContributions } = waitingQueue;
|
|
1641
|
+
const { contributionComputation: avgContributionComputationTime, fullContribution: avgFullContributionTime, verifyCloudFunction: avgVerifyCloudFunctionTime } = avgTimings;
|
|
1642
|
+
const { cfOrVm, vm } = verification;
|
|
1643
|
+
// we might not have it if the circuit is not using VM.
|
|
1644
|
+
let vmInstanceId = "";
|
|
1645
|
+
if (vm)
|
|
1646
|
+
vmInstanceId = vm.vmInstanceId;
|
|
1647
|
+
// Define pre-conditions.
|
|
1648
|
+
const isFinalizing = state === "CLOSED" /* CeremonyState.CLOSED */ && request.auth && request.auth.token.coordinator; // true only when the coordinator verifies the final contributions.
|
|
1649
|
+
const isContributing = status === "CONTRIBUTING" /* ParticipantStatus.CONTRIBUTING */;
|
|
1650
|
+
const isUsingVM = cfOrVm === "VM" /* CircuitContributionVerificationMechanism.VM */ && !!vmInstanceId;
|
|
1651
|
+
// Prepare state.
|
|
1652
|
+
let isContributionValid = false;
|
|
1653
|
+
let verifyCloudFunctionExecutionTime = 0; // time spent while executing the verify contribution cloud function.
|
|
1654
|
+
let verifyCloudFunctionTime = 0; // time spent while executing the core business logic of this cloud function.
|
|
1655
|
+
let fullContributionTime = 0; // time spent while doing non-verification contributions tasks (download, compute, upload).
|
|
1656
|
+
let contributionComputationTime = 0; // time spent while computing the contribution.
|
|
1657
|
+
let lastZkeyBlake2bHash = ""; // the Blake2B hash of the last zKey.
|
|
1658
|
+
let verificationTranscriptTemporaryLocalPath = ""; // the local temporary path for the verification transcript.
|
|
1659
|
+
let transcriptBlake2bHash = ""; // the Blake2B hash of the verification transcript.
|
|
1660
|
+
let commandId = ""; // the unique identifier of the VM command.
|
|
1661
|
+
// Derive necessary data.
|
|
1662
|
+
const lastZkeyIndex = actions.formatZkeyIndex(completedContributions + 1);
|
|
1663
|
+
const verificationTranscriptCompleteFilename = `${prefix}_${isFinalizing
|
|
1664
|
+
? `${contributorOrCoordinatorIdentifier}_${actions.finalContributionIndex}_verification_transcript.log`
|
|
1665
|
+
: `${lastZkeyIndex}_${contributorOrCoordinatorIdentifier}_verification_transcript.log`}`;
|
|
1666
|
+
const lastZkeyFilename = `${prefix}_${isFinalizing ? actions.finalContributionIndex : lastZkeyIndex}.zkey`;
|
|
1667
|
+
// Prepare state for VM verification (if needed).
|
|
1668
|
+
const ec2 = await createEC2Client();
|
|
1669
|
+
const ssm = await createSSMClient();
|
|
1670
|
+
// Step (1.A.1).
|
|
1671
|
+
// Get storage paths.
|
|
1672
|
+
const verificationTranscriptStoragePathAndFilename = actions.getTranscriptStorageFilePath(prefix, verificationTranscriptCompleteFilename);
|
|
1673
|
+
// the zKey storage path is required to be sent to the VM api
|
|
1674
|
+
const lastZkeyStoragePath = actions.getZkeyStorageFilePath(prefix, `${prefix}_${isFinalizing ? actions.finalContributionIndex : lastZkeyIndex}.zkey`);
|
|
1675
|
+
const verificationTaskTimer = new timerNode.Timer({ label: `${ceremonyId}-${circuitId}-${participantDoc.id}` });
|
|
1676
|
+
const dumpLog = async (path) => {
|
|
1677
|
+
printLog(`transcript >>>>>>`, LogLevel.DEBUG);
|
|
1678
|
+
try {
|
|
1679
|
+
const data = await fs.promises.readFile(path, "utf8");
|
|
1680
|
+
printLog(data, LogLevel.DEBUG);
|
|
1629
1681
|
}
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
.
|
|
1636
|
-
.
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
// Sleep ~3 seconds to wait for verification transcription.
|
|
1640
|
-
await sleep(3000);
|
|
1641
|
-
// Step (1.A.4.A.1).
|
|
1682
|
+
catch (readError) {
|
|
1683
|
+
printLog(readError, LogLevel.ERROR);
|
|
1684
|
+
}
|
|
1685
|
+
};
|
|
1686
|
+
const completeVerification = async () => {
|
|
1687
|
+
// Stop verification task timer.
|
|
1688
|
+
printLog("Completing verification", LogLevel.DEBUG);
|
|
1689
|
+
verificationTaskTimer.stop();
|
|
1690
|
+
verifyCloudFunctionExecutionTime = verificationTaskTimer.ms();
|
|
1642
1691
|
if (isUsingVM) {
|
|
1643
|
-
//
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1692
|
+
// Create temporary path.
|
|
1693
|
+
verificationTranscriptTemporaryLocalPath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}.log`);
|
|
1694
|
+
await sleep(1000); // wait 1s for file creation.
|
|
1695
|
+
// Download from bucket.
|
|
1696
|
+
// nb. the transcript MUST be uploaded from the VM by verification commands.
|
|
1697
|
+
await downloadArtifactFromS3Bucket(bucketName, verificationTranscriptStoragePathAndFilename, verificationTranscriptTemporaryLocalPath);
|
|
1698
|
+
// Read the verification trascript and validate data by checking for core info ("ZKey Ok!").
|
|
1699
|
+
const content = fs.readFileSync(verificationTranscriptTemporaryLocalPath, "utf-8");
|
|
1700
|
+
if (content.includes("ZKey Ok!"))
|
|
1701
|
+
isContributionValid = true;
|
|
1702
|
+
// If the contribution is valid, then format and store the trascript.
|
|
1703
|
+
if (isContributionValid) {
|
|
1704
|
+
// eslint-disable-next-line no-control-regex
|
|
1705
|
+
const updated = content.replace(/\x1b[[0-9;]*m/g, "");
|
|
1706
|
+
fs.writeFileSync(verificationTranscriptTemporaryLocalPath, updated);
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
printLog(`The contribution has been verified - Result ${isContributionValid}`, LogLevel.DEBUG);
|
|
1710
|
+
// Create a new contribution document.
|
|
1711
|
+
const contributionDoc = await firestore
|
|
1712
|
+
.collection(actions.getContributionsCollectionPath(ceremonyId, circuitId))
|
|
1713
|
+
.doc()
|
|
1714
|
+
.get();
|
|
1715
|
+
// Step (1.A.4).
|
|
1716
|
+
if (isContributionValid) {
|
|
1717
|
+
// Sleep ~3 seconds to wait for verification transcription.
|
|
1718
|
+
await sleep(3000);
|
|
1719
|
+
// Step (1.A.4.A.1).
|
|
1720
|
+
if (isUsingVM) {
|
|
1721
|
+
// Retrieve the contribution hash from the command output.
|
|
1722
|
+
lastZkeyBlake2bHash = await actions.retrieveCommandOutput(ssm, vmInstanceId, commandId);
|
|
1723
|
+
const hashRegex = /[a-fA-F0-9]{64}/;
|
|
1724
|
+
const match = lastZkeyBlake2bHash.match(hashRegex);
|
|
1725
|
+
lastZkeyBlake2bHash = match.at(0);
|
|
1726
|
+
// re upload the formatted verification transcript
|
|
1727
|
+
await uploadFileToBucket(bucketName, verificationTranscriptStoragePathAndFilename, verificationTranscriptTemporaryLocalPath, true);
|
|
1728
|
+
}
|
|
1729
|
+
else {
|
|
1730
|
+
// Upload verification transcript.
|
|
1731
|
+
/// nb. do not use multi-part upload here due to small file size.
|
|
1732
|
+
await uploadFileToBucket(bucketName, verificationTranscriptStoragePathAndFilename, verificationTranscriptTemporaryLocalPath, true);
|
|
1733
|
+
}
|
|
1734
|
+
// Compute verification transcript hash.
|
|
1735
|
+
transcriptBlake2bHash = await actions.blake512FromPath(verificationTranscriptTemporaryLocalPath);
|
|
1736
|
+
// Free resources by unlinking transcript temporary file.
|
|
1737
|
+
fs.unlinkSync(verificationTranscriptTemporaryLocalPath);
|
|
1738
|
+
// Filter participant contributions to find the data related to the one verified.
|
|
1739
|
+
const participantContributions = contributions.filter((contribution) => !!contribution.hash && !!contribution.computationTime && !contribution.doc);
|
|
1740
|
+
/// @dev (there must be only one contribution with an empty 'doc' field).
|
|
1741
|
+
if (participantContributions.length !== 1)
|
|
1742
|
+
logAndThrowError(SPECIFIC_ERRORS.SE_VERIFICATION_NO_PARTICIPANT_CONTRIBUTION_DATA);
|
|
1743
|
+
// Get contribution computation time.
|
|
1744
|
+
contributionComputationTime = contributions.at(0).computationTime;
|
|
1745
|
+
// Step (1.A.4.A.2).
|
|
1746
|
+
batch.create(contributionDoc.ref, {
|
|
1747
|
+
participantId: participantDoc.id,
|
|
1748
|
+
contributionComputationTime,
|
|
1749
|
+
verificationComputationTime: verifyCloudFunctionExecutionTime,
|
|
1750
|
+
zkeyIndex: isFinalizing ? actions.finalContributionIndex : lastZkeyIndex,
|
|
1751
|
+
files: {
|
|
1752
|
+
transcriptFilename: verificationTranscriptCompleteFilename,
|
|
1753
|
+
lastZkeyFilename,
|
|
1754
|
+
transcriptStoragePath: verificationTranscriptStoragePathAndFilename,
|
|
1755
|
+
lastZkeyStoragePath,
|
|
1756
|
+
transcriptBlake2bHash,
|
|
1757
|
+
lastZkeyBlake2bHash
|
|
1758
|
+
},
|
|
1759
|
+
verificationSoftware: {
|
|
1760
|
+
name: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_NAME),
|
|
1761
|
+
version: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_VERSION),
|
|
1762
|
+
commitHash: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_COMMIT_HASH)
|
|
1763
|
+
},
|
|
1764
|
+
valid: isContributionValid,
|
|
1765
|
+
lastUpdated: getCurrentServerTimestampInMillis()
|
|
1766
|
+
});
|
|
1767
|
+
verifyContributionTimer.stop();
|
|
1768
|
+
verifyCloudFunctionTime = verifyContributionTimer.ms();
|
|
1652
1769
|
}
|
|
1653
1770
|
else {
|
|
1654
|
-
//
|
|
1655
|
-
|
|
1656
|
-
await
|
|
1771
|
+
// Step (1.A.4.B).
|
|
1772
|
+
// Free-up storage by deleting invalid contribution.
|
|
1773
|
+
await deleteObject(bucketName, lastZkeyStoragePath);
|
|
1774
|
+
// Step (1.A.4.B.1).
|
|
1775
|
+
batch.create(contributionDoc.ref, {
|
|
1776
|
+
participantId: participantDoc.id,
|
|
1777
|
+
verificationComputationTime: verifyCloudFunctionExecutionTime,
|
|
1778
|
+
zkeyIndex: isFinalizing ? actions.finalContributionIndex : lastZkeyIndex,
|
|
1779
|
+
verificationSoftware: {
|
|
1780
|
+
name: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_NAME),
|
|
1781
|
+
version: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_VERSION),
|
|
1782
|
+
commitHash: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_COMMIT_HASH)
|
|
1783
|
+
},
|
|
1784
|
+
valid: isContributionValid,
|
|
1785
|
+
lastUpdated: getCurrentServerTimestampInMillis()
|
|
1786
|
+
});
|
|
1787
|
+
}
|
|
1788
|
+
// Stop VM instance
|
|
1789
|
+
if (isUsingVM) {
|
|
1790
|
+
// using try and catch as the VM stopping function can throw
|
|
1791
|
+
// however we want to continue without stopping as the
|
|
1792
|
+
// verification was valid, and inform the coordinator
|
|
1793
|
+
try {
|
|
1794
|
+
await actions.stopEC2Instance(ec2, vmInstanceId);
|
|
1795
|
+
}
|
|
1796
|
+
catch (error) {
|
|
1797
|
+
printLog(`Error while stopping VM instance ${vmInstanceId} - Error ${error}`, LogLevel.WARN);
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
// Step (1.A.4.C)
|
|
1801
|
+
if (!isFinalizing) {
|
|
1802
|
+
// Step (1.A.4.C.1)
|
|
1803
|
+
// Compute new average contribution/verification time.
|
|
1804
|
+
fullContributionTime = Number(verificationStartedAt) - Number(contributionStartedAt);
|
|
1805
|
+
const newAvgContributionComputationTime = avgContributionComputationTime > 0
|
|
1806
|
+
? (avgContributionComputationTime + contributionComputationTime) / 2
|
|
1807
|
+
: contributionComputationTime;
|
|
1808
|
+
const newAvgFullContributionTime = avgFullContributionTime > 0
|
|
1809
|
+
? (avgFullContributionTime + fullContributionTime) / 2
|
|
1810
|
+
: fullContributionTime;
|
|
1811
|
+
const newAvgVerifyCloudFunctionTime = avgVerifyCloudFunctionTime > 0
|
|
1812
|
+
? (avgVerifyCloudFunctionTime + verifyCloudFunctionTime) / 2
|
|
1813
|
+
: verifyCloudFunctionTime;
|
|
1814
|
+
// Prepare tx to update circuit average contribution/verification time.
|
|
1815
|
+
const updatedCircuitDoc = await getDocumentById(actions.getCircuitsCollectionPath(ceremonyId), circuitId);
|
|
1816
|
+
const { waitingQueue: updatedWaitingQueue } = updatedCircuitDoc.data();
|
|
1817
|
+
/// @dev this must happen only for valid contributions.
|
|
1818
|
+
batch.update(circuitDoc.ref, {
|
|
1819
|
+
avgTimings: {
|
|
1820
|
+
contributionComputation: isContributionValid
|
|
1821
|
+
? newAvgContributionComputationTime
|
|
1822
|
+
: avgContributionComputationTime,
|
|
1823
|
+
fullContribution: isContributionValid
|
|
1824
|
+
? newAvgFullContributionTime
|
|
1825
|
+
: avgFullContributionTime,
|
|
1826
|
+
verifyCloudFunction: isContributionValid
|
|
1827
|
+
? newAvgVerifyCloudFunctionTime
|
|
1828
|
+
: avgVerifyCloudFunctionTime
|
|
1829
|
+
},
|
|
1830
|
+
waitingQueue: {
|
|
1831
|
+
...updatedWaitingQueue,
|
|
1832
|
+
completedContributions: isContributionValid
|
|
1833
|
+
? completedContributions + 1
|
|
1834
|
+
: completedContributions,
|
|
1835
|
+
failedContributions: isContributionValid ? failedContributions : failedContributions + 1
|
|
1836
|
+
},
|
|
1837
|
+
lastUpdated: getCurrentServerTimestampInMillis()
|
|
1838
|
+
});
|
|
1839
|
+
}
|
|
1840
|
+
// Step (2).
|
|
1841
|
+
await batch.commit();
|
|
1842
|
+
printLog(`The contribution #${isFinalizing ? actions.finalContributionIndex : lastZkeyIndex} of circuit ${circuitId} (ceremony ${ceremonyId}) has been verified as ${isContributionValid ? "valid" : "invalid"} for the participant ${participantDoc.id}`, LogLevel.INFO);
|
|
1843
|
+
};
|
|
1844
|
+
// Step (1).
|
|
1845
|
+
if (isContributing || isFinalizing) {
|
|
1846
|
+
// Prepare timer.
|
|
1847
|
+
verificationTaskTimer.start();
|
|
1848
|
+
// Step (1.A.3.0).
|
|
1849
|
+
if (isUsingVM) {
|
|
1850
|
+
printLog(`Starting the VM mechanism`, LogLevel.DEBUG);
|
|
1851
|
+
// Prepare for VM execution.
|
|
1852
|
+
let isVMRunning = false; // true when the VM is up, otherwise false.
|
|
1853
|
+
// Step (1.A.3.1).
|
|
1854
|
+
await actions.startEC2Instance(ec2, vmInstanceId);
|
|
1855
|
+
await sleep(60000); // nb. wait for VM startup (1 mins + retry).
|
|
1856
|
+
// Check if the startup is running.
|
|
1857
|
+
isVMRunning = await checkIfVMRunning(ec2, vmInstanceId);
|
|
1858
|
+
printLog(`VM running: ${isVMRunning}`, LogLevel.DEBUG);
|
|
1859
|
+
// Step (1.A.3.2).
|
|
1860
|
+
// Prepare.
|
|
1861
|
+
const verificationCommand = actions.vmContributionVerificationCommand(bucketName, lastZkeyStoragePath, verificationTranscriptStoragePathAndFilename);
|
|
1862
|
+
// Run.
|
|
1863
|
+
commandId = await actions.runCommandUsingSSM(ssm, vmInstanceId, verificationCommand);
|
|
1864
|
+
printLog(`Starting the execution of command ${commandId}`, LogLevel.DEBUG);
|
|
1865
|
+
// Step (1.A.3.3).
|
|
1866
|
+
return await waitForVMCommandExecution(ssm, vmInstanceId, commandId)
|
|
1867
|
+
.then(async () => {
|
|
1868
|
+
// Command execution successfully completed.
|
|
1869
|
+
printLog(`Command ${commandId} execution has been successfully completed`, LogLevel.DEBUG);
|
|
1870
|
+
await completeVerification();
|
|
1871
|
+
})
|
|
1872
|
+
.catch((error) => {
|
|
1873
|
+
// Command execution aborted.
|
|
1874
|
+
printLog(`Command ${commandId} execution has been aborted - Error ${error}`, LogLevel.WARN);
|
|
1875
|
+
logAndThrowError(COMMON_ERRORS.CM_INVALID_COMMAND_EXECUTION);
|
|
1876
|
+
});
|
|
1657
1877
|
}
|
|
1658
|
-
// Compute verification transcript hash.
|
|
1659
|
-
transcriptBlake2bHash = await actions.blake512FromPath(verificationTranscriptTemporaryLocalPath);
|
|
1660
|
-
// Free resources by unlinking transcript temporary file.
|
|
1661
|
-
fs.unlinkSync(verificationTranscriptTemporaryLocalPath);
|
|
1662
|
-
// Filter participant contributions to find the data related to the one verified.
|
|
1663
|
-
const participantContributions = contributions.filter((contribution) => !!contribution.hash && !!contribution.computationTime && !contribution.doc);
|
|
1664
|
-
/// @dev (there must be only one contribution with an empty 'doc' field).
|
|
1665
|
-
if (participantContributions.length !== 1)
|
|
1666
|
-
logAndThrowError(SPECIFIC_ERRORS.SE_VERIFICATION_NO_PARTICIPANT_CONTRIBUTION_DATA);
|
|
1667
|
-
// Get contribution computation time.
|
|
1668
|
-
contributionComputationTime = contributions.at(0).computationTime;
|
|
1669
|
-
// Step (1.A.4.A.2).
|
|
1670
|
-
batch.create(contributionDoc.ref, {
|
|
1671
|
-
participantId: participantDoc.id,
|
|
1672
|
-
contributionComputationTime,
|
|
1673
|
-
verificationComputationTime: verifyCloudFunctionExecutionTime,
|
|
1674
|
-
zkeyIndex: isFinalizing ? actions.finalContributionIndex : lastZkeyIndex,
|
|
1675
|
-
files: {
|
|
1676
|
-
transcriptFilename: verificationTranscriptCompleteFilename,
|
|
1677
|
-
lastZkeyFilename,
|
|
1678
|
-
transcriptStoragePath: verificationTranscriptStoragePathAndFilename,
|
|
1679
|
-
lastZkeyStoragePath,
|
|
1680
|
-
transcriptBlake2bHash,
|
|
1681
|
-
lastZkeyBlake2bHash
|
|
1682
|
-
},
|
|
1683
|
-
verificationSoftware: {
|
|
1684
|
-
name: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_NAME),
|
|
1685
|
-
version: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_VERSION),
|
|
1686
|
-
commitHash: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_COMMIT_HASH)
|
|
1687
|
-
},
|
|
1688
|
-
valid: isContributionValid,
|
|
1689
|
-
lastUpdated: getCurrentServerTimestampInMillis()
|
|
1690
|
-
});
|
|
1691
|
-
verifyContributionTimer.stop();
|
|
1692
|
-
verifyCloudFunctionTime = verifyContributionTimer.ms();
|
|
1693
|
-
}
|
|
1694
|
-
else {
|
|
1695
|
-
// Step (1.A.4.B).
|
|
1696
|
-
// Free-up storage by deleting invalid contribution.
|
|
1697
|
-
await deleteObject(bucketName, lastZkeyStoragePath);
|
|
1698
|
-
// Step (1.A.4.B.1).
|
|
1699
|
-
batch.create(contributionDoc.ref, {
|
|
1700
|
-
participantId: participantDoc.id,
|
|
1701
|
-
verificationComputationTime: verifyCloudFunctionExecutionTime,
|
|
1702
|
-
zkeyIndex: isFinalizing ? actions.finalContributionIndex : lastZkeyIndex,
|
|
1703
|
-
verificationSoftware: {
|
|
1704
|
-
name: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_NAME),
|
|
1705
|
-
version: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_VERSION),
|
|
1706
|
-
commitHash: String(process.env.CUSTOM_CONTRIBUTION_VERIFICATION_SOFTWARE_COMMIT_HASH)
|
|
1707
|
-
},
|
|
1708
|
-
valid: isContributionValid,
|
|
1709
|
-
lastUpdated: getCurrentServerTimestampInMillis()
|
|
1710
|
-
});
|
|
1711
|
-
}
|
|
1712
|
-
// Step (1.A.4.C)
|
|
1713
|
-
if (!isFinalizing) {
|
|
1714
|
-
// Step (1.A.4.C.1)
|
|
1715
|
-
// Compute new average contribution/verification time.
|
|
1716
|
-
fullContributionTime = Number(verificationStartedAt) - Number(contributionStartedAt);
|
|
1717
|
-
const newAvgContributionComputationTime = avgContributionComputationTime > 0
|
|
1718
|
-
? (avgContributionComputationTime + contributionComputationTime) / 2
|
|
1719
|
-
: contributionComputationTime;
|
|
1720
|
-
const newAvgFullContributionTime = avgFullContributionTime > 0
|
|
1721
|
-
? (avgFullContributionTime + fullContributionTime) / 2
|
|
1722
|
-
: fullContributionTime;
|
|
1723
|
-
const newAvgVerifyCloudFunctionTime = avgVerifyCloudFunctionTime > 0
|
|
1724
|
-
? (avgVerifyCloudFunctionTime + verifyCloudFunctionTime) / 2
|
|
1725
|
-
: verifyCloudFunctionTime;
|
|
1726
|
-
// Prepare tx to update circuit average contribution/verification time.
|
|
1727
|
-
const updatedCircuitDoc = await getDocumentById(actions.getCircuitsCollectionPath(ceremonyId), circuitId);
|
|
1728
|
-
const { waitingQueue: updatedWaitingQueue } = updatedCircuitDoc.data();
|
|
1729
|
-
/// @dev this must happen only for valid contributions.
|
|
1730
|
-
batch.update(circuitDoc.ref, {
|
|
1731
|
-
avgTimings: {
|
|
1732
|
-
contributionComputation: isContributionValid
|
|
1733
|
-
? newAvgContributionComputationTime
|
|
1734
|
-
: avgContributionComputationTime,
|
|
1735
|
-
fullContribution: isContributionValid ? newAvgFullContributionTime : avgFullContributionTime,
|
|
1736
|
-
verifyCloudFunction: isContributionValid
|
|
1737
|
-
? newAvgVerifyCloudFunctionTime
|
|
1738
|
-
: avgVerifyCloudFunctionTime
|
|
1739
|
-
},
|
|
1740
|
-
waitingQueue: {
|
|
1741
|
-
...updatedWaitingQueue,
|
|
1742
|
-
completedContributions: isContributionValid
|
|
1743
|
-
? completedContributions + 1
|
|
1744
|
-
: completedContributions,
|
|
1745
|
-
failedContributions: isContributionValid ? failedContributions : failedContributions + 1
|
|
1746
|
-
},
|
|
1747
|
-
lastUpdated: getCurrentServerTimestampInMillis()
|
|
1748
|
-
});
|
|
1749
|
-
}
|
|
1750
|
-
// Step (2).
|
|
1751
|
-
await batch.commit();
|
|
1752
|
-
printLog(`The contribution #${isFinalizing ? actions.finalContributionIndex : lastZkeyIndex} of circuit ${circuitId} (ceremony ${ceremonyId}) has been verified as ${isContributionValid ? "valid" : "invalid"} for the participant ${participantDoc.id}`, LogLevel.DEBUG);
|
|
1753
|
-
};
|
|
1754
|
-
// Step (1).
|
|
1755
|
-
if (isContributing || isFinalizing) {
|
|
1756
|
-
// Prepare timer.
|
|
1757
|
-
verificationTaskTimer.start();
|
|
1758
|
-
// Step (1.A.3.0).
|
|
1759
|
-
if (isUsingVM) {
|
|
1760
|
-
printLog(`Starting the VM mechanism`, LogLevel.DEBUG);
|
|
1761
|
-
// Prepare for VM execution.
|
|
1762
|
-
let isVMRunning = false; // true when the VM is up, otherwise false.
|
|
1763
|
-
// Step (1.A.3.1).
|
|
1764
|
-
await actions.startEC2Instance(ec2, vmInstanceId);
|
|
1765
|
-
await sleep(60000); // nb. wait for VM startup (1 mins + retry).
|
|
1766
|
-
// Check if the startup is running.
|
|
1767
|
-
isVMRunning = await checkIfVMRunning(ec2, vmInstanceId);
|
|
1768
|
-
printLog(`VM running: ${isVMRunning}`, LogLevel.DEBUG);
|
|
1769
|
-
// Step (1.A.3.2).
|
|
1770
|
-
// Prepare.
|
|
1771
|
-
const verificationCommand = actions.vmContributionVerificationCommand(bucketName, lastZkeyStoragePath, verificationTranscriptStoragePathAndFilename);
|
|
1772
|
-
// Run.
|
|
1773
|
-
commandId = await actions.runCommandUsingSSM(ssm, vmInstanceId, verificationCommand);
|
|
1774
|
-
printLog(`Starting the execution of command ${commandId}`, LogLevel.DEBUG);
|
|
1775
|
-
// Step (1.A.3.3).
|
|
1776
|
-
return new Promise((resolve, reject) => waitForVMCommandExecution(resolve, reject, ssm, vmInstanceId, commandId))
|
|
1777
|
-
.then(async () => {
|
|
1778
|
-
// Command execution successfully completed.
|
|
1779
|
-
printLog(`Command ${commandId} execution has been successfully completed`, LogLevel.DEBUG);
|
|
1780
|
-
await completeVerification();
|
|
1781
|
-
})
|
|
1782
|
-
.catch((error) => {
|
|
1783
|
-
// Command execution aborted.
|
|
1784
|
-
printLog(`Command ${commandId} execution has been aborted - Error ${error}`, LogLevel.DEBUG);
|
|
1785
|
-
logAndThrowError(COMMON_ERRORS.CM_INVALID_COMMAND_EXECUTION);
|
|
1786
|
-
});
|
|
1787
|
-
}
|
|
1788
|
-
else {
|
|
1789
1878
|
// CF approach.
|
|
1790
1879
|
printLog(`CF mechanism`, LogLevel.DEBUG);
|
|
1791
1880
|
const potStoragePath = actions.getPotStorageFilePath(files.potFilename);
|
|
1792
1881
|
const firstZkeyStoragePath = actions.getZkeyStorageFilePath(prefix, `${prefix}_${actions.genesisZkeyIndex}.zkey`);
|
|
1882
|
+
printLog(`pot file: ${potStoragePath}`, LogLevel.DEBUG);
|
|
1883
|
+
printLog(`zkey file: ${firstZkeyStoragePath}`, LogLevel.DEBUG);
|
|
1793
1884
|
// Prepare temporary file paths.
|
|
1794
1885
|
// (nb. these are needed to download the necessary artifacts for verification from AWS S3).
|
|
1795
1886
|
verificationTranscriptTemporaryLocalPath = createTemporaryLocalPath(verificationTranscriptCompleteFilename);
|
|
1796
1887
|
const potTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}.pot`);
|
|
1797
1888
|
const firstZkeyTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}_genesis.zkey`);
|
|
1798
1889
|
const lastZkeyTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}_last.zkey`);
|
|
1890
|
+
printLog(`pot file: ${potTempFilePath}`, LogLevel.DEBUG);
|
|
1891
|
+
printLog(`firstZkey file: ${firstZkeyTempFilePath}`, LogLevel.DEBUG);
|
|
1892
|
+
printLog(`last zkey file: ${lastZkeyTempFilePath}`, LogLevel.DEBUG);
|
|
1799
1893
|
// Create and populate transcript.
|
|
1800
1894
|
const transcriptLogger = actions.createCustomLoggerForFile(verificationTranscriptTemporaryLocalPath);
|
|
1801
1895
|
transcriptLogger.info(`${isFinalizing ? `Final verification` : `Verification`} transcript for ${prefix} circuit Phase 2 contribution.\n${isFinalizing ? `Coordinator ` : `Contributor # ${Number(lastZkeyIndex)}`} (${contributorOrCoordinatorIdentifier})\n`);
|
|
@@ -1805,6 +1899,7 @@ const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB"
|
|
|
1805
1899
|
await downloadArtifactFromS3Bucket(bucketName, lastZkeyStoragePath, lastZkeyTempFilePath);
|
|
1806
1900
|
// Step (1.A.4).
|
|
1807
1901
|
isContributionValid = await snarkjs.zKey.verifyFromInit(firstZkeyTempFilePath, potTempFilePath, lastZkeyTempFilePath, transcriptLogger);
|
|
1902
|
+
await dumpLog(verificationTranscriptTemporaryLocalPath);
|
|
1808
1903
|
// Compute contribution hash.
|
|
1809
1904
|
lastZkeyBlake2bHash = await actions.blake512FromPath(lastZkeyTempFilePath);
|
|
1810
1905
|
// Free resources by unlinking temporary folders.
|
|
@@ -1819,6 +1914,11 @@ const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB"
|
|
|
1819
1914
|
}
|
|
1820
1915
|
await completeVerification();
|
|
1821
1916
|
}
|
|
1917
|
+
return null;
|
|
1918
|
+
}
|
|
1919
|
+
catch (error) {
|
|
1920
|
+
logAndThrowError(makeError("unknown", error));
|
|
1921
|
+
return null;
|
|
1822
1922
|
}
|
|
1823
1923
|
});
|
|
1824
1924
|
/**
|
|
@@ -1827,9 +1927,9 @@ const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB"
|
|
|
1827
1927
|
* this does not happen if the participant is actually the coordinator who is finalizing the ceremony.
|
|
1828
1928
|
*/
|
|
1829
1929
|
const refreshParticipantAfterContributionVerification = functionsV1__namespace
|
|
1830
|
-
.region(
|
|
1930
|
+
.region("europe-west1")
|
|
1831
1931
|
.runWith({
|
|
1832
|
-
memory: "
|
|
1932
|
+
memory: "1GB"
|
|
1833
1933
|
})
|
|
1834
1934
|
.firestore.document(`/${actions.commonTerms.collections.ceremonies.name}/{ceremony}/${actions.commonTerms.collections.circuits.name}/{circuit}/${actions.commonTerms.collections.contributions.name}/{contributions}`)
|
|
1835
1935
|
.onCreate(async (createdContribution) => {
|
|
@@ -1880,7 +1980,7 @@ const refreshParticipantAfterContributionVerification = functionsV1__namespace
|
|
|
1880
1980
|
lastUpdated: getCurrentServerTimestampInMillis()
|
|
1881
1981
|
});
|
|
1882
1982
|
await batch.commit();
|
|
1883
|
-
printLog(`Participant ${participantId} refreshed after contribution ${createdContribution.id} - The participant was finalizing the ceremony ${isFinalizing}`, LogLevel.
|
|
1983
|
+
printLog(`Participant ${participantId} refreshed after contribution ${createdContribution.id} - The participant was finalizing the ceremony? ${isFinalizing}`, LogLevel.INFO);
|
|
1884
1984
|
});
|
|
1885
1985
|
/**
|
|
1886
1986
|
* Finalize the ceremony circuit.
|
|
@@ -1888,9 +1988,9 @@ const refreshParticipantAfterContributionVerification = functionsV1__namespace
|
|
|
1888
1988
|
* and verification key extracted from the circuit final contribution (as part of the ceremony finalization process).
|
|
1889
1989
|
*/
|
|
1890
1990
|
const finalizeCircuit = functionsV1__namespace
|
|
1891
|
-
.region(
|
|
1991
|
+
.region("europe-west1")
|
|
1892
1992
|
.runWith({
|
|
1893
|
-
memory: "
|
|
1993
|
+
memory: "1GB"
|
|
1894
1994
|
})
|
|
1895
1995
|
.https.onCall(async (data, context) => {
|
|
1896
1996
|
if (!context.auth || !context.auth.token.coordinator)
|
|
@@ -2034,7 +2134,7 @@ const checkIfBucketIsDedicatedToCeremony = async (bucketName) => {
|
|
|
2034
2134
|
const createBucket = functions__namespace
|
|
2035
2135
|
.region("europe-west1")
|
|
2036
2136
|
.runWith({
|
|
2037
|
-
memory: "
|
|
2137
|
+
memory: "1GB"
|
|
2038
2138
|
})
|
|
2039
2139
|
.https.onCall(async (data, context) => {
|
|
2040
2140
|
// Check if the user has the coordinator claim.
|
|
@@ -2045,6 +2145,7 @@ const createBucket = functions__namespace
|
|
|
2045
2145
|
// Connect to S3 client.
|
|
2046
2146
|
const S3 = await getS3Client();
|
|
2047
2147
|
try {
|
|
2148
|
+
printLog(`Creating AWS S3 bucket ${data.bucketName} ...`, LogLevel.LOG);
|
|
2048
2149
|
// Try to get information about the bucket.
|
|
2049
2150
|
await S3.send(new clientS3.HeadBucketCommand({ Bucket: data.bucketName }));
|
|
2050
2151
|
// If the command succeeded, the bucket exists, throw an error.
|
|
@@ -2085,8 +2186,10 @@ const createBucket = functions__namespace
|
|
|
2085
2186
|
CORSConfiguration: {
|
|
2086
2187
|
CORSRules: [
|
|
2087
2188
|
{
|
|
2088
|
-
AllowedMethods: ["GET"],
|
|
2089
|
-
AllowedOrigins: ["*"]
|
|
2189
|
+
AllowedMethods: ["GET", "PUT"],
|
|
2190
|
+
AllowedOrigins: ["*"],
|
|
2191
|
+
ExposeHeaders: ["ETag", "Content-Length"],
|
|
2192
|
+
AllowedHeaders: ["*"]
|
|
2090
2193
|
}
|
|
2091
2194
|
]
|
|
2092
2195
|
}
|
|
@@ -2122,7 +2225,7 @@ const createBucket = functions__namespace
|
|
|
2122
2225
|
const checkIfObjectExist = functions__namespace
|
|
2123
2226
|
.region("europe-west1")
|
|
2124
2227
|
.runWith({
|
|
2125
|
-
memory: "
|
|
2228
|
+
memory: "1GB"
|
|
2126
2229
|
})
|
|
2127
2230
|
.https.onCall(async (data, context) => {
|
|
2128
2231
|
// Check if the user has the coordinator claim.
|
|
@@ -2168,7 +2271,7 @@ const checkIfObjectExist = functions__namespace
|
|
|
2168
2271
|
const generateGetObjectPreSignedUrl = functions__namespace
|
|
2169
2272
|
.region("europe-west1")
|
|
2170
2273
|
.runWith({
|
|
2171
|
-
memory: "
|
|
2274
|
+
memory: "1GB"
|
|
2172
2275
|
})
|
|
2173
2276
|
.https.onCall(async (data, context) => {
|
|
2174
2277
|
if (!context.auth)
|
|
@@ -2208,7 +2311,7 @@ const generateGetObjectPreSignedUrl = functions__namespace
|
|
|
2208
2311
|
const startMultiPartUpload = functions__namespace
|
|
2209
2312
|
.region("europe-west1")
|
|
2210
2313
|
.runWith({
|
|
2211
|
-
memory: "
|
|
2314
|
+
memory: "2GB"
|
|
2212
2315
|
})
|
|
2213
2316
|
.https.onCall(async (data, context) => {
|
|
2214
2317
|
if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
|
|
@@ -2263,7 +2366,8 @@ const startMultiPartUpload = functions__namespace
|
|
|
2263
2366
|
const generatePreSignedUrlsParts = functions__namespace
|
|
2264
2367
|
.region("europe-west1")
|
|
2265
2368
|
.runWith({
|
|
2266
|
-
memory: "
|
|
2369
|
+
memory: "1GB",
|
|
2370
|
+
timeoutSeconds: 300
|
|
2267
2371
|
})
|
|
2268
2372
|
.https.onCall(async (data, context) => {
|
|
2269
2373
|
if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
|
|
@@ -2323,7 +2427,7 @@ const generatePreSignedUrlsParts = functions__namespace
|
|
|
2323
2427
|
const completeMultiPartUpload = functions__namespace
|
|
2324
2428
|
.region("europe-west1")
|
|
2325
2429
|
.runWith({
|
|
2326
|
-
memory: "
|
|
2430
|
+
memory: "2GB"
|
|
2327
2431
|
})
|
|
2328
2432
|
.https.onCall(async (data, context) => {
|
|
2329
2433
|
if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
|
|
@@ -2372,6 +2476,216 @@ const completeMultiPartUpload = functions__namespace
|
|
|
2372
2476
|
}
|
|
2373
2477
|
});
|
|
2374
2478
|
|
|
2479
|
+
dotenv.config();
|
|
2480
|
+
const { BANDADA_API_URL, BANDADA_GROUP_ID } = process.env;
|
|
2481
|
+
const bandadaApi = new apiSdk.ApiSdk(BANDADA_API_URL);
|
|
2482
|
+
const bandadaValidateProof = functions__namespace
|
|
2483
|
+
.region("europe-west1")
|
|
2484
|
+
.runWith({
|
|
2485
|
+
memory: "1GB"
|
|
2486
|
+
})
|
|
2487
|
+
.https.onCall(async (data) => {
|
|
2488
|
+
const VKEY_DATA = {
|
|
2489
|
+
protocol: "groth16",
|
|
2490
|
+
curve: "bn128",
|
|
2491
|
+
nPublic: 3,
|
|
2492
|
+
vk_alpha_1: [
|
|
2493
|
+
"20491192805390485299153009773594534940189261866228447918068658471970481763042",
|
|
2494
|
+
"9383485363053290200918347156157836566562967994039712273449902621266178545958",
|
|
2495
|
+
"1"
|
|
2496
|
+
],
|
|
2497
|
+
vk_beta_2: [
|
|
2498
|
+
[
|
|
2499
|
+
"6375614351688725206403948262868962793625744043794305715222011528459656738731",
|
|
2500
|
+
"4252822878758300859123897981450591353533073413197771768651442665752259397132"
|
|
2501
|
+
],
|
|
2502
|
+
[
|
|
2503
|
+
"10505242626370262277552901082094356697409835680220590971873171140371331206856",
|
|
2504
|
+
"21847035105528745403288232691147584728191162732299865338377159692350059136679"
|
|
2505
|
+
],
|
|
2506
|
+
["1", "0"]
|
|
2507
|
+
],
|
|
2508
|
+
vk_gamma_2: [
|
|
2509
|
+
[
|
|
2510
|
+
"10857046999023057135944570762232829481370756359578518086990519993285655852781",
|
|
2511
|
+
"11559732032986387107991004021392285783925812861821192530917403151452391805634"
|
|
2512
|
+
],
|
|
2513
|
+
[
|
|
2514
|
+
"8495653923123431417604973247489272438418190587263600148770280649306958101930",
|
|
2515
|
+
"4082367875863433681332203403145435568316851327593401208105741076214120093531"
|
|
2516
|
+
],
|
|
2517
|
+
["1", "0"]
|
|
2518
|
+
],
|
|
2519
|
+
vk_delta_2: [
|
|
2520
|
+
[
|
|
2521
|
+
"3697618915467790705869942236922063775466274665053173890632463796679068973252",
|
|
2522
|
+
"14948341351907992175709156460547989243732741534604949238422596319735704165658"
|
|
2523
|
+
],
|
|
2524
|
+
[
|
|
2525
|
+
"3028459181652799888716942141752307629938889957960373621898607910203491239368",
|
|
2526
|
+
"11380736494786911280692284374675752681598754560757720296073023058533044108340"
|
|
2527
|
+
],
|
|
2528
|
+
["1", "0"]
|
|
2529
|
+
],
|
|
2530
|
+
vk_alphabeta_12: [
|
|
2531
|
+
[
|
|
2532
|
+
[
|
|
2533
|
+
"2029413683389138792403550203267699914886160938906632433982220835551125967885",
|
|
2534
|
+
"21072700047562757817161031222997517981543347628379360635925549008442030252106"
|
|
2535
|
+
],
|
|
2536
|
+
[
|
|
2537
|
+
"5940354580057074848093997050200682056184807770593307860589430076672439820312",
|
|
2538
|
+
"12156638873931618554171829126792193045421052652279363021382169897324752428276"
|
|
2539
|
+
],
|
|
2540
|
+
[
|
|
2541
|
+
"7898200236362823042373859371574133993780991612861777490112507062703164551277",
|
|
2542
|
+
"7074218545237549455313236346927434013100842096812539264420499035217050630853"
|
|
2543
|
+
]
|
|
2544
|
+
],
|
|
2545
|
+
[
|
|
2546
|
+
[
|
|
2547
|
+
"7077479683546002997211712695946002074877511277312570035766170199895071832130",
|
|
2548
|
+
"10093483419865920389913245021038182291233451549023025229112148274109565435465"
|
|
2549
|
+
],
|
|
2550
|
+
[
|
|
2551
|
+
"4595479056700221319381530156280926371456704509942304414423590385166031118820",
|
|
2552
|
+
"19831328484489333784475432780421641293929726139240675179672856274388269393268"
|
|
2553
|
+
],
|
|
2554
|
+
[
|
|
2555
|
+
"11934129596455521040620786944827826205713621633706285934057045369193958244500",
|
|
2556
|
+
"8037395052364110730298837004334506829870972346962140206007064471173334027475"
|
|
2557
|
+
]
|
|
2558
|
+
]
|
|
2559
|
+
],
|
|
2560
|
+
IC: [
|
|
2561
|
+
[
|
|
2562
|
+
"12951059800758687233303204819298121944551181861362200875212570257618182506154",
|
|
2563
|
+
"5751958719396509176593242305268064754837298673622815112953832050159760501392",
|
|
2564
|
+
"1"
|
|
2565
|
+
],
|
|
2566
|
+
[
|
|
2567
|
+
"9561588427935871983444704959674198910445823619407211599507208879011862515257",
|
|
2568
|
+
"14576201570478094842467636169770180675293504492823217349086195663150934064643",
|
|
2569
|
+
"1"
|
|
2570
|
+
],
|
|
2571
|
+
[
|
|
2572
|
+
"4811967233483727873912563574622036989372099129165459921963463310078093941559",
|
|
2573
|
+
"1874883809855039536107616044787862082553628089593740724610117059083415551067",
|
|
2574
|
+
"1"
|
|
2575
|
+
],
|
|
2576
|
+
[
|
|
2577
|
+
"12252730267779308452229639835051322390696643456253768618882001876621526827161",
|
|
2578
|
+
"7899194018737016222260328309937800777948677569409898603827268776967707173231",
|
|
2579
|
+
"1"
|
|
2580
|
+
]
|
|
2581
|
+
]
|
|
2582
|
+
};
|
|
2583
|
+
if (!BANDADA_GROUP_ID)
|
|
2584
|
+
throw new Error("BANDADA_GROUP_ID is not defined in .env");
|
|
2585
|
+
const { proof, publicSignals } = data;
|
|
2586
|
+
const isCorrect = snarkjs.groth16.verify(VKEY_DATA, publicSignals, proof);
|
|
2587
|
+
if (!isCorrect)
|
|
2588
|
+
return {
|
|
2589
|
+
valid: false,
|
|
2590
|
+
message: "Invalid proof",
|
|
2591
|
+
token: ""
|
|
2592
|
+
};
|
|
2593
|
+
const commitment = data.publicSignals[1];
|
|
2594
|
+
const isMember = await bandadaApi.isGroupMember(BANDADA_GROUP_ID, commitment);
|
|
2595
|
+
if (!isMember)
|
|
2596
|
+
return {
|
|
2597
|
+
valid: false,
|
|
2598
|
+
message: "Not a member of the group",
|
|
2599
|
+
token: ""
|
|
2600
|
+
};
|
|
2601
|
+
const auth$1 = auth.getAuth();
|
|
2602
|
+
try {
|
|
2603
|
+
await admin.auth().createUser({
|
|
2604
|
+
uid: commitment
|
|
2605
|
+
});
|
|
2606
|
+
}
|
|
2607
|
+
catch (error) {
|
|
2608
|
+
// if user already exist then just pass
|
|
2609
|
+
if (error.code !== "auth/uid-already-exists") {
|
|
2610
|
+
throw new Error(error);
|
|
2611
|
+
}
|
|
2612
|
+
}
|
|
2613
|
+
const token = await auth$1.createCustomToken(commitment);
|
|
2614
|
+
return {
|
|
2615
|
+
valid: true,
|
|
2616
|
+
message: "Valid proof and group member",
|
|
2617
|
+
token
|
|
2618
|
+
};
|
|
2619
|
+
});
|
|
2620
|
+
|
|
2621
|
+
dotenv.config();
|
|
2622
|
+
const checkNonceOfSIWEAddress = functions__namespace
|
|
2623
|
+
.region("europe-west1")
|
|
2624
|
+
.runWith({ memory: "1GB" })
|
|
2625
|
+
.https.onCall(async (data) => {
|
|
2626
|
+
try {
|
|
2627
|
+
const { auth0Token } = data;
|
|
2628
|
+
const result = (await fetch(`${process.env.AUTH0_APPLICATION_URL}/userinfo`, {
|
|
2629
|
+
method: "GET",
|
|
2630
|
+
headers: {
|
|
2631
|
+
"content-type": "application/json",
|
|
2632
|
+
authorization: `Bearer ${auth0Token}`
|
|
2633
|
+
}
|
|
2634
|
+
}).then((_res) => _res.json()));
|
|
2635
|
+
if (!result.sub) {
|
|
2636
|
+
return {
|
|
2637
|
+
valid: false,
|
|
2638
|
+
message: "No user detected. Please check device flow token"
|
|
2639
|
+
};
|
|
2640
|
+
}
|
|
2641
|
+
const auth$1 = auth.getAuth();
|
|
2642
|
+
// check nonce
|
|
2643
|
+
const parts = result.sub.split("|");
|
|
2644
|
+
const address = decodeURIComponent(parts[2]).split(":")[2];
|
|
2645
|
+
const minimumNonce = Number(process.env.ETH_MINIMUM_NONCE);
|
|
2646
|
+
const nonceBlockHeight = "latest"; // process.env.ETH_NONCE_BLOCK_HEIGHT
|
|
2647
|
+
// look up nonce for address @block
|
|
2648
|
+
let nonceOk = true;
|
|
2649
|
+
if (minimumNonce > 0) {
|
|
2650
|
+
const provider = setEthProvider();
|
|
2651
|
+
console.log(`got provider - block # ${await provider.getBlockNumber()}`);
|
|
2652
|
+
const nonce = await provider.getTransactionCount(address, nonceBlockHeight);
|
|
2653
|
+
console.log(`nonce ${nonce}`);
|
|
2654
|
+
nonceOk = nonce >= minimumNonce;
|
|
2655
|
+
}
|
|
2656
|
+
console.log(`checking nonce ${nonceOk}`);
|
|
2657
|
+
if (!nonceOk) {
|
|
2658
|
+
return {
|
|
2659
|
+
valid: false,
|
|
2660
|
+
message: "Eth address does not meet the nonce requirements"
|
|
2661
|
+
};
|
|
2662
|
+
}
|
|
2663
|
+
try {
|
|
2664
|
+
await admin.auth().createUser({
|
|
2665
|
+
displayName: address,
|
|
2666
|
+
uid: address
|
|
2667
|
+
});
|
|
2668
|
+
}
|
|
2669
|
+
catch (error) {
|
|
2670
|
+
// if user already exist then just pass
|
|
2671
|
+
if (error.code !== "auth/uid-already-exists") {
|
|
2672
|
+
throw new Error(error);
|
|
2673
|
+
}
|
|
2674
|
+
}
|
|
2675
|
+
const token = await auth$1.createCustomToken(address);
|
|
2676
|
+
return {
|
|
2677
|
+
valid: true,
|
|
2678
|
+
token
|
|
2679
|
+
};
|
|
2680
|
+
}
|
|
2681
|
+
catch (error) {
|
|
2682
|
+
return {
|
|
2683
|
+
valid: false,
|
|
2684
|
+
message: `Something went wrong ${error}`
|
|
2685
|
+
};
|
|
2686
|
+
}
|
|
2687
|
+
});
|
|
2688
|
+
|
|
2375
2689
|
dotenv.config();
|
|
2376
2690
|
/**
|
|
2377
2691
|
* Check and remove the current contributor if it doesn't complete the contribution on the specified amount of time.
|
|
@@ -2394,7 +2708,7 @@ dotenv.config();
|
|
|
2394
2708
|
const checkAndRemoveBlockingContributor = functions__namespace
|
|
2395
2709
|
.region("europe-west1")
|
|
2396
2710
|
.runWith({
|
|
2397
|
-
memory: "
|
|
2711
|
+
memory: "1GB"
|
|
2398
2712
|
})
|
|
2399
2713
|
.pubsub.schedule("every 1 minutes")
|
|
2400
2714
|
.onRun(async () => {
|
|
@@ -2413,7 +2727,7 @@ const checkAndRemoveBlockingContributor = functions__namespace
|
|
|
2413
2727
|
// Get ceremony circuits.
|
|
2414
2728
|
const circuits = await getCeremonyCircuits(ceremony.id);
|
|
2415
2729
|
// Extract ceremony data.
|
|
2416
|
-
const { timeoutMechanismType, penalty } = ceremony.data();
|
|
2730
|
+
const { timeoutType: timeoutMechanismType, penalty } = ceremony.data();
|
|
2417
2731
|
for (const circuit of circuits) {
|
|
2418
2732
|
if (!circuit.data())
|
|
2419
2733
|
// Do not use `logAndThrowError` method to avoid the function to exit before checking every ceremony.
|
|
@@ -2426,7 +2740,7 @@ const checkAndRemoveBlockingContributor = functions__namespace
|
|
|
2426
2740
|
// Case (A).
|
|
2427
2741
|
if (!currentContributor)
|
|
2428
2742
|
// Do not use `logAndThrowError` method to avoid the function to exit before checking every ceremony.
|
|
2429
|
-
printLog(`No current contributor for circuit ${circuit.id} - ceremony ${ceremony.id}`, LogLevel.
|
|
2743
|
+
printLog(`No current contributor for circuit ${circuit.id} - ceremony ${ceremony.id}`, LogLevel.DEBUG);
|
|
2430
2744
|
else if (avgFullContribution === 0 &&
|
|
2431
2745
|
avgContributionComputation === 0 &&
|
|
2432
2746
|
avgVerifyCloudFunction === 0 &&
|
|
@@ -2452,7 +2766,7 @@ const checkAndRemoveBlockingContributor = functions__namespace
|
|
|
2452
2766
|
? Number(contributionStartedAt) +
|
|
2453
2767
|
Number(avgFullContribution) +
|
|
2454
2768
|
Number(timeoutDynamicThreshold)
|
|
2455
|
-
: Number(contributionStartedAt) + Number(fixedTimeWindow) * 60000; // * 60000 = convert minutes to millis.
|
|
2769
|
+
: (Number(contributionStartedAt) + Number(fixedTimeWindow)) * 60000; // * 60000 = convert minutes to millis.
|
|
2456
2770
|
// Case (D).
|
|
2457
2771
|
const timeoutExpirationDateInMsForVerificationCloudFunction = contributionStep === "VERIFYING" /* ParticipantContributionStep.VERIFYING */ &&
|
|
2458
2772
|
!!verificationStartedAt
|
|
@@ -2463,17 +2777,18 @@ const checkAndRemoveBlockingContributor = functions__namespace
|
|
|
2463
2777
|
if (timeoutExpirationDateInMsForBlockingContributor < currentServerTimestamp &&
|
|
2464
2778
|
(contributionStep === "DOWNLOADING" /* ParticipantContributionStep.DOWNLOADING */ ||
|
|
2465
2779
|
contributionStep === "COMPUTING" /* ParticipantContributionStep.COMPUTING */ ||
|
|
2466
|
-
contributionStep === "UPLOADING" /* ParticipantContributionStep.UPLOADING */
|
|
2780
|
+
contributionStep === "UPLOADING" /* ParticipantContributionStep.UPLOADING */ ||
|
|
2781
|
+
contributionStep === "COMPLETED" /* ParticipantContributionStep.COMPLETED */))
|
|
2467
2782
|
timeoutType = "BLOCKING_CONTRIBUTION" /* TimeoutType.BLOCKING_CONTRIBUTION */;
|
|
2468
2783
|
if (timeoutExpirationDateInMsForVerificationCloudFunction > 0 &&
|
|
2469
2784
|
timeoutExpirationDateInMsForVerificationCloudFunction < currentServerTimestamp &&
|
|
2470
2785
|
contributionStep === "VERIFYING" /* ParticipantContributionStep.VERIFYING */)
|
|
2471
2786
|
timeoutType = "BLOCKING_CLOUD_FUNCTION" /* TimeoutType.BLOCKING_CLOUD_FUNCTION */;
|
|
2472
|
-
printLog(`${timeoutType} detected for circuit ${circuit.id} - ceremony ${ceremony.id}`, LogLevel.DEBUG);
|
|
2473
2787
|
if (!timeoutType)
|
|
2474
2788
|
// Do not use `logAndThrowError` method to avoid the function to exit before checking every ceremony.
|
|
2475
|
-
printLog(`No timeout for circuit ${circuit.id} - ceremony ${ceremony.id}`, LogLevel.
|
|
2789
|
+
printLog(`No timeout for circuit ${circuit.id} - ceremony ${ceremony.id}`, LogLevel.DEBUG);
|
|
2476
2790
|
else {
|
|
2791
|
+
printLog(`${timeoutType} detected for circuit ${circuit.id} - ceremony ${ceremony.id}`, LogLevel.WARN);
|
|
2477
2792
|
// Case (E).
|
|
2478
2793
|
let nextCurrentContributorId = "";
|
|
2479
2794
|
// Prepare Firestore batch of txs.
|
|
@@ -2524,7 +2839,7 @@ const checkAndRemoveBlockingContributor = functions__namespace
|
|
|
2524
2839
|
});
|
|
2525
2840
|
// Send atomic update for Firestore.
|
|
2526
2841
|
await batch.commit();
|
|
2527
|
-
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.
|
|
2842
|
+
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);
|
|
2528
2843
|
}
|
|
2529
2844
|
}
|
|
2530
2845
|
}
|
|
@@ -2540,7 +2855,7 @@ const checkAndRemoveBlockingContributor = functions__namespace
|
|
|
2540
2855
|
const resumeContributionAfterTimeoutExpiration = functions__namespace
|
|
2541
2856
|
.region("europe-west1")
|
|
2542
2857
|
.runWith({
|
|
2543
|
-
memory: "
|
|
2858
|
+
memory: "1GB"
|
|
2544
2859
|
})
|
|
2545
2860
|
.https.onCall(async (data, context) => {
|
|
2546
2861
|
if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
|
|
@@ -2563,7 +2878,8 @@ const resumeContributionAfterTimeoutExpiration = functions__namespace
|
|
|
2563
2878
|
if (status === "EXHUMED" /* ParticipantStatus.EXHUMED */)
|
|
2564
2879
|
await participantDoc.ref.update({
|
|
2565
2880
|
status: "READY" /* ParticipantStatus.READY */,
|
|
2566
|
-
lastUpdated: getCurrentServerTimestampInMillis()
|
|
2881
|
+
lastUpdated: getCurrentServerTimestampInMillis(),
|
|
2882
|
+
tempContributionData: {}
|
|
2567
2883
|
});
|
|
2568
2884
|
else
|
|
2569
2885
|
logAndThrowError(SPECIFIC_ERRORS.SE_CONTRIBUTE_CANNOT_PROGRESS_TO_NEXT_CIRCUIT);
|
|
@@ -2572,9 +2888,11 @@ const resumeContributionAfterTimeoutExpiration = functions__namespace
|
|
|
2572
2888
|
|
|
2573
2889
|
admin.initializeApp();
|
|
2574
2890
|
|
|
2891
|
+
exports.bandadaValidateProof = bandadaValidateProof;
|
|
2575
2892
|
exports.checkAndPrepareCoordinatorForFinalization = checkAndPrepareCoordinatorForFinalization;
|
|
2576
2893
|
exports.checkAndRemoveBlockingContributor = checkAndRemoveBlockingContributor;
|
|
2577
2894
|
exports.checkIfObjectExist = checkIfObjectExist;
|
|
2895
|
+
exports.checkNonceOfSIWEAddress = checkNonceOfSIWEAddress;
|
|
2578
2896
|
exports.checkParticipantForCeremony = checkParticipantForCeremony;
|
|
2579
2897
|
exports.completeMultiPartUpload = completeMultiPartUpload;
|
|
2580
2898
|
exports.coordinateCeremonyParticipant = coordinateCeremonyParticipant;
|