@devtion/backend 0.0.0-56491a8 → 0.0.0-57a8ab9
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 +287 -106
- package/dist/src/functions/index.mjs +291 -111
- 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 +1 -0
- package/dist/types/functions/index.d.ts.map +1 -1
- package/dist/types/functions/storage.d.ts.map +1 -1
- package/dist/types/functions/timeout.d.ts.map +1 -1
- package/dist/types/functions/user.d.ts.map +1 -1
- package/dist/types/lib/errors.d.ts +2 -1
- package/dist/types/lib/errors.d.ts.map +1 -1
- package/dist/types/lib/utils.d.ts.map +1 -1
- package/dist/types/types/index.d.ts +25 -0
- package/dist/types/types/index.d.ts.map +1 -1
- package/package.json +4 -3
- package/src/functions/bandada.ts +156 -0
- package/src/functions/ceremony.ts +8 -3
- package/src/functions/circuit.ts +137 -115
- package/src/functions/index.ts +1 -0
- package/src/functions/participant.ts +8 -8
- package/src/functions/storage.ts +6 -3
- package/src/functions/timeout.ts +3 -2
- package/src/functions/user.ts +20 -10
- package/src/lib/errors.ts +6 -1
- package/src/lib/utils.ts +10 -8
- package/src/types/declarations.d.ts +1 -0
- package/src/types/index.ts +27 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @module @
|
|
3
|
-
* @version 1.
|
|
2
|
+
* @module @p0tion/backend
|
|
3
|
+
* @version 1.1.1
|
|
4
4
|
* @file MPC Phase 2 backend for Firebase services management
|
|
5
5
|
* @copyright Ethereum Foundation 2022
|
|
6
6
|
* @license MIT
|
|
@@ -31,6 +31,8 @@ var functionsV1 = require('firebase-functions/v1');
|
|
|
31
31
|
var functionsV2 = require('firebase-functions/v2');
|
|
32
32
|
var timerNode = require('timer-node');
|
|
33
33
|
var snarkjs = require('snarkjs');
|
|
34
|
+
var apiSdk = require('@bandada/api-sdk');
|
|
35
|
+
var auth = require('firebase-admin/auth');
|
|
34
36
|
|
|
35
37
|
function _interopNamespaceDefault(e) {
|
|
36
38
|
var n = Object.create(null);
|
|
@@ -72,7 +74,7 @@ var LogLevel;
|
|
|
72
74
|
* @notice the set of Firebase Functions status codes. The codes are the same at the
|
|
73
75
|
* ones exposed by {@link https://github.com/grpc/grpc/blob/master/doc/statuscodes.md | gRPC}.
|
|
74
76
|
* @param errorCode <FunctionsErrorCode> - the set of possible error codes.
|
|
75
|
-
* @param message <string> - the error
|
|
77
|
+
* @param message <string> - the error message.
|
|
76
78
|
* @param [details] <string> - the details of the error (optional).
|
|
77
79
|
* @returns <HttpsError>
|
|
78
80
|
*/
|
|
@@ -144,7 +146,8 @@ const SPECIFIC_ERRORS = {
|
|
|
144
146
|
SE_VM_FAILED_COMMAND_EXECUTION: makeError("failed-precondition", "VM command execution failed", "Please, contact the coordinator if this error persists."),
|
|
145
147
|
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
148
|
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.")
|
|
149
|
+
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_UNKNOWN_COMMAND_STATUS: makeError("unavailable", "VM command execution has failed due to an unknown status code", "Please, contact the coordinator if this error persists.")
|
|
148
151
|
};
|
|
149
152
|
/**
|
|
150
153
|
* A set of common errors.
|
|
@@ -287,7 +290,7 @@ const queryOpenedCeremonies = async () => {
|
|
|
287
290
|
const getCircuitDocumentByPosition = async (ceremonyId, sequencePosition) => {
|
|
288
291
|
// Query for all ceremony circuits.
|
|
289
292
|
const circuits = await getCeremonyCircuits(ceremonyId);
|
|
290
|
-
// Apply a filter using the sequence
|
|
293
|
+
// Apply a filter using the sequence position.
|
|
291
294
|
const matchedCircuits = circuits.filter((circuit) => circuit.data().sequencePosition === sequencePosition);
|
|
292
295
|
if (matchedCircuits.length !== 1)
|
|
293
296
|
logAndThrowError(COMMON_ERRORS.CM_NO_CIRCUIT_FOR_GIVEN_SEQUENCE_POSITION);
|
|
@@ -328,7 +331,7 @@ const downloadArtifactFromS3Bucket = async (bucketName, objectKey, localFilePath
|
|
|
328
331
|
const writeStream = node_fs.createWriteStream(localFilePath);
|
|
329
332
|
const streamPipeline = node_util.promisify(node_stream.pipeline);
|
|
330
333
|
await streamPipeline(response.body, writeStream);
|
|
331
|
-
writeStream.on(
|
|
334
|
+
writeStream.on("finish", () => {
|
|
332
335
|
writeStream.end();
|
|
333
336
|
});
|
|
334
337
|
};
|
|
@@ -452,12 +455,14 @@ const htmlEncodeCircuitData = (circuitDocument) => ({
|
|
|
452
455
|
const getGitHubVariables = () => {
|
|
453
456
|
if (!process.env.GITHUB_MINIMUM_FOLLOWERS ||
|
|
454
457
|
!process.env.GITHUB_MINIMUM_FOLLOWING ||
|
|
455
|
-
!process.env.GITHUB_MINIMUM_PUBLIC_REPOS
|
|
458
|
+
!process.env.GITHUB_MINIMUM_PUBLIC_REPOS ||
|
|
459
|
+
!process.env.GITHUB_MINIMUM_AGE)
|
|
456
460
|
logAndThrowError(COMMON_ERRORS.CM_WRONG_CONFIGURATION);
|
|
457
461
|
return {
|
|
458
462
|
minimumFollowers: Number(process.env.GITHUB_MINIMUM_FOLLOWERS),
|
|
459
463
|
minimumFollowing: Number(process.env.GITHUB_MINIMUM_FOLLOWING),
|
|
460
|
-
minimumPublicRepos: Number(process.env.GITHUB_MINIMUM_PUBLIC_REPOS)
|
|
464
|
+
minimumPublicRepos: Number(process.env.GITHUB_MINIMUM_PUBLIC_REPOS),
|
|
465
|
+
minimumAge: Number(process.env.GITHUB_MINIMUM_AGE)
|
|
461
466
|
};
|
|
462
467
|
};
|
|
463
468
|
/**
|
|
@@ -467,7 +472,7 @@ const getGitHubVariables = () => {
|
|
|
467
472
|
const getAWSVariables = () => {
|
|
468
473
|
if (!process.env.AWS_ACCESS_KEY_ID ||
|
|
469
474
|
!process.env.AWS_SECRET_ACCESS_KEY ||
|
|
470
|
-
!process.env.
|
|
475
|
+
!process.env.AWS_INSTANCE_PROFILE_ARN ||
|
|
471
476
|
!process.env.AWS_AMI_ID ||
|
|
472
477
|
!process.env.AWS_SNS_TOPIC_ARN)
|
|
473
478
|
logAndThrowError(COMMON_ERRORS.CM_WRONG_CONFIGURATION);
|
|
@@ -475,7 +480,7 @@ const getAWSVariables = () => {
|
|
|
475
480
|
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
476
481
|
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
|
477
482
|
region: process.env.AWS_REGION || "eu-central-1",
|
|
478
|
-
|
|
483
|
+
instanceProfileArn: process.env.AWS_INSTANCE_PROFILE_ARN,
|
|
479
484
|
amiId: process.env.AWS_AMI_ID,
|
|
480
485
|
snsTopic: process.env.AWS_SNS_TOPIC_ARN
|
|
481
486
|
};
|
|
@@ -553,16 +558,18 @@ const registerAuthUser = functions__namespace
|
|
|
553
558
|
email === process.env.CUSTOM_CLAIMS_COORDINATOR_EMAIL_ADDRESS_OR_DOMAIN)) {
|
|
554
559
|
const auth = admin.auth();
|
|
555
560
|
// if provider == github.com let's use our functions to check the user's reputation
|
|
556
|
-
if (user.providerData[0].providerId === "github.com") {
|
|
561
|
+
if (user.providerData.length > 0 && user.providerData[0].providerId === "github.com") {
|
|
557
562
|
const vars = getGitHubVariables();
|
|
558
563
|
// this return true or false
|
|
559
564
|
try {
|
|
560
|
-
const { reputable, avatarUrl: avatarURL } = await actions.githubReputation(user.providerData[0].uid, vars.minimumFollowing, vars.minimumFollowers, vars.minimumPublicRepos);
|
|
565
|
+
const { reputable, avatarUrl: avatarURL } = await actions.githubReputation(user.providerData[0].uid, vars.minimumFollowing, vars.minimumFollowers, vars.minimumPublicRepos, vars.minimumAge);
|
|
561
566
|
if (!reputable) {
|
|
562
567
|
// Delete user
|
|
563
568
|
await auth.deleteUser(user.uid);
|
|
564
569
|
// 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
|
|
570
|
+
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
|
|
571
|
+
? user.uid
|
|
572
|
+
: 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
573
|
}
|
|
567
574
|
// store locally
|
|
568
575
|
avatarUrl = avatarURL;
|
|
@@ -577,13 +584,13 @@ const registerAuthUser = functions__namespace
|
|
|
577
584
|
}
|
|
578
585
|
// Set document (nb. we refer to providerData[0] because we use Github OAuth provider only).
|
|
579
586
|
// In future releases we might want to loop through the providerData array as we support
|
|
580
|
-
// more providers.
|
|
587
|
+
// more providers.
|
|
581
588
|
await userRef.set({
|
|
582
589
|
name: encodedDisplayName,
|
|
583
590
|
encodedDisplayName,
|
|
584
591
|
// Metadata.
|
|
585
592
|
creationTime,
|
|
586
|
-
lastSignInTime,
|
|
593
|
+
lastSignInTime: lastSignInTime || creationTime,
|
|
587
594
|
// Optional.
|
|
588
595
|
email: email || "",
|
|
589
596
|
emailVerified: emailVerified || false,
|
|
@@ -593,7 +600,7 @@ const registerAuthUser = functions__namespace
|
|
|
593
600
|
// we want to create a new collection for the users to store the avatars
|
|
594
601
|
const avatarRef = firestore.collection(actions.commonTerms.collections.avatars.name).doc(uid);
|
|
595
602
|
await avatarRef.set({
|
|
596
|
-
avatarUrl: avatarUrl || ""
|
|
603
|
+
avatarUrl: avatarUrl || ""
|
|
597
604
|
});
|
|
598
605
|
printLog(`Authenticated user document with identifier ${uid} has been correctly stored`, LogLevel.DEBUG);
|
|
599
606
|
printLog(`Authenticated user avatar with identifier ${uid} has been correctly stored`, LogLevel.DEBUG);
|
|
@@ -733,7 +740,7 @@ const setupCeremony = functions__namespace
|
|
|
733
740
|
// Check if using the VM approach for contribution verification.
|
|
734
741
|
if (circuit.verification.cfOrVm === "VM" /* CircuitContributionVerificationMechanism.VM */) {
|
|
735
742
|
// VM command to be run at the startup.
|
|
736
|
-
const startupCommand = actions.vmBootstrapCommand(bucketName);
|
|
743
|
+
const startupCommand = actions.vmBootstrapCommand(`${bucketName}/circuits/${circuit.name}`);
|
|
737
744
|
// Get EC2 client.
|
|
738
745
|
const ec2Client = await createEC2Client();
|
|
739
746
|
// Get AWS variables.
|
|
@@ -742,7 +749,8 @@ const setupCeremony = functions__namespace
|
|
|
742
749
|
const vmCommands = actions.vmDependenciesAndCacheArtifactsCommand(`${bucketName}/${circuit.files?.initialZkeyStoragePath}`, `${bucketName}/${circuit.files?.potStoragePath}`, snsTopic, region);
|
|
743
750
|
printLog(`Check VM dependencies and cache artifacts commands ${vmCommands.join("\n")}`, LogLevel.DEBUG);
|
|
744
751
|
// Upload the post-startup commands script file.
|
|
745
|
-
|
|
752
|
+
printLog(`Uploading VM post-startup commands script file ${actions.vmBootstrapScriptFilename}`, LogLevel.DEBUG);
|
|
753
|
+
await uploadFileToBucketNoFile(bucketName, `circuits/${circuit.name}/${actions.vmBootstrapScriptFilename}`, vmCommands.join("\n"));
|
|
746
754
|
// Compute the VM disk space requirement (in GB).
|
|
747
755
|
const vmDiskSize = actions.computeDiskSizeForVM(circuit.zKeySizeInBytes, circuit.metadata?.pot);
|
|
748
756
|
printLog(`Check VM startup commands ${startupCommand.join("\n")}`, LogLevel.DEBUG);
|
|
@@ -836,7 +844,7 @@ const finalizeCeremony = functions__namespace
|
|
|
836
844
|
// Get ceremony circuits.
|
|
837
845
|
const circuits = await getCeremonyCircuits(ceremonyId);
|
|
838
846
|
// Get final contribution for each circuit.
|
|
839
|
-
// nb. the `getFinalContributionDocument` checks the
|
|
847
|
+
// nb. the `getFinalContributionDocument` checks the existence of the final contribution document (if not present, throws).
|
|
840
848
|
// Therefore, we just need to call the method without taking any data to verify the pre-condition of having already computed
|
|
841
849
|
// the final contributions for each ceremony circuit.
|
|
842
850
|
for await (const circuit of circuits)
|
|
@@ -889,7 +897,7 @@ dotenv.config();
|
|
|
889
897
|
* @dev true when the participant can participate (1.A, 3.B, 1.D); otherwise false.
|
|
890
898
|
*/
|
|
891
899
|
const checkParticipantForCeremony = functions__namespace
|
|
892
|
-
.region(
|
|
900
|
+
.region("europe-west1")
|
|
893
901
|
.runWith({
|
|
894
902
|
memory: "512MB"
|
|
895
903
|
})
|
|
@@ -960,7 +968,7 @@ const checkParticipantForCeremony = functions__namespace
|
|
|
960
968
|
participantDoc.ref.update({
|
|
961
969
|
status: "EXHUMED" /* ParticipantStatus.EXHUMED */,
|
|
962
970
|
contributions,
|
|
963
|
-
tempContributionData: tempContributionData
|
|
971
|
+
tempContributionData: tempContributionData || firestore.FieldValue.delete(),
|
|
964
972
|
contributionStep: "DOWNLOADING" /* ParticipantContributionStep.DOWNLOADING */,
|
|
965
973
|
contributionStartedAt: 0,
|
|
966
974
|
verificationStartedAt: firestore.FieldValue.delete(),
|
|
@@ -993,7 +1001,7 @@ const checkParticipantForCeremony = functions__namespace
|
|
|
993
1001
|
* 2) the participant has just finished the contribution for a circuit (contributionProgress != 0 && status = CONTRIBUTED && contributionStep = COMPLETED).
|
|
994
1002
|
*/
|
|
995
1003
|
const progressToNextCircuitForContribution = functions__namespace
|
|
996
|
-
.region(
|
|
1004
|
+
.region("europe-west1")
|
|
997
1005
|
.runWith({
|
|
998
1006
|
memory: "512MB"
|
|
999
1007
|
})
|
|
@@ -1040,7 +1048,7 @@ const progressToNextCircuitForContribution = functions__namespace
|
|
|
1040
1048
|
* 5) Completed contribution computation and verification.
|
|
1041
1049
|
*/
|
|
1042
1050
|
const progressToNextContributionStep = functions__namespace
|
|
1043
|
-
.region(
|
|
1051
|
+
.region("europe-west1")
|
|
1044
1052
|
.runWith({
|
|
1045
1053
|
memory: "512MB"
|
|
1046
1054
|
})
|
|
@@ -1091,7 +1099,7 @@ const progressToNextContributionStep = functions__namespace
|
|
|
1091
1099
|
* @dev enable the current contributor to resume a contribution from where it had left off.
|
|
1092
1100
|
*/
|
|
1093
1101
|
const permanentlyStoreCurrentContributionTimeAndHash = functions__namespace
|
|
1094
|
-
.region(
|
|
1102
|
+
.region("europe-west1")
|
|
1095
1103
|
.runWith({
|
|
1096
1104
|
memory: "512MB"
|
|
1097
1105
|
})
|
|
@@ -1133,7 +1141,7 @@ const permanentlyStoreCurrentContributionTimeAndHash = functions__namespace
|
|
|
1133
1141
|
* @dev enable the current contributor to resume a multi-part upload from where it had left off.
|
|
1134
1142
|
*/
|
|
1135
1143
|
const temporaryStoreCurrentContributionMultiPartUploadId = functions__namespace
|
|
1136
|
-
.region(
|
|
1144
|
+
.region("europe-west1")
|
|
1137
1145
|
.runWith({
|
|
1138
1146
|
memory: "512MB"
|
|
1139
1147
|
})
|
|
@@ -1171,7 +1179,7 @@ const temporaryStoreCurrentContributionMultiPartUploadId = functions__namespace
|
|
|
1171
1179
|
* @dev enable the current contributor to resume a multi-part upload from where it had left off.
|
|
1172
1180
|
*/
|
|
1173
1181
|
const temporaryStoreCurrentContributionUploadedChunkData = functions__namespace
|
|
1174
|
-
.region(
|
|
1182
|
+
.region("europe-west1")
|
|
1175
1183
|
.runWith({
|
|
1176
1184
|
memory: "512MB"
|
|
1177
1185
|
})
|
|
@@ -1213,7 +1221,7 @@ const temporaryStoreCurrentContributionUploadedChunkData = functions__namespace
|
|
|
1213
1221
|
* contributed to every selected ceremony circuits (= DONE).
|
|
1214
1222
|
*/
|
|
1215
1223
|
const checkAndPrepareCoordinatorForFinalization = functions__namespace
|
|
1216
|
-
.region(
|
|
1224
|
+
.region("europe-west1")
|
|
1217
1225
|
.runWith({
|
|
1218
1226
|
memory: "512MB"
|
|
1219
1227
|
})
|
|
@@ -1365,54 +1373,74 @@ const coordinate = async (participant, circuit, isSingleParticipantCoordination,
|
|
|
1365
1373
|
* Wait until the command has completed its execution inside the VM.
|
|
1366
1374
|
* @dev this method implements a custom interval to check 5 times after 1 minute if the command execution
|
|
1367
1375
|
* has been completed or not by calling the `retrieveCommandStatus` method.
|
|
1368
|
-
* @param {any} resolve the promise.
|
|
1369
|
-
* @param {any} reject the promise.
|
|
1370
1376
|
* @param {SSMClient} ssm the SSM client.
|
|
1371
1377
|
* @param {string} vmInstanceId the unique identifier of the VM instance.
|
|
1372
1378
|
* @param {string} commandId the unique identifier of the VM command.
|
|
1373
1379
|
* @returns <Promise<void>> true when the command execution succeed; otherwise false.
|
|
1374
1380
|
*/
|
|
1375
|
-
const waitForVMCommandExecution = (
|
|
1376
|
-
const
|
|
1381
|
+
const waitForVMCommandExecution = (ssm, vmInstanceId, commandId) => new Promise((resolve, reject) => {
|
|
1382
|
+
const poll = async () => {
|
|
1377
1383
|
try {
|
|
1378
1384
|
// Get command status.
|
|
1379
1385
|
const cmdStatus = await actions.retrieveCommandStatus(ssm, vmInstanceId, commandId);
|
|
1380
1386
|
printLog(`Checking command ${commandId} status => ${cmdStatus}`, LogLevel.DEBUG);
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1387
|
+
let error;
|
|
1388
|
+
switch (cmdStatus) {
|
|
1389
|
+
case clientSsm.CommandInvocationStatus.CANCELLING:
|
|
1390
|
+
case clientSsm.CommandInvocationStatus.CANCELLED: {
|
|
1391
|
+
error = SPECIFIC_ERRORS.SE_VM_CANCELLED_COMMAND_EXECUTION;
|
|
1392
|
+
break;
|
|
1393
|
+
}
|
|
1394
|
+
case clientSsm.CommandInvocationStatus.DELAYED: {
|
|
1395
|
+
error = SPECIFIC_ERRORS.SE_VM_DELAYED_COMMAND_EXECUTION;
|
|
1396
|
+
break;
|
|
1397
|
+
}
|
|
1398
|
+
case clientSsm.CommandInvocationStatus.FAILED: {
|
|
1399
|
+
error = SPECIFIC_ERRORS.SE_VM_FAILED_COMMAND_EXECUTION;
|
|
1400
|
+
break;
|
|
1401
|
+
}
|
|
1402
|
+
case clientSsm.CommandInvocationStatus.TIMED_OUT: {
|
|
1403
|
+
error = SPECIFIC_ERRORS.SE_VM_TIMEDOUT_COMMAND_EXECUTION;
|
|
1404
|
+
break;
|
|
1405
|
+
}
|
|
1406
|
+
case clientSsm.CommandInvocationStatus.IN_PROGRESS:
|
|
1407
|
+
case clientSsm.CommandInvocationStatus.PENDING: {
|
|
1408
|
+
// wait a minute and poll again
|
|
1409
|
+
setTimeout(poll, 60000);
|
|
1410
|
+
return;
|
|
1411
|
+
}
|
|
1412
|
+
case clientSsm.CommandInvocationStatus.SUCCESS: {
|
|
1413
|
+
printLog(`Command ${commandId} successfully completed`, LogLevel.DEBUG);
|
|
1414
|
+
// Resolve the promise.
|
|
1415
|
+
resolve();
|
|
1416
|
+
return;
|
|
1417
|
+
}
|
|
1418
|
+
default: {
|
|
1419
|
+
logAndThrowError(SPECIFIC_ERRORS.SE_VM_UNKNOWN_COMMAND_STATUS);
|
|
1420
|
+
}
|
|
1397
1421
|
}
|
|
1398
|
-
|
|
1399
|
-
logAndThrowError(
|
|
1400
|
-
reject();
|
|
1422
|
+
if (error) {
|
|
1423
|
+
logAndThrowError(error);
|
|
1401
1424
|
}
|
|
1402
1425
|
}
|
|
1403
1426
|
catch (error) {
|
|
1404
1427
|
printLog(`Invalid command ${commandId} execution`, LogLevel.DEBUG);
|
|
1428
|
+
const ec2 = await createEC2Client();
|
|
1429
|
+
// if it errors out, let's just log it as a warning so the coordinator is aware
|
|
1430
|
+
try {
|
|
1431
|
+
await actions.stopEC2Instance(ec2, vmInstanceId);
|
|
1432
|
+
}
|
|
1433
|
+
catch (error) {
|
|
1434
|
+
printLog(`Error while stopping VM instance ${vmInstanceId} - Error ${error}`, LogLevel.WARN);
|
|
1435
|
+
}
|
|
1405
1436
|
if (!error.toString().includes(commandId))
|
|
1406
1437
|
logAndThrowError(COMMON_ERRORS.CM_INVALID_COMMAND_EXECUTION);
|
|
1407
1438
|
// Reject the promise.
|
|
1408
1439
|
reject();
|
|
1409
1440
|
}
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
}
|
|
1414
|
-
}, 60000); // 1 minute.
|
|
1415
|
-
};
|
|
1441
|
+
};
|
|
1442
|
+
setTimeout(poll, 60000);
|
|
1443
|
+
});
|
|
1416
1444
|
/**
|
|
1417
1445
|
* This method is used to coordinate the waiting queues of ceremony circuits.
|
|
1418
1446
|
* @dev this cloud function is triggered whenever an update of a document related to a participant of a ceremony occurs.
|
|
@@ -1433,7 +1461,7 @@ const waitForVMCommandExecution = (resolve, reject, ssm, vmInstanceId, commandId
|
|
|
1433
1461
|
* - Just completed a contribution or all contributions for each circuit. If yes, coordinate (multi-participant scenario).
|
|
1434
1462
|
*/
|
|
1435
1463
|
const coordinateCeremonyParticipant = functionsV1__namespace
|
|
1436
|
-
.region(
|
|
1464
|
+
.region("europe-west1")
|
|
1437
1465
|
.runWith({
|
|
1438
1466
|
memory: "512MB"
|
|
1439
1467
|
})
|
|
@@ -1504,11 +1532,9 @@ const checkIfVMRunning = async (ec2, vmInstanceId, attempts = 5) => {
|
|
|
1504
1532
|
const isVMRunning = await actions.checkIfRunning(ec2, vmInstanceId);
|
|
1505
1533
|
if (!isVMRunning) {
|
|
1506
1534
|
printLog(`VM not running, ${attempts - 1} attempts remaining. Retrying in 1 minute...`, LogLevel.DEBUG);
|
|
1507
|
-
return
|
|
1508
|
-
}
|
|
1509
|
-
else {
|
|
1510
|
-
return true;
|
|
1535
|
+
return checkIfVMRunning(ec2, vmInstanceId, attempts - 1);
|
|
1511
1536
|
}
|
|
1537
|
+
return true;
|
|
1512
1538
|
};
|
|
1513
1539
|
/**
|
|
1514
1540
|
* Verify the contribution of a participant computed while contributing to a specific circuit of a ceremony.
|
|
@@ -1536,7 +1562,7 @@ const checkIfVMRunning = async (ec2, vmInstanceId, attempts = 5) => {
|
|
|
1536
1562
|
* 1.A.4.C.1) If true, update circuit waiting for queue and average timings accordingly to contribution verification results;
|
|
1537
1563
|
* 2) Send all updates atomically to the Firestore database.
|
|
1538
1564
|
*/
|
|
1539
|
-
const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB", timeoutSeconds: 3600, region:
|
|
1565
|
+
const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB", timeoutSeconds: 3600, region: "europe-west1" }, async (request) => {
|
|
1540
1566
|
if (!request.auth || (!request.auth.token.participant && !request.auth.token.coordinator))
|
|
1541
1567
|
logAndThrowError(SPECIFIC_ERRORS.SE_AUTH_NO_CURRENT_AUTH_USER);
|
|
1542
1568
|
if (!request.data.ceremonyId ||
|
|
@@ -1647,8 +1673,6 @@ const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB"
|
|
|
1647
1673
|
lastZkeyBlake2bHash = match.at(0);
|
|
1648
1674
|
// re upload the formatted verification transcript
|
|
1649
1675
|
await uploadFileToBucket(bucketName, verificationTranscriptStoragePathAndFilename, verificationTranscriptTemporaryLocalPath, true);
|
|
1650
|
-
// Stop VM instance.
|
|
1651
|
-
await actions.stopEC2Instance(ec2, vmInstanceId);
|
|
1652
1676
|
}
|
|
1653
1677
|
else {
|
|
1654
1678
|
// Upload verification transcript.
|
|
@@ -1709,6 +1733,18 @@ const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB"
|
|
|
1709
1733
|
lastUpdated: getCurrentServerTimestampInMillis()
|
|
1710
1734
|
});
|
|
1711
1735
|
}
|
|
1736
|
+
// Stop VM instance
|
|
1737
|
+
if (isUsingVM) {
|
|
1738
|
+
// using try and catch as the VM stopping function can throw
|
|
1739
|
+
// however we want to continue without stopping as the
|
|
1740
|
+
// verification was valid, and inform the coordinator
|
|
1741
|
+
try {
|
|
1742
|
+
await actions.stopEC2Instance(ec2, vmInstanceId);
|
|
1743
|
+
}
|
|
1744
|
+
catch (error) {
|
|
1745
|
+
printLog(`Error while stopping VM instance ${vmInstanceId} - Error ${error}`, LogLevel.WARN);
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1712
1748
|
// Step (1.A.4.C)
|
|
1713
1749
|
if (!isFinalizing) {
|
|
1714
1750
|
// Step (1.A.4.C.1)
|
|
@@ -1723,7 +1759,7 @@ const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB"
|
|
|
1723
1759
|
const newAvgVerifyCloudFunctionTime = avgVerifyCloudFunctionTime > 0
|
|
1724
1760
|
? (avgVerifyCloudFunctionTime + verifyCloudFunctionTime) / 2
|
|
1725
1761
|
: verifyCloudFunctionTime;
|
|
1726
|
-
// Prepare tx to update circuit average contribution/verification time.
|
|
1762
|
+
// Prepare tx to update circuit average contribution/verification time.
|
|
1727
1763
|
const updatedCircuitDoc = await getDocumentById(actions.getCircuitsCollectionPath(ceremonyId), circuitId);
|
|
1728
1764
|
const { waitingQueue: updatedWaitingQueue } = updatedCircuitDoc.data();
|
|
1729
1765
|
/// @dev this must happen only for valid contributions.
|
|
@@ -1773,7 +1809,7 @@ const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB"
|
|
|
1773
1809
|
commandId = await actions.runCommandUsingSSM(ssm, vmInstanceId, verificationCommand);
|
|
1774
1810
|
printLog(`Starting the execution of command ${commandId}`, LogLevel.DEBUG);
|
|
1775
1811
|
// Step (1.A.3.3).
|
|
1776
|
-
return
|
|
1812
|
+
return waitForVMCommandExecution(ssm, vmInstanceId, commandId)
|
|
1777
1813
|
.then(async () => {
|
|
1778
1814
|
// Command execution successfully completed.
|
|
1779
1815
|
printLog(`Command ${commandId} execution has been successfully completed`, LogLevel.DEBUG);
|
|
@@ -1785,40 +1821,38 @@ const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB"
|
|
|
1785
1821
|
logAndThrowError(COMMON_ERRORS.CM_INVALID_COMMAND_EXECUTION);
|
|
1786
1822
|
});
|
|
1787
1823
|
}
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
printLog(`Error while unlinking temporary files - Error ${error}`, LogLevel.WARN);
|
|
1819
|
-
}
|
|
1820
|
-
await completeVerification();
|
|
1824
|
+
// CF approach.
|
|
1825
|
+
printLog(`CF mechanism`, LogLevel.DEBUG);
|
|
1826
|
+
const potStoragePath = actions.getPotStorageFilePath(files.potFilename);
|
|
1827
|
+
const firstZkeyStoragePath = actions.getZkeyStorageFilePath(prefix, `${prefix}_${actions.genesisZkeyIndex}.zkey`);
|
|
1828
|
+
// Prepare temporary file paths.
|
|
1829
|
+
// (nb. these are needed to download the necessary artifacts for verification from AWS S3).
|
|
1830
|
+
verificationTranscriptTemporaryLocalPath = createTemporaryLocalPath(verificationTranscriptCompleteFilename);
|
|
1831
|
+
const potTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}.pot`);
|
|
1832
|
+
const firstZkeyTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}_genesis.zkey`);
|
|
1833
|
+
const lastZkeyTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}_last.zkey`);
|
|
1834
|
+
// Create and populate transcript.
|
|
1835
|
+
const transcriptLogger = actions.createCustomLoggerForFile(verificationTranscriptTemporaryLocalPath);
|
|
1836
|
+
transcriptLogger.info(`${isFinalizing ? `Final verification` : `Verification`} transcript for ${prefix} circuit Phase 2 contribution.\n${isFinalizing ? `Coordinator ` : `Contributor # ${Number(lastZkeyIndex)}`} (${contributorOrCoordinatorIdentifier})\n`);
|
|
1837
|
+
// Step (1.A.2).
|
|
1838
|
+
await downloadArtifactFromS3Bucket(bucketName, potStoragePath, potTempFilePath);
|
|
1839
|
+
await downloadArtifactFromS3Bucket(bucketName, firstZkeyStoragePath, firstZkeyTempFilePath);
|
|
1840
|
+
await downloadArtifactFromS3Bucket(bucketName, lastZkeyStoragePath, lastZkeyTempFilePath);
|
|
1841
|
+
// Step (1.A.4).
|
|
1842
|
+
isContributionValid = await snarkjs.zKey.verifyFromInit(firstZkeyTempFilePath, potTempFilePath, lastZkeyTempFilePath, transcriptLogger);
|
|
1843
|
+
// Compute contribution hash.
|
|
1844
|
+
lastZkeyBlake2bHash = await actions.blake512FromPath(lastZkeyTempFilePath);
|
|
1845
|
+
// Free resources by unlinking temporary folders.
|
|
1846
|
+
// Do not free-up verification transcript path here.
|
|
1847
|
+
try {
|
|
1848
|
+
fs.unlinkSync(potTempFilePath);
|
|
1849
|
+
fs.unlinkSync(firstZkeyTempFilePath);
|
|
1850
|
+
fs.unlinkSync(lastZkeyTempFilePath);
|
|
1851
|
+
}
|
|
1852
|
+
catch (error) {
|
|
1853
|
+
printLog(`Error while unlinking temporary files - Error ${error}`, LogLevel.WARN);
|
|
1821
1854
|
}
|
|
1855
|
+
await completeVerification();
|
|
1822
1856
|
}
|
|
1823
1857
|
});
|
|
1824
1858
|
/**
|
|
@@ -1827,7 +1861,7 @@ const verifycontribution = functionsV2__namespace.https.onCall({ memory: "16GiB"
|
|
|
1827
1861
|
* this does not happen if the participant is actually the coordinator who is finalizing the ceremony.
|
|
1828
1862
|
*/
|
|
1829
1863
|
const refreshParticipantAfterContributionVerification = functionsV1__namespace
|
|
1830
|
-
.region(
|
|
1864
|
+
.region("europe-west1")
|
|
1831
1865
|
.runWith({
|
|
1832
1866
|
memory: "512MB"
|
|
1833
1867
|
})
|
|
@@ -1888,7 +1922,7 @@ const refreshParticipantAfterContributionVerification = functionsV1__namespace
|
|
|
1888
1922
|
* and verification key extracted from the circuit final contribution (as part of the ceremony finalization process).
|
|
1889
1923
|
*/
|
|
1890
1924
|
const finalizeCircuit = functionsV1__namespace
|
|
1891
|
-
.region(
|
|
1925
|
+
.region("europe-west1")
|
|
1892
1926
|
.runWith({
|
|
1893
1927
|
memory: "512MB"
|
|
1894
1928
|
})
|
|
@@ -2085,8 +2119,10 @@ const createBucket = functions__namespace
|
|
|
2085
2119
|
CORSConfiguration: {
|
|
2086
2120
|
CORSRules: [
|
|
2087
2121
|
{
|
|
2088
|
-
AllowedMethods: ["GET"],
|
|
2089
|
-
AllowedOrigins: ["*"]
|
|
2122
|
+
AllowedMethods: ["GET", "PUT"],
|
|
2123
|
+
AllowedOrigins: ["*"],
|
|
2124
|
+
ExposeHeaders: ["ETag", "Content-Length"],
|
|
2125
|
+
AllowedHeaders: ["*"]
|
|
2090
2126
|
}
|
|
2091
2127
|
]
|
|
2092
2128
|
}
|
|
@@ -2263,7 +2299,8 @@ const startMultiPartUpload = functions__namespace
|
|
|
2263
2299
|
const generatePreSignedUrlsParts = functions__namespace
|
|
2264
2300
|
.region("europe-west1")
|
|
2265
2301
|
.runWith({
|
|
2266
|
-
memory: "512MB"
|
|
2302
|
+
memory: "512MB",
|
|
2303
|
+
timeoutSeconds: 300
|
|
2267
2304
|
})
|
|
2268
2305
|
.https.onCall(async (data, context) => {
|
|
2269
2306
|
if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
|
|
@@ -2372,6 +2409,148 @@ const completeMultiPartUpload = functions__namespace
|
|
|
2372
2409
|
}
|
|
2373
2410
|
});
|
|
2374
2411
|
|
|
2412
|
+
const VKEY_DATA = {
|
|
2413
|
+
protocol: "groth16",
|
|
2414
|
+
curve: "bn128",
|
|
2415
|
+
nPublic: 3,
|
|
2416
|
+
vk_alpha_1: [
|
|
2417
|
+
"20491192805390485299153009773594534940189261866228447918068658471970481763042",
|
|
2418
|
+
"9383485363053290200918347156157836566562967994039712273449902621266178545958",
|
|
2419
|
+
"1"
|
|
2420
|
+
],
|
|
2421
|
+
vk_beta_2: [
|
|
2422
|
+
[
|
|
2423
|
+
"6375614351688725206403948262868962793625744043794305715222011528459656738731",
|
|
2424
|
+
"4252822878758300859123897981450591353533073413197771768651442665752259397132"
|
|
2425
|
+
],
|
|
2426
|
+
[
|
|
2427
|
+
"10505242626370262277552901082094356697409835680220590971873171140371331206856",
|
|
2428
|
+
"21847035105528745403288232691147584728191162732299865338377159692350059136679"
|
|
2429
|
+
],
|
|
2430
|
+
["1", "0"]
|
|
2431
|
+
],
|
|
2432
|
+
vk_gamma_2: [
|
|
2433
|
+
[
|
|
2434
|
+
"10857046999023057135944570762232829481370756359578518086990519993285655852781",
|
|
2435
|
+
"11559732032986387107991004021392285783925812861821192530917403151452391805634"
|
|
2436
|
+
],
|
|
2437
|
+
[
|
|
2438
|
+
"8495653923123431417604973247489272438418190587263600148770280649306958101930",
|
|
2439
|
+
"4082367875863433681332203403145435568316851327593401208105741076214120093531"
|
|
2440
|
+
],
|
|
2441
|
+
["1", "0"]
|
|
2442
|
+
],
|
|
2443
|
+
vk_delta_2: [
|
|
2444
|
+
[
|
|
2445
|
+
"3697618915467790705869942236922063775466274665053173890632463796679068973252",
|
|
2446
|
+
"14948341351907992175709156460547989243732741534604949238422596319735704165658"
|
|
2447
|
+
],
|
|
2448
|
+
[
|
|
2449
|
+
"3028459181652799888716942141752307629938889957960373621898607910203491239368",
|
|
2450
|
+
"11380736494786911280692284374675752681598754560757720296073023058533044108340"
|
|
2451
|
+
],
|
|
2452
|
+
["1", "0"]
|
|
2453
|
+
],
|
|
2454
|
+
vk_alphabeta_12: [
|
|
2455
|
+
[
|
|
2456
|
+
[
|
|
2457
|
+
"2029413683389138792403550203267699914886160938906632433982220835551125967885",
|
|
2458
|
+
"21072700047562757817161031222997517981543347628379360635925549008442030252106"
|
|
2459
|
+
],
|
|
2460
|
+
[
|
|
2461
|
+
"5940354580057074848093997050200682056184807770593307860589430076672439820312",
|
|
2462
|
+
"12156638873931618554171829126792193045421052652279363021382169897324752428276"
|
|
2463
|
+
],
|
|
2464
|
+
[
|
|
2465
|
+
"7898200236362823042373859371574133993780991612861777490112507062703164551277",
|
|
2466
|
+
"7074218545237549455313236346927434013100842096812539264420499035217050630853"
|
|
2467
|
+
]
|
|
2468
|
+
],
|
|
2469
|
+
[
|
|
2470
|
+
[
|
|
2471
|
+
"7077479683546002997211712695946002074877511277312570035766170199895071832130",
|
|
2472
|
+
"10093483419865920389913245021038182291233451549023025229112148274109565435465"
|
|
2473
|
+
],
|
|
2474
|
+
[
|
|
2475
|
+
"4595479056700221319381530156280926371456704509942304414423590385166031118820",
|
|
2476
|
+
"19831328484489333784475432780421641293929726139240675179672856274388269393268"
|
|
2477
|
+
],
|
|
2478
|
+
[
|
|
2479
|
+
"11934129596455521040620786944827826205713621633706285934057045369193958244500",
|
|
2480
|
+
"8037395052364110730298837004334506829870972346962140206007064471173334027475"
|
|
2481
|
+
]
|
|
2482
|
+
]
|
|
2483
|
+
],
|
|
2484
|
+
IC: [
|
|
2485
|
+
[
|
|
2486
|
+
"12951059800758687233303204819298121944551181861362200875212570257618182506154",
|
|
2487
|
+
"5751958719396509176593242305268064754837298673622815112953832050159760501392",
|
|
2488
|
+
"1"
|
|
2489
|
+
],
|
|
2490
|
+
[
|
|
2491
|
+
"9561588427935871983444704959674198910445823619407211599507208879011862515257",
|
|
2492
|
+
"14576201570478094842467636169770180675293504492823217349086195663150934064643",
|
|
2493
|
+
"1"
|
|
2494
|
+
],
|
|
2495
|
+
[
|
|
2496
|
+
"4811967233483727873912563574622036989372099129165459921963463310078093941559",
|
|
2497
|
+
"1874883809855039536107616044787862082553628089593740724610117059083415551067",
|
|
2498
|
+
"1"
|
|
2499
|
+
],
|
|
2500
|
+
[
|
|
2501
|
+
"12252730267779308452229639835051322390696643456253768618882001876621526827161",
|
|
2502
|
+
"7899194018737016222260328309937800777948677569409898603827268776967707173231",
|
|
2503
|
+
"1"
|
|
2504
|
+
]
|
|
2505
|
+
]
|
|
2506
|
+
};
|
|
2507
|
+
dotenv.config();
|
|
2508
|
+
const { BANDADA_API_URL, BANDADA_GROUP_ID } = process.env;
|
|
2509
|
+
const bandadaApi = new apiSdk.ApiSdk(BANDADA_API_URL);
|
|
2510
|
+
const bandadaValidateProof = functions__namespace
|
|
2511
|
+
.region("europe-west1")
|
|
2512
|
+
.runWith({
|
|
2513
|
+
memory: "512MB"
|
|
2514
|
+
})
|
|
2515
|
+
.https.onCall(async (data) => {
|
|
2516
|
+
if (!BANDADA_GROUP_ID)
|
|
2517
|
+
throw new Error("BANDADA_GROUP_ID is not defined in .env");
|
|
2518
|
+
const { proof, publicSignals } = data;
|
|
2519
|
+
const isCorrect = snarkjs.groth16.verify(VKEY_DATA, publicSignals, proof);
|
|
2520
|
+
if (!isCorrect)
|
|
2521
|
+
return {
|
|
2522
|
+
valid: false,
|
|
2523
|
+
message: "Invalid proof",
|
|
2524
|
+
token: ""
|
|
2525
|
+
};
|
|
2526
|
+
const commitment = data.publicSignals[1];
|
|
2527
|
+
const isMember = await bandadaApi.isGroupMember(BANDADA_GROUP_ID, commitment);
|
|
2528
|
+
if (!isMember)
|
|
2529
|
+
return {
|
|
2530
|
+
valid: false,
|
|
2531
|
+
message: "Not a member of the group",
|
|
2532
|
+
token: ""
|
|
2533
|
+
};
|
|
2534
|
+
const auth$1 = auth.getAuth();
|
|
2535
|
+
try {
|
|
2536
|
+
await admin.auth().createUser({
|
|
2537
|
+
uid: commitment
|
|
2538
|
+
});
|
|
2539
|
+
}
|
|
2540
|
+
catch (error) {
|
|
2541
|
+
// if user already exist then just pass
|
|
2542
|
+
if (error.code !== "auth/uid-already-exists") {
|
|
2543
|
+
throw new Error(error);
|
|
2544
|
+
}
|
|
2545
|
+
}
|
|
2546
|
+
const token = await auth$1.createCustomToken(commitment);
|
|
2547
|
+
return {
|
|
2548
|
+
valid: true,
|
|
2549
|
+
message: "Valid proof and group member",
|
|
2550
|
+
token
|
|
2551
|
+
};
|
|
2552
|
+
});
|
|
2553
|
+
|
|
2375
2554
|
dotenv.config();
|
|
2376
2555
|
/**
|
|
2377
2556
|
* Check and remove the current contributor if it doesn't complete the contribution on the specified amount of time.
|
|
@@ -2413,7 +2592,7 @@ const checkAndRemoveBlockingContributor = functions__namespace
|
|
|
2413
2592
|
// Get ceremony circuits.
|
|
2414
2593
|
const circuits = await getCeremonyCircuits(ceremony.id);
|
|
2415
2594
|
// Extract ceremony data.
|
|
2416
|
-
const { timeoutMechanismType, penalty } = ceremony.data();
|
|
2595
|
+
const { timeoutType: timeoutMechanismType, penalty } = ceremony.data();
|
|
2417
2596
|
for (const circuit of circuits) {
|
|
2418
2597
|
if (!circuit.data())
|
|
2419
2598
|
// Do not use `logAndThrowError` method to avoid the function to exit before checking every ceremony.
|
|
@@ -2563,7 +2742,8 @@ const resumeContributionAfterTimeoutExpiration = functions__namespace
|
|
|
2563
2742
|
if (status === "EXHUMED" /* ParticipantStatus.EXHUMED */)
|
|
2564
2743
|
await participantDoc.ref.update({
|
|
2565
2744
|
status: "READY" /* ParticipantStatus.READY */,
|
|
2566
|
-
lastUpdated: getCurrentServerTimestampInMillis()
|
|
2745
|
+
lastUpdated: getCurrentServerTimestampInMillis(),
|
|
2746
|
+
tempContributionData: {}
|
|
2567
2747
|
});
|
|
2568
2748
|
else
|
|
2569
2749
|
logAndThrowError(SPECIFIC_ERRORS.SE_CONTRIBUTE_CANNOT_PROGRESS_TO_NEXT_CIRCUIT);
|
|
@@ -2572,6 +2752,7 @@ const resumeContributionAfterTimeoutExpiration = functions__namespace
|
|
|
2572
2752
|
|
|
2573
2753
|
admin.initializeApp();
|
|
2574
2754
|
|
|
2755
|
+
exports.bandadaValidateProof = bandadaValidateProof;
|
|
2575
2756
|
exports.checkAndPrepareCoordinatorForFinalization = checkAndPrepareCoordinatorForFinalization;
|
|
2576
2757
|
exports.checkAndRemoveBlockingContributor = checkAndRemoveBlockingContributor;
|
|
2577
2758
|
exports.checkIfObjectExist = checkIfObjectExist;
|