@devtion/backend 0.0.0-0fd4368 → 0.0.0-142ec0a
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 +28 -2
- package/dist/src/functions/index.js +157 -165
- package/dist/src/functions/index.mjs +157 -165
- package/dist/types/functions/ceremony.d.ts.map +1 -1
- package/dist/types/functions/circuit.d.ts.map +1 -1
- package/dist/types/functions/storage.d.ts.map +1 -1
- package/dist/types/functions/timeout.d.ts.map +1 -1
- package/dist/types/functions/user.d.ts.map +1 -1
- package/dist/types/lib/errors.d.ts +1 -0
- package/dist/types/lib/errors.d.ts.map +1 -1
- package/dist/types/lib/utils.d.ts +1 -1
- package/dist/types/lib/utils.d.ts.map +1 -1
- package/dist/types/types/index.d.ts +1 -1
- package/package.json +3 -3
- package/src/functions/ceremony.ts +9 -4
- package/src/functions/circuit.ts +137 -185
- package/src/functions/participant.ts +9 -9
- package/src/functions/storage.ts +7 -4
- package/src/functions/timeout.ts +5 -4
- package/src/functions/user.ts +33 -8
- package/src/lib/errors.ts +5 -0
- package/src/lib/utils.ts +11 -9
- package/src/types/index.ts +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module @p0tion/backend
|
|
3
|
-
* @version 1.
|
|
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
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import admin from 'firebase-admin';
|
|
10
10
|
import * as functions from 'firebase-functions';
|
|
11
11
|
import dotenv from 'dotenv';
|
|
12
|
-
import { getCircuitsCollectionPath, getTimeoutsCollectionPath, commonTerms, finalContributionIndex, getContributionsCollectionPath, githubReputation, getBucketName, vmBootstrapCommand, vmDependenciesAndCacheArtifactsCommand, vmBootstrapScriptFilename, computeDiskSizeForVM, createEC2Instance, getParticipantsCollectionPath, terminateEC2Instance, formatZkeyIndex, getTranscriptStorageFilePath, getZkeyStorageFilePath, startEC2Instance, vmContributionVerificationCommand, runCommandUsingSSM, getPotStorageFilePath, genesisZkeyIndex, createCustomLoggerForFile, blake512FromPath, getVerificationKeyStorageFilePath, getVerifierContractStorageFilePath, computeSHA256ToHex,
|
|
12
|
+
import { getCircuitsCollectionPath, getTimeoutsCollectionPath, commonTerms, finalContributionIndex, getContributionsCollectionPath, githubReputation, getBucketName, vmBootstrapCommand, vmDependenciesAndCacheArtifactsCommand, vmBootstrapScriptFilename, computeDiskSizeForVM, createEC2Instance, getParticipantsCollectionPath, terminateEC2Instance, formatZkeyIndex, getTranscriptStorageFilePath, getZkeyStorageFilePath, startEC2Instance, vmContributionVerificationCommand, runCommandUsingSSM, getPotStorageFilePath, genesisZkeyIndex, createCustomLoggerForFile, blake512FromPath, getVerificationKeyStorageFilePath, getVerifierContractStorageFilePath, computeSHA256ToHex, checkIfRunning, retrieveCommandOutput, stopEC2Instance, verificationKeyAcronym, verifierSmartContractAcronym, retrieveCommandStatus } from '@p0tion/actions';
|
|
13
13
|
import { encode } from 'html-entities';
|
|
14
14
|
import { Timestamp, FieldValue } from 'firebase-admin/firestore';
|
|
15
15
|
import { S3Client, GetObjectCommand, PutObjectCommand, DeleteObjectCommand, HeadBucketCommand, CreateBucketCommand, PutPublicAccessBlockCommand, PutBucketCorsCommand, HeadObjectCommand, CreateMultipartUploadCommand, UploadPartCommand, CompleteMultipartUploadCommand } from '@aws-sdk/client-s3';
|
|
@@ -121,7 +121,8 @@ const SPECIFIC_ERRORS = {
|
|
|
121
121
|
SE_VM_FAILED_COMMAND_EXECUTION: makeError("failed-precondition", "VM command execution failed", "Please, contact the coordinator if this error persists."),
|
|
122
122
|
SE_VM_TIMEDOUT_COMMAND_EXECUTION: makeError("deadline-exceeded", "VM command execution took too long and has been timed-out", "Please, contact the coordinator if this error persists."),
|
|
123
123
|
SE_VM_CANCELLED_COMMAND_EXECUTION: makeError("cancelled", "VM command execution has been cancelled", "Please, contact the coordinator if this error persists."),
|
|
124
|
-
SE_VM_DELAYED_COMMAND_EXECUTION: makeError("unavailable", "VM command execution has been delayed since there were no available instance at the moment", "Please, contact the coordinator if this error persists.")
|
|
124
|
+
SE_VM_DELAYED_COMMAND_EXECUTION: makeError("unavailable", "VM command execution has been delayed since there were no available instance at the moment", "Please, contact the coordinator if this error persists."),
|
|
125
|
+
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.")
|
|
125
126
|
};
|
|
126
127
|
/**
|
|
127
128
|
* A set of common errors.
|
|
@@ -264,7 +265,7 @@ const queryOpenedCeremonies = async () => {
|
|
|
264
265
|
const getCircuitDocumentByPosition = async (ceremonyId, sequencePosition) => {
|
|
265
266
|
// Query for all ceremony circuits.
|
|
266
267
|
const circuits = await getCeremonyCircuits(ceremonyId);
|
|
267
|
-
// Apply a filter using the sequence
|
|
268
|
+
// Apply a filter using the sequence position.
|
|
268
269
|
const matchedCircuits = circuits.filter((circuit) => circuit.data().sequencePosition === sequencePosition);
|
|
269
270
|
if (matchedCircuits.length !== 1)
|
|
270
271
|
logAndThrowError(COMMON_ERRORS.CM_NO_CIRCUIT_FOR_GIVEN_SEQUENCE_POSITION);
|
|
@@ -305,7 +306,7 @@ const downloadArtifactFromS3Bucket = async (bucketName, objectKey, localFilePath
|
|
|
305
306
|
const writeStream = createWriteStream(localFilePath);
|
|
306
307
|
const streamPipeline = promisify(pipeline);
|
|
307
308
|
await streamPipeline(response.body, writeStream);
|
|
308
|
-
writeStream.on(
|
|
309
|
+
writeStream.on("finish", () => {
|
|
309
310
|
writeStream.end();
|
|
310
311
|
});
|
|
311
312
|
};
|
|
@@ -429,12 +430,14 @@ const htmlEncodeCircuitData = (circuitDocument) => ({
|
|
|
429
430
|
const getGitHubVariables = () => {
|
|
430
431
|
if (!process.env.GITHUB_MINIMUM_FOLLOWERS ||
|
|
431
432
|
!process.env.GITHUB_MINIMUM_FOLLOWING ||
|
|
432
|
-
!process.env.GITHUB_MINIMUM_PUBLIC_REPOS
|
|
433
|
+
!process.env.GITHUB_MINIMUM_PUBLIC_REPOS ||
|
|
434
|
+
!process.env.GITHUB_MINIMUM_AGE)
|
|
433
435
|
logAndThrowError(COMMON_ERRORS.CM_WRONG_CONFIGURATION);
|
|
434
436
|
return {
|
|
435
437
|
minimumFollowers: Number(process.env.GITHUB_MINIMUM_FOLLOWERS),
|
|
436
438
|
minimumFollowing: Number(process.env.GITHUB_MINIMUM_FOLLOWING),
|
|
437
|
-
minimumPublicRepos: Number(process.env.GITHUB_MINIMUM_PUBLIC_REPOS)
|
|
439
|
+
minimumPublicRepos: Number(process.env.GITHUB_MINIMUM_PUBLIC_REPOS),
|
|
440
|
+
minimumAge: Number(process.env.GITHUB_MINIMUM_AGE)
|
|
438
441
|
};
|
|
439
442
|
};
|
|
440
443
|
/**
|
|
@@ -444,7 +447,7 @@ const getGitHubVariables = () => {
|
|
|
444
447
|
const getAWSVariables = () => {
|
|
445
448
|
if (!process.env.AWS_ACCESS_KEY_ID ||
|
|
446
449
|
!process.env.AWS_SECRET_ACCESS_KEY ||
|
|
447
|
-
!process.env.
|
|
450
|
+
!process.env.AWS_INSTANCE_PROFILE_ARN ||
|
|
448
451
|
!process.env.AWS_AMI_ID ||
|
|
449
452
|
!process.env.AWS_SNS_TOPIC_ARN)
|
|
450
453
|
logAndThrowError(COMMON_ERRORS.CM_WRONG_CONFIGURATION);
|
|
@@ -452,7 +455,7 @@ const getAWSVariables = () => {
|
|
|
452
455
|
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
453
456
|
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
|
454
457
|
region: process.env.AWS_REGION || "eu-central-1",
|
|
455
|
-
|
|
458
|
+
instanceProfileArn: process.env.AWS_INSTANCE_PROFILE_ARN,
|
|
456
459
|
amiId: process.env.AWS_AMI_ID,
|
|
457
460
|
snsTopic: process.env.AWS_SNS_TOPIC_ARN
|
|
458
461
|
};
|
|
@@ -521,8 +524,10 @@ const registerAuthUser = functions
|
|
|
521
524
|
const { uid } = user;
|
|
522
525
|
// Reference to a document using uid.
|
|
523
526
|
const userRef = firestore.collection(commonTerms.collections.users.name).doc(uid);
|
|
524
|
-
// html encode the display name
|
|
525
|
-
const encodedDisplayName = encode(displayName);
|
|
527
|
+
// html encode the display name (or put the ID if the name is not displayed)
|
|
528
|
+
const encodedDisplayName = user.displayName === "Null" || user.displayName === null ? user.uid : encode(displayName);
|
|
529
|
+
// store the avatar URL of a contributor
|
|
530
|
+
let avatarUrl = "";
|
|
526
531
|
// we only do reputation check if the user is not a coordinator
|
|
527
532
|
if (!(email?.endsWith(`@${process.env.CUSTOM_CLAIMS_COORDINATOR_EMAIL_ADDRESS_OR_DOMAIN}`) ||
|
|
528
533
|
email === process.env.CUSTOM_CLAIMS_COORDINATOR_EMAIL_ADDRESS_OR_DOMAIN)) {
|
|
@@ -532,14 +537,18 @@ const registerAuthUser = functions
|
|
|
532
537
|
const vars = getGitHubVariables();
|
|
533
538
|
// this return true or false
|
|
534
539
|
try {
|
|
535
|
-
const
|
|
536
|
-
if (!
|
|
540
|
+
const { reputable, avatarUrl: avatarURL } = await githubReputation(user.providerData[0].uid, vars.minimumFollowing, vars.minimumFollowers, vars.minimumPublicRepos, vars.minimumAge);
|
|
541
|
+
if (!reputable) {
|
|
537
542
|
// Delete user
|
|
538
543
|
await auth.deleteUser(user.uid);
|
|
539
544
|
// Throw error
|
|
540
|
-
logAndThrowError(makeError("permission-denied", "The user is not allowed to sign up because their Github reputation is not high enough.", `The user ${user.displayName
|
|
545
|
+
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
|
|
546
|
+
? user.uid
|
|
547
|
+
: 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.`));
|
|
541
548
|
}
|
|
542
|
-
|
|
549
|
+
// store locally
|
|
550
|
+
avatarUrl = avatarURL;
|
|
551
|
+
printLog(`Github reputation check passed for user ${user.displayName === "Null" || user.displayName === null ? user.uid : user.displayName}`, LogLevel.DEBUG);
|
|
543
552
|
}
|
|
544
553
|
catch (error) {
|
|
545
554
|
// Delete user
|
|
@@ -549,6 +558,8 @@ const registerAuthUser = functions
|
|
|
549
558
|
}
|
|
550
559
|
}
|
|
551
560
|
// Set document (nb. we refer to providerData[0] because we use Github OAuth provider only).
|
|
561
|
+
// In future releases we might want to loop through the providerData array as we support
|
|
562
|
+
// more providers.
|
|
552
563
|
await userRef.set({
|
|
553
564
|
name: encodedDisplayName,
|
|
554
565
|
encodedDisplayName,
|
|
@@ -561,7 +572,13 @@ const registerAuthUser = functions
|
|
|
561
572
|
photoURL: photoURL || "",
|
|
562
573
|
lastUpdated: getCurrentServerTimestampInMillis()
|
|
563
574
|
});
|
|
575
|
+
// we want to create a new collection for the users to store the avatars
|
|
576
|
+
const avatarRef = firestore.collection(commonTerms.collections.avatars.name).doc(uid);
|
|
577
|
+
await avatarRef.set({
|
|
578
|
+
avatarUrl: avatarUrl || ""
|
|
579
|
+
});
|
|
564
580
|
printLog(`Authenticated user document with identifier ${uid} has been correctly stored`, LogLevel.DEBUG);
|
|
581
|
+
printLog(`Authenticated user avatar with identifier ${uid} has been correctly stored`, LogLevel.DEBUG);
|
|
565
582
|
});
|
|
566
583
|
/**
|
|
567
584
|
* Set custom claims for role-based access control on the newly created user.
|
|
@@ -698,7 +715,7 @@ const setupCeremony = functions
|
|
|
698
715
|
// Check if using the VM approach for contribution verification.
|
|
699
716
|
if (circuit.verification.cfOrVm === "VM" /* CircuitContributionVerificationMechanism.VM */) {
|
|
700
717
|
// VM command to be run at the startup.
|
|
701
|
-
const startupCommand = vmBootstrapCommand(bucketName);
|
|
718
|
+
const startupCommand = vmBootstrapCommand(`${bucketName}/circuits/${circuit.name}`);
|
|
702
719
|
// Get EC2 client.
|
|
703
720
|
const ec2Client = await createEC2Client();
|
|
704
721
|
// Get AWS variables.
|
|
@@ -707,7 +724,8 @@ const setupCeremony = functions
|
|
|
707
724
|
const vmCommands = vmDependenciesAndCacheArtifactsCommand(`${bucketName}/${circuit.files?.initialZkeyStoragePath}`, `${bucketName}/${circuit.files?.potStoragePath}`, snsTopic, region);
|
|
708
725
|
printLog(`Check VM dependencies and cache artifacts commands ${vmCommands.join("\n")}`, LogLevel.DEBUG);
|
|
709
726
|
// Upload the post-startup commands script file.
|
|
710
|
-
|
|
727
|
+
printLog(`Uploading VM post-startup commands script file ${vmBootstrapScriptFilename}`, LogLevel.DEBUG);
|
|
728
|
+
await uploadFileToBucketNoFile(bucketName, `circuits/${circuit.name}/${vmBootstrapScriptFilename}`, vmCommands.join("\n"));
|
|
711
729
|
// Compute the VM disk space requirement (in GB).
|
|
712
730
|
const vmDiskSize = computeDiskSizeForVM(circuit.zKeySizeInBytes, circuit.metadata?.pot);
|
|
713
731
|
printLog(`Check VM startup commands ${startupCommand.join("\n")}`, LogLevel.DEBUG);
|
|
@@ -801,7 +819,7 @@ const finalizeCeremony = functions
|
|
|
801
819
|
// Get ceremony circuits.
|
|
802
820
|
const circuits = await getCeremonyCircuits(ceremonyId);
|
|
803
821
|
// Get final contribution for each circuit.
|
|
804
|
-
// nb. the `getFinalContributionDocument` checks the
|
|
822
|
+
// nb. the `getFinalContributionDocument` checks the existence of the final contribution document (if not present, throws).
|
|
805
823
|
// Therefore, we just need to call the method without taking any data to verify the pre-condition of having already computed
|
|
806
824
|
// the final contributions for each ceremony circuit.
|
|
807
825
|
for await (const circuit of circuits)
|
|
@@ -854,7 +872,7 @@ dotenv.config();
|
|
|
854
872
|
* @dev true when the participant can participate (1.A, 3.B, 1.D); otherwise false.
|
|
855
873
|
*/
|
|
856
874
|
const checkParticipantForCeremony = functions
|
|
857
|
-
.region(
|
|
875
|
+
.region("europe-west1")
|
|
858
876
|
.runWith({
|
|
859
877
|
memory: "512MB"
|
|
860
878
|
})
|
|
@@ -925,7 +943,7 @@ const checkParticipantForCeremony = functions
|
|
|
925
943
|
participantDoc.ref.update({
|
|
926
944
|
status: "EXHUMED" /* ParticipantStatus.EXHUMED */,
|
|
927
945
|
contributions,
|
|
928
|
-
tempContributionData: tempContributionData
|
|
946
|
+
tempContributionData: tempContributionData || FieldValue.delete(),
|
|
929
947
|
contributionStep: "DOWNLOADING" /* ParticipantContributionStep.DOWNLOADING */,
|
|
930
948
|
contributionStartedAt: 0,
|
|
931
949
|
verificationStartedAt: FieldValue.delete(),
|
|
@@ -958,7 +976,7 @@ const checkParticipantForCeremony = functions
|
|
|
958
976
|
* 2) the participant has just finished the contribution for a circuit (contributionProgress != 0 && status = CONTRIBUTED && contributionStep = COMPLETED).
|
|
959
977
|
*/
|
|
960
978
|
const progressToNextCircuitForContribution = functions
|
|
961
|
-
.region(
|
|
979
|
+
.region("europe-west1")
|
|
962
980
|
.runWith({
|
|
963
981
|
memory: "512MB"
|
|
964
982
|
})
|
|
@@ -1005,7 +1023,7 @@ const progressToNextCircuitForContribution = functions
|
|
|
1005
1023
|
* 5) Completed contribution computation and verification.
|
|
1006
1024
|
*/
|
|
1007
1025
|
const progressToNextContributionStep = functions
|
|
1008
|
-
.region(
|
|
1026
|
+
.region("europe-west1")
|
|
1009
1027
|
.runWith({
|
|
1010
1028
|
memory: "512MB"
|
|
1011
1029
|
})
|
|
@@ -1056,7 +1074,7 @@ const progressToNextContributionStep = functions
|
|
|
1056
1074
|
* @dev enable the current contributor to resume a contribution from where it had left off.
|
|
1057
1075
|
*/
|
|
1058
1076
|
const permanentlyStoreCurrentContributionTimeAndHash = functions
|
|
1059
|
-
.region(
|
|
1077
|
+
.region("europe-west1")
|
|
1060
1078
|
.runWith({
|
|
1061
1079
|
memory: "512MB"
|
|
1062
1080
|
})
|
|
@@ -1098,7 +1116,7 @@ const permanentlyStoreCurrentContributionTimeAndHash = functions
|
|
|
1098
1116
|
* @dev enable the current contributor to resume a multi-part upload from where it had left off.
|
|
1099
1117
|
*/
|
|
1100
1118
|
const temporaryStoreCurrentContributionMultiPartUploadId = functions
|
|
1101
|
-
.region(
|
|
1119
|
+
.region("europe-west1")
|
|
1102
1120
|
.runWith({
|
|
1103
1121
|
memory: "512MB"
|
|
1104
1122
|
})
|
|
@@ -1136,7 +1154,7 @@ const temporaryStoreCurrentContributionMultiPartUploadId = functions
|
|
|
1136
1154
|
* @dev enable the current contributor to resume a multi-part upload from where it had left off.
|
|
1137
1155
|
*/
|
|
1138
1156
|
const temporaryStoreCurrentContributionUploadedChunkData = functions
|
|
1139
|
-
.region(
|
|
1157
|
+
.region("europe-west1")
|
|
1140
1158
|
.runWith({
|
|
1141
1159
|
memory: "512MB"
|
|
1142
1160
|
})
|
|
@@ -1178,7 +1196,7 @@ const temporaryStoreCurrentContributionUploadedChunkData = functions
|
|
|
1178
1196
|
* contributed to every selected ceremony circuits (= DONE).
|
|
1179
1197
|
*/
|
|
1180
1198
|
const checkAndPrepareCoordinatorForFinalization = functions
|
|
1181
|
-
.region(
|
|
1199
|
+
.region("europe-west1")
|
|
1182
1200
|
.runWith({
|
|
1183
1201
|
memory: "512MB"
|
|
1184
1202
|
})
|
|
@@ -1269,6 +1287,7 @@ const coordinate = async (participant, circuit, isSingleParticipantCoordination,
|
|
|
1269
1287
|
printLog(`Coordinate - executing scenario A - single - participantResumingAfterTimeoutExpiration`, LogLevel.DEBUG);
|
|
1270
1288
|
newParticipantStatus = "CONTRIBUTING" /* ParticipantStatus.CONTRIBUTING */;
|
|
1271
1289
|
newContributionStep = "DOWNLOADING" /* ParticipantContributionStep.DOWNLOADING */;
|
|
1290
|
+
newCurrentContributorId = participant.id;
|
|
1272
1291
|
}
|
|
1273
1292
|
// Scenario (B).
|
|
1274
1293
|
else if (participantIsNotCurrentContributor) {
|
|
@@ -1329,101 +1348,74 @@ const coordinate = async (participant, circuit, isSingleParticipantCoordination,
|
|
|
1329
1348
|
* Wait until the command has completed its execution inside the VM.
|
|
1330
1349
|
* @dev this method implements a custom interval to check 5 times after 1 minute if the command execution
|
|
1331
1350
|
* has been completed or not by calling the `retrieveCommandStatus` method.
|
|
1332
|
-
* @param {any} resolve the promise.
|
|
1333
|
-
* @param {any} reject the promise.
|
|
1334
1351
|
* @param {SSMClient} ssm the SSM client.
|
|
1335
1352
|
* @param {string} vmInstanceId the unique identifier of the VM instance.
|
|
1336
1353
|
* @param {string} commandId the unique identifier of the VM command.
|
|
1337
1354
|
* @returns <Promise<void>> true when the command execution succeed; otherwise false.
|
|
1338
1355
|
*/
|
|
1339
|
-
const waitForVMCommandExecution = (
|
|
1340
|
-
const
|
|
1356
|
+
const waitForVMCommandExecution = (ssm, vmInstanceId, commandId) => new Promise((resolve, reject) => {
|
|
1357
|
+
const poll = async () => {
|
|
1341
1358
|
try {
|
|
1342
1359
|
// Get command status.
|
|
1343
1360
|
const cmdStatus = await retrieveCommandStatus(ssm, vmInstanceId, commandId);
|
|
1344
1361
|
printLog(`Checking command ${commandId} status => ${cmdStatus}`, LogLevel.DEBUG);
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1362
|
+
let error;
|
|
1363
|
+
switch (cmdStatus) {
|
|
1364
|
+
case CommandInvocationStatus.CANCELLING:
|
|
1365
|
+
case CommandInvocationStatus.CANCELLED: {
|
|
1366
|
+
error = SPECIFIC_ERRORS.SE_VM_CANCELLED_COMMAND_EXECUTION;
|
|
1367
|
+
break;
|
|
1368
|
+
}
|
|
1369
|
+
case CommandInvocationStatus.DELAYED: {
|
|
1370
|
+
error = SPECIFIC_ERRORS.SE_VM_DELAYED_COMMAND_EXECUTION;
|
|
1371
|
+
break;
|
|
1372
|
+
}
|
|
1373
|
+
case CommandInvocationStatus.FAILED: {
|
|
1374
|
+
error = SPECIFIC_ERRORS.SE_VM_FAILED_COMMAND_EXECUTION;
|
|
1375
|
+
break;
|
|
1376
|
+
}
|
|
1377
|
+
case CommandInvocationStatus.TIMED_OUT: {
|
|
1378
|
+
error = SPECIFIC_ERRORS.SE_VM_TIMEDOUT_COMMAND_EXECUTION;
|
|
1379
|
+
break;
|
|
1380
|
+
}
|
|
1381
|
+
case CommandInvocationStatus.IN_PROGRESS:
|
|
1382
|
+
case CommandInvocationStatus.PENDING: {
|
|
1383
|
+
// wait a minute and poll again
|
|
1384
|
+
setTimeout(poll, 60000);
|
|
1385
|
+
return;
|
|
1386
|
+
}
|
|
1387
|
+
case CommandInvocationStatus.SUCCESS: {
|
|
1388
|
+
printLog(`Command ${commandId} successfully completed`, LogLevel.DEBUG);
|
|
1389
|
+
// Resolve the promise.
|
|
1390
|
+
resolve();
|
|
1391
|
+
return;
|
|
1392
|
+
}
|
|
1393
|
+
default: {
|
|
1394
|
+
logAndThrowError(SPECIFIC_ERRORS.SE_VM_UNKNOWN_COMMAND_STATUS);
|
|
1395
|
+
}
|
|
1361
1396
|
}
|
|
1362
|
-
|
|
1363
|
-
logAndThrowError(
|
|
1364
|
-
reject();
|
|
1397
|
+
if (error) {
|
|
1398
|
+
logAndThrowError(error);
|
|
1365
1399
|
}
|
|
1366
1400
|
}
|
|
1367
1401
|
catch (error) {
|
|
1368
1402
|
printLog(`Invalid command ${commandId} execution`, LogLevel.DEBUG);
|
|
1403
|
+
const ec2 = await createEC2Client();
|
|
1404
|
+
// if it errors out, let's just log it as a warning so the coordinator is aware
|
|
1405
|
+
try {
|
|
1406
|
+
await stopEC2Instance(ec2, vmInstanceId);
|
|
1407
|
+
}
|
|
1408
|
+
catch (error) {
|
|
1409
|
+
printLog(`Error while stopping VM instance ${vmInstanceId} - Error ${error}`, LogLevel.WARN);
|
|
1410
|
+
}
|
|
1369
1411
|
if (!error.toString().includes(commandId))
|
|
1370
1412
|
logAndThrowError(COMMON_ERRORS.CM_INVALID_COMMAND_EXECUTION);
|
|
1371
1413
|
// Reject the promise.
|
|
1372
1414
|
reject();
|
|
1373
1415
|
}
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
}
|
|
1378
|
-
}, 60000); // 1 minute.
|
|
1379
|
-
};
|
|
1380
|
-
/**
|
|
1381
|
-
* Wait until the artifacts have been downloaded.
|
|
1382
|
-
* @param {any} resolve the promise.
|
|
1383
|
-
* @param {any} reject the promise.
|
|
1384
|
-
* @param {string} potTempFilePath the tmp path to the locally downloaded pot file.
|
|
1385
|
-
* @param {string} firstZkeyTempFilePath the tmp path to the locally downloaded first zkey file.
|
|
1386
|
-
* @param {string} lastZkeyTempFilePath the tmp path to the locally downloaded last zkey file.
|
|
1387
|
-
*/
|
|
1388
|
-
const waitForFileDownload = (resolve, reject, potTempFilePath, firstZkeyTempFilePath, lastZkeyTempFilePath, circuitId, participantId) => {
|
|
1389
|
-
const maxWaitTime = 5 * 60 * 1000; // 5 minutes
|
|
1390
|
-
// every second check if the file download was completed
|
|
1391
|
-
const interval = setInterval(async () => {
|
|
1392
|
-
printLog(`Verifying that the artifacts were downloaded for circuit ${circuitId} and participant ${participantId}`, LogLevel.DEBUG);
|
|
1393
|
-
try {
|
|
1394
|
-
// check if files have been downloaded
|
|
1395
|
-
if (!fs.existsSync(potTempFilePath)) {
|
|
1396
|
-
printLog(`Pot file not found at ${potTempFilePath}`, LogLevel.DEBUG);
|
|
1397
|
-
}
|
|
1398
|
-
if (!fs.existsSync(firstZkeyTempFilePath)) {
|
|
1399
|
-
printLog(`First zkey file not found at ${firstZkeyTempFilePath}`, LogLevel.DEBUG);
|
|
1400
|
-
}
|
|
1401
|
-
if (!fs.existsSync(lastZkeyTempFilePath)) {
|
|
1402
|
-
printLog(`Last zkey file not found at ${lastZkeyTempFilePath}`, LogLevel.DEBUG);
|
|
1403
|
-
}
|
|
1404
|
-
// if all files were downloaded
|
|
1405
|
-
if (fs.existsSync(potTempFilePath) && fs.existsSync(firstZkeyTempFilePath) && fs.existsSync(lastZkeyTempFilePath)) {
|
|
1406
|
-
printLog(`All required files are present on disk.`, LogLevel.INFO);
|
|
1407
|
-
// resolve the promise
|
|
1408
|
-
resolve();
|
|
1409
|
-
}
|
|
1410
|
-
}
|
|
1411
|
-
catch (error) {
|
|
1412
|
-
// if we have an error then we print it as a warning and reject
|
|
1413
|
-
printLog(`Error while downloading files: ${error}`, LogLevel.WARN);
|
|
1414
|
-
reject();
|
|
1415
|
-
}
|
|
1416
|
-
finally {
|
|
1417
|
-
printLog(`Clearing the interval for file download. Circuit ${circuitId} and participant ${participantId}`, LogLevel.DEBUG);
|
|
1418
|
-
clearInterval(interval);
|
|
1419
|
-
}
|
|
1420
|
-
}, 5000);
|
|
1421
|
-
// we want to clean in 5 minutes in case
|
|
1422
|
-
setTimeout(() => {
|
|
1423
|
-
clearInterval(interval);
|
|
1424
|
-
reject(new Error('Timeout exceeded while waiting for files to be downloaded.'));
|
|
1425
|
-
}, maxWaitTime);
|
|
1426
|
-
};
|
|
1416
|
+
};
|
|
1417
|
+
setTimeout(poll, 60000);
|
|
1418
|
+
});
|
|
1427
1419
|
/**
|
|
1428
1420
|
* This method is used to coordinate the waiting queues of ceremony circuits.
|
|
1429
1421
|
* @dev this cloud function is triggered whenever an update of a document related to a participant of a ceremony occurs.
|
|
@@ -1444,7 +1436,7 @@ const waitForFileDownload = (resolve, reject, potTempFilePath, firstZkeyTempFile
|
|
|
1444
1436
|
* - Just completed a contribution or all contributions for each circuit. If yes, coordinate (multi-participant scenario).
|
|
1445
1437
|
*/
|
|
1446
1438
|
const coordinateCeremonyParticipant = functionsV1
|
|
1447
|
-
.region(
|
|
1439
|
+
.region("europe-west1")
|
|
1448
1440
|
.runWith({
|
|
1449
1441
|
memory: "512MB"
|
|
1450
1442
|
})
|
|
@@ -1515,11 +1507,9 @@ const checkIfVMRunning = async (ec2, vmInstanceId, attempts = 5) => {
|
|
|
1515
1507
|
const isVMRunning = await checkIfRunning(ec2, vmInstanceId);
|
|
1516
1508
|
if (!isVMRunning) {
|
|
1517
1509
|
printLog(`VM not running, ${attempts - 1} attempts remaining. Retrying in 1 minute...`, LogLevel.DEBUG);
|
|
1518
|
-
return
|
|
1519
|
-
}
|
|
1520
|
-
else {
|
|
1521
|
-
return true;
|
|
1510
|
+
return checkIfVMRunning(ec2, vmInstanceId, attempts - 1);
|
|
1522
1511
|
}
|
|
1512
|
+
return true;
|
|
1523
1513
|
};
|
|
1524
1514
|
/**
|
|
1525
1515
|
* Verify the contribution of a participant computed while contributing to a specific circuit of a ceremony.
|
|
@@ -1547,7 +1537,7 @@ const checkIfVMRunning = async (ec2, vmInstanceId, attempts = 5) => {
|
|
|
1547
1537
|
* 1.A.4.C.1) If true, update circuit waiting for queue and average timings accordingly to contribution verification results;
|
|
1548
1538
|
* 2) Send all updates atomically to the Firestore database.
|
|
1549
1539
|
*/
|
|
1550
|
-
const verifycontribution = functionsV2.https.onCall({ memory: "16GiB", timeoutSeconds: 3600, region:
|
|
1540
|
+
const verifycontribution = functionsV2.https.onCall({ memory: "16GiB", timeoutSeconds: 3600, region: "europe-west1" }, async (request) => {
|
|
1551
1541
|
if (!request.auth || (!request.auth.token.participant && !request.auth.token.coordinator))
|
|
1552
1542
|
logAndThrowError(SPECIFIC_ERRORS.SE_AUTH_NO_CURRENT_AUTH_USER);
|
|
1553
1543
|
if (!request.data.ceremonyId ||
|
|
@@ -1658,8 +1648,6 @@ const verifycontribution = functionsV2.https.onCall({ memory: "16GiB", timeoutSe
|
|
|
1658
1648
|
lastZkeyBlake2bHash = match.at(0);
|
|
1659
1649
|
// re upload the formatted verification transcript
|
|
1660
1650
|
await uploadFileToBucket(bucketName, verificationTranscriptStoragePathAndFilename, verificationTranscriptTemporaryLocalPath, true);
|
|
1661
|
-
// Stop VM instance.
|
|
1662
|
-
await stopEC2Instance(ec2, vmInstanceId);
|
|
1663
1651
|
}
|
|
1664
1652
|
else {
|
|
1665
1653
|
// Upload verification transcript.
|
|
@@ -1720,6 +1708,18 @@ const verifycontribution = functionsV2.https.onCall({ memory: "16GiB", timeoutSe
|
|
|
1720
1708
|
lastUpdated: getCurrentServerTimestampInMillis()
|
|
1721
1709
|
});
|
|
1722
1710
|
}
|
|
1711
|
+
// Stop VM instance
|
|
1712
|
+
if (isUsingVM) {
|
|
1713
|
+
// using try and catch as the VM stopping function can throw
|
|
1714
|
+
// however we want to continue without stopping as the
|
|
1715
|
+
// verification was valid, and inform the coordinator
|
|
1716
|
+
try {
|
|
1717
|
+
await stopEC2Instance(ec2, vmInstanceId);
|
|
1718
|
+
}
|
|
1719
|
+
catch (error) {
|
|
1720
|
+
printLog(`Error while stopping VM instance ${vmInstanceId} - Error ${error}`, LogLevel.WARN);
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
1723
|
// Step (1.A.4.C)
|
|
1724
1724
|
if (!isFinalizing) {
|
|
1725
1725
|
// Step (1.A.4.C.1)
|
|
@@ -1735,6 +1735,8 @@ const verifycontribution = functionsV2.https.onCall({ memory: "16GiB", timeoutSe
|
|
|
1735
1735
|
? (avgVerifyCloudFunctionTime + verifyCloudFunctionTime) / 2
|
|
1736
1736
|
: verifyCloudFunctionTime;
|
|
1737
1737
|
// Prepare tx to update circuit average contribution/verification time.
|
|
1738
|
+
const updatedCircuitDoc = await getDocumentById(getCircuitsCollectionPath(ceremonyId), circuitId);
|
|
1739
|
+
const { waitingQueue: updatedWaitingQueue } = updatedCircuitDoc.data();
|
|
1738
1740
|
/// @dev this must happen only for valid contributions.
|
|
1739
1741
|
batch.update(circuitDoc.ref, {
|
|
1740
1742
|
avgTimings: {
|
|
@@ -1747,7 +1749,7 @@ const verifycontribution = functionsV2.https.onCall({ memory: "16GiB", timeoutSe
|
|
|
1747
1749
|
: avgVerifyCloudFunctionTime
|
|
1748
1750
|
},
|
|
1749
1751
|
waitingQueue: {
|
|
1750
|
-
...
|
|
1752
|
+
...updatedWaitingQueue,
|
|
1751
1753
|
completedContributions: isContributionValid
|
|
1752
1754
|
? completedContributions + 1
|
|
1753
1755
|
: completedContributions,
|
|
@@ -1782,7 +1784,7 @@ const verifycontribution = functionsV2.https.onCall({ memory: "16GiB", timeoutSe
|
|
|
1782
1784
|
commandId = await runCommandUsingSSM(ssm, vmInstanceId, verificationCommand);
|
|
1783
1785
|
printLog(`Starting the execution of command ${commandId}`, LogLevel.DEBUG);
|
|
1784
1786
|
// Step (1.A.3.3).
|
|
1785
|
-
return
|
|
1787
|
+
return waitForVMCommandExecution(ssm, vmInstanceId, commandId)
|
|
1786
1788
|
.then(async () => {
|
|
1787
1789
|
// Command execution successfully completed.
|
|
1788
1790
|
printLog(`Command ${commandId} execution has been successfully completed`, LogLevel.DEBUG);
|
|
@@ -1794,52 +1796,38 @@ const verifycontribution = functionsV2.https.onCall({ memory: "16GiB", timeoutSe
|
|
|
1794
1796
|
logAndThrowError(COMMON_ERRORS.CM_INVALID_COMMAND_EXECUTION);
|
|
1795
1797
|
});
|
|
1796
1798
|
}
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
fs.unlinkSync(potTempFilePath);
|
|
1828
|
-
fs.unlinkSync(firstZkeyTempFilePath);
|
|
1829
|
-
fs.unlinkSync(lastZkeyTempFilePath);
|
|
1830
|
-
}
|
|
1831
|
-
catch (error) {
|
|
1832
|
-
printLog(`Error while unlinking temporary files - Error ${error}`, LogLevel.WARN);
|
|
1833
|
-
}
|
|
1834
|
-
await completeVerification();
|
|
1835
|
-
})
|
|
1836
|
-
.catch((error) => {
|
|
1837
|
-
// Throw the new error
|
|
1838
|
-
const commonError = COMMON_ERRORS.CM_INVALID_REQUEST;
|
|
1839
|
-
const additionalDetails = error.toString();
|
|
1840
|
-
logAndThrowError(makeError(commonError.code, commonError.message, additionalDetails));
|
|
1841
|
-
});
|
|
1799
|
+
// CF approach.
|
|
1800
|
+
printLog(`CF mechanism`, LogLevel.DEBUG);
|
|
1801
|
+
const potStoragePath = getPotStorageFilePath(files.potFilename);
|
|
1802
|
+
const firstZkeyStoragePath = getZkeyStorageFilePath(prefix, `${prefix}_${genesisZkeyIndex}.zkey`);
|
|
1803
|
+
// Prepare temporary file paths.
|
|
1804
|
+
// (nb. these are needed to download the necessary artifacts for verification from AWS S3).
|
|
1805
|
+
verificationTranscriptTemporaryLocalPath = createTemporaryLocalPath(verificationTranscriptCompleteFilename);
|
|
1806
|
+
const potTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}.pot`);
|
|
1807
|
+
const firstZkeyTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}_genesis.zkey`);
|
|
1808
|
+
const lastZkeyTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}_last.zkey`);
|
|
1809
|
+
// Create and populate transcript.
|
|
1810
|
+
const transcriptLogger = createCustomLoggerForFile(verificationTranscriptTemporaryLocalPath);
|
|
1811
|
+
transcriptLogger.info(`${isFinalizing ? `Final verification` : `Verification`} transcript for ${prefix} circuit Phase 2 contribution.\n${isFinalizing ? `Coordinator ` : `Contributor # ${Number(lastZkeyIndex)}`} (${contributorOrCoordinatorIdentifier})\n`);
|
|
1812
|
+
// Step (1.A.2).
|
|
1813
|
+
await downloadArtifactFromS3Bucket(bucketName, potStoragePath, potTempFilePath);
|
|
1814
|
+
await downloadArtifactFromS3Bucket(bucketName, firstZkeyStoragePath, firstZkeyTempFilePath);
|
|
1815
|
+
await downloadArtifactFromS3Bucket(bucketName, lastZkeyStoragePath, lastZkeyTempFilePath);
|
|
1816
|
+
// Step (1.A.4).
|
|
1817
|
+
isContributionValid = await zKey.verifyFromInit(firstZkeyTempFilePath, potTempFilePath, lastZkeyTempFilePath, transcriptLogger);
|
|
1818
|
+
// Compute contribution hash.
|
|
1819
|
+
lastZkeyBlake2bHash = await blake512FromPath(lastZkeyTempFilePath);
|
|
1820
|
+
// Free resources by unlinking temporary folders.
|
|
1821
|
+
// Do not free-up verification transcript path here.
|
|
1822
|
+
try {
|
|
1823
|
+
fs.unlinkSync(potTempFilePath);
|
|
1824
|
+
fs.unlinkSync(firstZkeyTempFilePath);
|
|
1825
|
+
fs.unlinkSync(lastZkeyTempFilePath);
|
|
1826
|
+
}
|
|
1827
|
+
catch (error) {
|
|
1828
|
+
printLog(`Error while unlinking temporary files - Error ${error}`, LogLevel.WARN);
|
|
1842
1829
|
}
|
|
1830
|
+
await completeVerification();
|
|
1843
1831
|
}
|
|
1844
1832
|
});
|
|
1845
1833
|
/**
|
|
@@ -1848,7 +1836,7 @@ const verifycontribution = functionsV2.https.onCall({ memory: "16GiB", timeoutSe
|
|
|
1848
1836
|
* this does not happen if the participant is actually the coordinator who is finalizing the ceremony.
|
|
1849
1837
|
*/
|
|
1850
1838
|
const refreshParticipantAfterContributionVerification = functionsV1
|
|
1851
|
-
.region(
|
|
1839
|
+
.region("europe-west1")
|
|
1852
1840
|
.runWith({
|
|
1853
1841
|
memory: "512MB"
|
|
1854
1842
|
})
|
|
@@ -1909,7 +1897,7 @@ const refreshParticipantAfterContributionVerification = functionsV1
|
|
|
1909
1897
|
* and verification key extracted from the circuit final contribution (as part of the ceremony finalization process).
|
|
1910
1898
|
*/
|
|
1911
1899
|
const finalizeCircuit = functionsV1
|
|
1912
|
-
.region(
|
|
1900
|
+
.region("europe-west1")
|
|
1913
1901
|
.runWith({
|
|
1914
1902
|
memory: "512MB"
|
|
1915
1903
|
})
|
|
@@ -2106,8 +2094,10 @@ const createBucket = functions
|
|
|
2106
2094
|
CORSConfiguration: {
|
|
2107
2095
|
CORSRules: [
|
|
2108
2096
|
{
|
|
2109
|
-
AllowedMethods: ["GET"],
|
|
2110
|
-
AllowedOrigins: ["*"]
|
|
2097
|
+
AllowedMethods: ["GET", "PUT"],
|
|
2098
|
+
AllowedOrigins: ["*"],
|
|
2099
|
+
ExposeHeaders: ["ETag", "Content-Length"],
|
|
2100
|
+
AllowedHeaders: ["*"]
|
|
2111
2101
|
}
|
|
2112
2102
|
]
|
|
2113
2103
|
}
|
|
@@ -2284,7 +2274,8 @@ const startMultiPartUpload = functions
|
|
|
2284
2274
|
const generatePreSignedUrlsParts = functions
|
|
2285
2275
|
.region("europe-west1")
|
|
2286
2276
|
.runWith({
|
|
2287
|
-
memory: "512MB"
|
|
2277
|
+
memory: "512MB",
|
|
2278
|
+
timeoutSeconds: 300
|
|
2288
2279
|
})
|
|
2289
2280
|
.https.onCall(async (data, context) => {
|
|
2290
2281
|
if (!context.auth || (!context.auth.token.participant && !context.auth.token.coordinator))
|
|
@@ -2434,7 +2425,7 @@ const checkAndRemoveBlockingContributor = functions
|
|
|
2434
2425
|
// Get ceremony circuits.
|
|
2435
2426
|
const circuits = await getCeremonyCircuits(ceremony.id);
|
|
2436
2427
|
// Extract ceremony data.
|
|
2437
|
-
const { timeoutMechanismType, penalty } = ceremony.data();
|
|
2428
|
+
const { timeoutType: timeoutMechanismType, penalty } = ceremony.data();
|
|
2438
2429
|
for (const circuit of circuits) {
|
|
2439
2430
|
if (!circuit.data())
|
|
2440
2431
|
// Do not use `logAndThrowError` method to avoid the function to exit before checking every ceremony.
|
|
@@ -2500,7 +2491,7 @@ const checkAndRemoveBlockingContributor = functions
|
|
|
2500
2491
|
// Prepare Firestore batch of txs.
|
|
2501
2492
|
const batch = firestore.batch();
|
|
2502
2493
|
// Remove current contributor from waiting queue.
|
|
2503
|
-
contributors.shift(
|
|
2494
|
+
contributors.shift();
|
|
2504
2495
|
// Check if someone else is ready to start the contribution.
|
|
2505
2496
|
if (contributors.length > 0) {
|
|
2506
2497
|
// Step (E.1).
|
|
@@ -2584,7 +2575,8 @@ const resumeContributionAfterTimeoutExpiration = functions
|
|
|
2584
2575
|
if (status === "EXHUMED" /* ParticipantStatus.EXHUMED */)
|
|
2585
2576
|
await participantDoc.ref.update({
|
|
2586
2577
|
status: "READY" /* ParticipantStatus.READY */,
|
|
2587
|
-
lastUpdated: getCurrentServerTimestampInMillis()
|
|
2578
|
+
lastUpdated: getCurrentServerTimestampInMillis(),
|
|
2579
|
+
tempContributionData: {}
|
|
2588
2580
|
});
|
|
2589
2581
|
else
|
|
2590
2582
|
logAndThrowError(SPECIFIC_ERRORS.SE_CONTRIBUTE_CANNOT_PROGRESS_TO_NEXT_CIRCUIT);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ceremony.d.ts","sourceRoot":"","sources":["../../../src/functions/ceremony.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,oBAAoB,CAAA;AAuC/C;;;;;GAKG;AACH,eAAO,MAAM,aAAa,kCAiBpB,CAAA;AAEN;;;;;GAKG;AACH,eAAO,MAAM,YAAY,kCAkBnB,CAAA;AAEN;;;;GAIG;AACH,eAAO,MAAM,aAAa,
|
|
1
|
+
{"version":3,"file":"ceremony.d.ts","sourceRoot":"","sources":["../../../src/functions/ceremony.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,oBAAoB,CAAA;AAuC/C;;;;;GAKG;AACH,eAAO,MAAM,aAAa,kCAiBpB,CAAA;AAEN;;;;;GAKG;AACH,eAAO,MAAM,YAAY,kCAkBnB,CAAA;AAEN;;;;GAIG;AACH,eAAO,MAAM,aAAa,mDA8HpB,CAAA;AAEN;;;GAGG;AACH,eAAO,MAAM,+BAA+B,oEAsCtC,CAAA;AAEN;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,mDAiEvB,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"circuit.d.ts","sourceRoot":"","sources":["../../../src/functions/circuit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,WAAW,MAAM,uBAAuB,CAAA;AACpD,OAAO,KAAK,WAAW,MAAM,uBAAuB,CAAA;
|
|
1
|
+
{"version":3,"file":"circuit.d.ts","sourceRoot":"","sources":["../../../src/functions/circuit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,WAAW,MAAM,uBAAuB,CAAA;AACpD,OAAO,KAAK,WAAW,MAAM,uBAAuB,CAAA;AA2CpD,OAAO,EAAuB,sBAAsB,EAAE,MAAM,gBAAgB,CAAA;AAwP5E;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,6BAA6B,4FAoGpC,CAAA;AAwBN;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,kBAAkB,0EA8Z9B,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,+CAA+C,wEA4EtD,CAAA;AAEN;;;;GAIG;AACH,eAAO,MAAM,eAAe,uDA8EtB,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../../src/functions/storage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,oBAAoB,CAAA;AAiI/C;;;GAGG;AACH,eAAO,MAAM,YAAY,
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../../src/functions/storage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,oBAAoB,CAAA;AAiI/C;;;GAGG;AACH,eAAO,MAAM,YAAY,mDAkGnB,CAAA;AAEN;;;GAGG;AACH,eAAO,MAAM,kBAAkB,mDAgDzB,CAAA;AAEN;;;;;GAKG;AACH,eAAO,MAAM,6BAA6B,mDAyCpC,CAAA;AAEN;;;GAGG;AACH,eAAO,MAAM,oBAAoB,mDA2D3B,CAAA;AAEN;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,mDAwElC,CAAA;AAEL;;;GAGG;AACH,eAAO,MAAM,uBAAuB,mDAgE9B,CAAA"}
|