@devtion/devcli 0.0.0-a6dcd68 → 0.0.0-b499eaf

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/dist/index.js CHANGED
@@ -2,14 +2,14 @@
2
2
 
3
3
  /**
4
4
  * @module @p0tion/phase2cli
5
- * @version 1.0.5
5
+ * @version 1.1.1
6
6
  * @file All-in-one interactive command-line for interfacing with zkSNARK Phase 2 Trusted Setup ceremonies
7
7
  * @copyright Ethereum Foundation 2022
8
8
  * @license MIT
9
9
  * @see [Github]{@link https://github.com/privacy-scaling-explorations/p0tion}
10
10
  */
11
11
  import { createCommand } from 'commander';
12
- import fs, { readFileSync, createWriteStream, renameSync } from 'fs';
12
+ import fs, { readFileSync, createWriteStream, existsSync, renameSync } from 'fs';
13
13
  import { dirname } from 'path';
14
14
  import { fileURLToPath } from 'url';
15
15
  import { zKey } from 'snarkjs';
@@ -17,8 +17,7 @@ import boxen from 'boxen';
17
17
  import { pipeline } from 'node:stream';
18
18
  import { promisify } from 'node:util';
19
19
  import fetch$1 from 'node-fetch';
20
- import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3';
21
- import { commonTerms, formatZkeyIndex, getZkeyStorageFilePath, finalContributionIndex, createCustomLoggerForFile, getBucketName, progressToNextContributionStep, permanentlyStoreCurrentContributionTimeAndHash, convertToDoubleDigits, multiPartUpload, verifyContribution, generateGetObjectPreSignedUrl, convertBytesOrKbToGb, numExpIterations, getDocumentById, getParticipantsCollectionPath, fromQueryToFirebaseDocumentInfo, getAllCollectionDocs, extractPrefix, autoGenerateEntropy, vmConfigurationTypes, initializeFirebaseCoreServices, signInToFirebaseWithCredentials, getCurrentFirebaseAuthUser, isCoordinator, parseCeremonyFile, blake512FromPath, checkIfObjectExist, setupCeremony, genesisZkeyIndex, getR1csStorageFilePath, getWasmStorageFilePath, getPotStorageFilePath, extractPoTFromFilename, potFileDownloadMainUrl, createS3Bucket, potFilenameTemplate, getR1CSInfo, getOpenedCeremonies, getCeremonyCircuits, checkParticipantForCeremony, getCurrentActiveParticipantTimeout, getCircuitBySequencePosition, getCircuitContributionsFromContributor, progressToNextCircuitForContribution, resumeContributionAfterTimeoutExpiration, generateValidContributionsAttestation, getContributionsValidityForContributor, getClosedCeremonies, checkAndPrepareCoordinatorForFinalization, computeSHA256ToHex, finalizeCeremony, getVerificationKeyStorageFilePath, verificationKeyAcronym, getVerifierContractStorageFilePath, verifierSmartContractAcronym, finalizeCircuit, exportVkey, exportVerifierContract } from '@p0tion/actions';
20
+ import { commonTerms, formatZkeyIndex, getZkeyStorageFilePath, finalContributionIndex, createCustomLoggerForFile, getBucketName, progressToNextContributionStep, permanentlyStoreCurrentContributionTimeAndHash, convertToDoubleDigits, multiPartUpload, verifyContribution, generateGetObjectPreSignedUrl, convertBytesOrKbToGb, numExpIterations, getDocumentById, getParticipantsCollectionPath, fromQueryToFirebaseDocumentInfo, getAllCollectionDocs, extractPrefix, autoGenerateEntropy, vmConfigurationTypes, initializeFirebaseCoreServices, signInToFirebaseWithCredentials, getCurrentFirebaseAuthUser, isCoordinator, parseCeremonyFile, blake512FromPath, checkIfObjectExist, setupCeremony, genesisZkeyIndex, getR1csStorageFilePath, getWasmStorageFilePath, getPotStorageFilePath, extractPoTFromFilename, potFileDownloadMainUrl, createS3Bucket, potFilenameTemplate, getR1CSInfo, getOpenedCeremonies, getCeremonyCircuits, checkParticipantForCeremony, getCurrentActiveParticipantTimeout, getCircuitBySequencePosition, getCircuitContributionsFromContributor, progressToNextCircuitForContribution, resumeContributionAfterTimeoutExpiration, generateValidContributionsAttestation, getContributionsValidityForContributor, getClosedCeremonies, checkAndPrepareCoordinatorForFinalization, computeSHA256ToHex, finalizeCeremony, getVerificationKeyStorageFilePath, verificationKeyAcronym, getVerifierContractStorageFilePath, verifierSmartContractAcronym, finalizeCircuit, exportVkey, exportVerifierContract } from '@devtion/actions';
22
21
  import fetch from '@adobe/node-fetch-retry';
23
22
  import { request } from '@octokit/request';
24
23
  import { SingleBar, Presets } from 'cli-progress';
@@ -34,7 +33,6 @@ import Conf from 'conf';
34
33
  import prompts from 'prompts';
35
34
  import clear from 'clear';
36
35
  import figlet from 'figlet';
37
- import { Readable } from 'stream';
38
36
  import { createOAuthDeviceAuth } from '@octokit/auth-oauth-device';
39
37
  import clipboard from 'clipboardy';
40
38
  import open from 'open';
@@ -367,6 +365,12 @@ const getVerificationKeyLocalFilePath = (completeFilename) => `${verificationKey
367
365
  * @returns <string> - the complete final verifier contract path to the file.
368
366
  */
369
367
  const getVerifierContractLocalFilePath = (completeFilename) => `${verifierContractsLocalFolderPath}/${completeFilename}`;
368
+ /**
369
+ * Get the complete final attestation file path.
370
+ * @param completeFilename <string> - the complete filename of the file (name.ext).
371
+ * @returns <string> - the complete final final attestation path to the file.
372
+ */
373
+ const getFinalAttestationLocalFilePath = (completeFilename) => `${finalAttestationsLocalFolderPath}/${completeFilename}`;
370
374
  /**
371
375
  * Get the final transcript file path.
372
376
  * @param completeFilename <string> - the complete filename of the file (name.ext).
@@ -581,8 +585,18 @@ const publishGist = async (token, content, ceremonyTitle, ceremonyPrefix) => {
581
585
  * @returns <string> - the ready to share tweet url.
582
586
  */
583
587
  const generateCustomUrlToTweetAboutParticipation = (ceremonyName, gistUrl, isFinalizing) => isFinalizing
584
- ? `https://twitter.com/intent/tweet?text=I%20have%20finalized%20the%20${ceremonyName}%20Phase%202%20Trusted%20Setup%20ceremony!%20You%20can%20view%20my%20final%20attestation%20here:%20${gistUrl}%20#Ethereum%20#ZKP%20#PSE`
585
- : `https://twitter.com/intent/tweet?text=I%20contributed%20to%20the%20${ceremonyName}%20Phase%202%20Trusted%20Setup%20ceremony!%20You%20can%20contribute%20here:%20https://github.com/privacy-scaling-explorations/p0tion%20You%20can%20view%20my%20attestation%20here:%20${gistUrl}%20#Ethereum%20#ZKP`;
588
+ ? `https://twitter.com/intent/tweet?text=I%20have%20finalized%20the%20${ceremonyName}${ceremonyName.toLowerCase().includes("trusted") ||
589
+ ceremonyName.toLowerCase().includes("setup") ||
590
+ ceremonyName.toLowerCase().includes("phase2") ||
591
+ ceremonyName.toLowerCase().includes("ceremony")
592
+ ? "!"
593
+ : "%20Phase%202%20Trusted%20Setup%20ceremony!"}%20You%20can%20view%20my%20final%20attestation%20here:%20${gistUrl}%20#Ethereum%20#ZKP%20#PSE`
594
+ : `https://twitter.com/intent/tweet?text=I%20contributed%20to%20the%20${ceremonyName}${ceremonyName.toLowerCase().includes("trusted") ||
595
+ ceremonyName.toLowerCase().includes("setup") ||
596
+ ceremonyName.toLowerCase().includes("phase2") ||
597
+ ceremonyName.toLowerCase().includes("ceremony")
598
+ ? "!"
599
+ : "%20Phase%202%20Trusted%20Setup%20ceremony!"}%20You%20can%20view%20the%20steps%20to%20contribute%20here:%20https://ceremony.pse.dev%20You%20can%20view%20my%20attestation%20here:%20${gistUrl}%20#Ethereum%20#ZKP`;
586
600
  /**
587
601
  * Return a custom progress bar.
588
602
  * @param type <ProgressBarType> - the type of the progress bar.
@@ -710,13 +724,14 @@ const getLatestUpdatesFromParticipant = async (firestoreDatabase, ceremonyId, pa
710
724
  * @param entropyOrBeaconHash <string> - the entropy or beacon hash (only when finalizing) for the contribution.
711
725
  * @param contributorOrCoordinatorIdentifier <string> - the identifier of the contributor or coordinator (only when finalizing).
712
726
  * @param isFinalizing <boolean> - flag to discriminate between ceremony finalization (true) and contribution (false).
727
+ * @param circuitsLength <number> - the total number of circuits in the ceremony.
713
728
  */
714
- const handleStartOrResumeContribution = async (cloudFunctions, firestoreDatabase, ceremony, circuit, participant, entropyOrBeaconHash, contributorOrCoordinatorIdentifier, isFinalizing) => {
729
+ const handleStartOrResumeContribution = async (cloudFunctions, firestoreDatabase, ceremony, circuit, participant, entropyOrBeaconHash, contributorOrCoordinatorIdentifier, isFinalizing, circuitsLength) => {
715
730
  // Extract data.
716
731
  const { prefix: ceremonyPrefix } = ceremony.data;
717
732
  const { waitingQueue, avgTimings, prefix: circuitPrefix, sequencePosition } = circuit.data;
718
733
  const { completedContributions } = waitingQueue; // = current progress.
719
- console.log(`${theme.text.bold(`\n- Circuit # ${theme.colors.magenta(`${sequencePosition}`)}`)} (Contribution Steps)`);
734
+ console.log(`${theme.text.bold(`\n- Circuit # ${theme.colors.magenta(`${sequencePosition}/${circuitsLength}`)}`)} (Contribution Steps)`);
720
735
  // Get most up-to-date data from the participant document.
721
736
  let participantData = await getLatestUpdatesFromParticipant(firestoreDatabase, ceremony.id, participant.id);
722
737
  const spinner = customSpinner(`${participantData.contributionStep === "DOWNLOADING" /* ParticipantContributionStep.DOWNLOADING */
@@ -762,6 +777,7 @@ const handleStartOrResumeContribution = async (cloudFunctions, firestoreDatabase
762
777
  // Download the latest contribution from bucket.
763
778
  await downloadCeremonyArtifact(cloudFunctions, bucketName, lastZkeyStorageFilePath, lastZkeyLocalFilePath);
764
779
  console.log(`${theme.symbols.success} Contribution ${theme.text.bold(`#${lastZkeyIndex}`)} correctly downloaded`);
780
+ await sleep(3000);
765
781
  // Advance to next contribution step (COMPUTING) if not finalizing.
766
782
  if (!isFinalizing) {
767
783
  spinner.text = `Preparing for contribution computation...`;
@@ -789,11 +805,14 @@ const handleStartOrResumeContribution = async (cloudFunctions, firestoreDatabase
789
805
  showError(COMMAND_ERRORS.COMMAND_CONTRIBUTE_FINALIZE_NO_TRANSCRIPT_CONTRIBUTION_HASH_MATCH, true);
790
806
  // Format contribution hash.
791
807
  const contributionHash = matchContributionHash?.at(0)?.replace("\n\t\t", "");
808
+ await sleep(500);
792
809
  // Make request to cloud functions to permanently store the information.
793
810
  await permanentlyStoreCurrentContributionTimeAndHash(cloudFunctions, ceremony.id, computingTime, contributionHash);
794
811
  // Format computing time.
795
812
  const { seconds: computationSeconds, minutes: computationMinutes, hours: computationHours } = getSecondsMinutesHoursFromMillis(computingTime);
796
813
  spinner.succeed(`${isFinalizing ? "Contribution" : `Contribution ${theme.text.bold(`#${nextZkeyIndex}`)}`} computation took ${theme.text.bold(`${convertToDoubleDigits(computationHours)}:${convertToDoubleDigits(computationMinutes)}:${convertToDoubleDigits(computationSeconds)}`)}`);
814
+ // ensure the previous step is completed
815
+ await sleep(5000);
797
816
  // Advance to next contribution step (UPLOADING) if not finalizing.
798
817
  if (!isFinalizing) {
799
818
  spinner.text = `Preparing for uploading the contribution...`;
@@ -809,12 +828,17 @@ const handleStartOrResumeContribution = async (cloudFunctions, firestoreDatabase
809
828
  console.log(`${theme.symbols.success} Contribution ${theme.text.bold(`#${nextZkeyIndex}`)} already computed`);
810
829
  // Contribution step = UPLOADING.
811
830
  if (isFinalizing || participantData.contributionStep === "UPLOADING" /* ParticipantContributionStep.UPLOADING */) {
812
- spinner.text = `Uploading ${isFinalizing ? "final" : "your"} contribution ${!isFinalizing ? theme.text.bold(`#${nextZkeyIndex}`) : ""} to storage.\n${theme.symbols.warning} This step may take a while based on circuit size and your contribution speed. Everything's fine, just be patient.`;
831
+ spinner.text = `Uploading ${isFinalizing ? "final" : "your"} contribution ${!isFinalizing ? theme.text.bold(`#${nextZkeyIndex}`) : ""} to storage.\n${theme.symbols.warning} This step may take a while based on circuit size and your internet speed. Everything's fine, just be patient.`;
813
832
  spinner.start();
814
- if (!isFinalizing)
815
- await multiPartUpload(cloudFunctions, bucketName, nextZkeyStorageFilePath, nextZkeyLocalFilePath, Number(process.env.CONFIG_STREAM_CHUNK_SIZE_IN_MB), ceremony.id, participantData.tempContributionData);
833
+ const progressBar = customProgressBar(ProgressBarType.UPLOAD, `your contribution`);
834
+ if (!isFinalizing) {
835
+ await multiPartUpload(cloudFunctions, bucketName, nextZkeyStorageFilePath, nextZkeyLocalFilePath, Number(process.env.CONFIG_STREAM_CHUNK_SIZE_IN_MB), ceremony.id, participantData.tempContributionData, progressBar);
836
+ progressBar.stop();
837
+ }
816
838
  else
817
839
  await multiPartUpload(cloudFunctions, bucketName, nextZkeyStorageFilePath, nextZkeyLocalFilePath, Number(process.env.CONFIG_STREAM_CHUNK_SIZE_IN_MB));
840
+ // small sleep to ensure the previous step is completed
841
+ await sleep(5000);
818
842
  spinner.succeed(`${isFinalizing ? `Contribution` : `Contribution ${theme.text.bold(`#${nextZkeyIndex}`)}`} correctly saved to storage`);
819
843
  // Advance to next contribution step (VERIFYING) if not finalizing.
820
844
  if (!isFinalizing) {
@@ -992,7 +1016,7 @@ const promptCircomCompiler = async () => {
992
1016
  * Shows a list of circuits for a single option selection.
993
1017
  * @dev the circuit names are derived from local R1CS files.
994
1018
  * @param options <Array<string>> - an array of circuits names.
995
- * @returns Promise<string> - the name of the choosen circuit.
1019
+ * @returns Promise<string> - the name of the chosen circuit.
996
1020
  */
997
1021
  const promptCircuitSelector = async (options) => {
998
1022
  const { circuitFilename } = await prompts({
@@ -1010,7 +1034,7 @@ const promptCircuitSelector = async (options) => {
1010
1034
  * Shows a list of standard EC2 VM instance types for a single option selection.
1011
1035
  * @notice the suggested VM configuration type is calculated based on circuit constraint size.
1012
1036
  * @param constraintSize <number> - the amount of circuit constraints
1013
- * @returns Promise<string> - the name of the choosen VM type.
1037
+ * @returns Promise<string> - the name of the chosen VM type.
1014
1038
  */
1015
1039
  const promptVMTypeSelector = async (constraintSize) => {
1016
1040
  let suggestedConfiguration = 0;
@@ -1107,7 +1131,7 @@ const promptVMDiskTypeSelector = async () => {
1107
1131
  /**
1108
1132
  * Show a series of questions about the circuits.
1109
1133
  * @param constraintSize <number> - the amount of circuit constraints.
1110
- * @param timeoutMechanismType <CeremonyTimeoutType> - the choosen timeout mechanism type for the ceremony.
1134
+ * @param timeoutMechanismType <CeremonyTimeoutType> - the chosen timeout mechanism type for the ceremony.
1111
1135
  * @param needPromptCircomCompiler <boolean> - a boolean value indicating if the questions related to the Circom compiler version and commit hash must be asked.
1112
1136
  * @param enforceVM <boolean> - a boolean value indicating if the contribution verification could be supported by VM-only approach or not.
1113
1137
  * @returns Promise<Array<Circuit>> - circuit info prompted by the coordinator.
@@ -1120,7 +1144,7 @@ const promptCircuitInputData = async (constraintSize, timeoutMechanismType, same
1120
1144
  let circomVersion = "";
1121
1145
  let circomCommitHash = "";
1122
1146
  let circuitInputData;
1123
- let useCfOrVm;
1147
+ let cfOrVm;
1124
1148
  let vmDiskType;
1125
1149
  let vmConfigurationType = "";
1126
1150
  const questions = [
@@ -1175,18 +1199,21 @@ const promptCircuitInputData = async (constraintSize, timeoutMechanismType, same
1175
1199
  circomVersion = version;
1176
1200
  circomCommitHash = commitHash;
1177
1201
  }
1178
- // Ask for prefered contribution verification method (CF vs VM).
1202
+ // Ask for preferred contribution verification method (CF vs VM).
1179
1203
  if (!enforceVM) {
1180
1204
  const { confirmation } = await askForConfirmation(`The contribution verification can be performed using Cloud Functions (CF, cheaper for small contributions but limited to 1M constraints) or custom virtual machines (expensive but could scale up to 30M constraints). Be aware about VM costs and if you wanna learn more, please visit the documentation to have a complete overview about cost estimation of the two mechanisms.\nChoose the contribution verification mechanism`, `CF`, // eq. true.
1181
1205
  `VM` // eq. false.
1182
1206
  );
1183
- useCfOrVm = confirmation;
1207
+ cfOrVm = confirmation
1208
+ ? "CF" /* CircuitContributionVerificationMechanism.CF */
1209
+ : "VM" /* CircuitContributionVerificationMechanism.VM */;
1184
1210
  }
1185
- else
1186
- useCfOrVm = "VM" /* CircuitContributionVerificationMechanism.VM */;
1187
- if (useCfOrVm === undefined)
1211
+ else {
1212
+ cfOrVm = "VM" /* CircuitContributionVerificationMechanism.VM */;
1213
+ }
1214
+ if (cfOrVm === undefined)
1188
1215
  showError(COMMAND_ERRORS.COMMAND_ABORT_PROMPT, true);
1189
- if (!useCfOrVm) {
1216
+ if (cfOrVm === "VM" /* CircuitContributionVerificationMechanism.VM */) {
1190
1217
  // Ask for selecting the specific VM configuration type.
1191
1218
  vmConfigurationType = await promptVMTypeSelector(constraintSize);
1192
1219
  // Ask for selecting the specific VM disk (volume) type.
@@ -1220,9 +1247,7 @@ const promptCircuitInputData = async (constraintSize, timeoutMechanismType, same
1220
1247
  paramsConfiguration: circuitConfigurationValues
1221
1248
  },
1222
1249
  verification: {
1223
- cfOrVm: useCfOrVm
1224
- ? "CF" /* CircuitContributionVerificationMechanism.CF */
1225
- : "VM" /* CircuitContributionVerificationMechanism.VM */,
1250
+ cfOrVm,
1226
1251
  vm: {
1227
1252
  vmConfigurationType,
1228
1253
  vmDiskType
@@ -1258,9 +1283,7 @@ const promptCircuitInputData = async (constraintSize, timeoutMechanismType, same
1258
1283
  paramsConfiguration: circuitConfigurationValues
1259
1284
  },
1260
1285
  verification: {
1261
- cfOrVm: useCfOrVm
1262
- ? "CF" /* CircuitContributionVerificationMechanism.CF */
1263
- : "VM" /* CircuitContributionVerificationMechanism.VM */,
1286
+ cfOrVm,
1264
1287
  vm: {
1265
1288
  vmConfigurationType,
1266
1289
  vmDiskType
@@ -1304,7 +1327,7 @@ const promptCircuitAddition = async () => {
1304
1327
  * Shows a list of pre-computed zKeys for a single option selection.
1305
1328
  * @dev the names are derived from local zKeys files.
1306
1329
  * @param options <Array<string>> - an array of pre-computed zKeys names.
1307
- * @returns Promise<string> - the name of the choosen pre-computed zKey.
1330
+ * @returns Promise<string> - the name of the chosen pre-computed zKey.
1308
1331
  */
1309
1332
  const promptPreComputedZkeySelector = async (options) => {
1310
1333
  const { preComputedZkeyFilename } = await prompts({
@@ -1342,13 +1365,13 @@ const promptNeededPowersForCircuit = async (suggestedSmallestNeededPowers) => {
1342
1365
  * Shows a list of PoT files for a single option selection.
1343
1366
  * @dev the names are derived from local PoT files.
1344
1367
  * @param options <Array<string>> - an array of PoT file names.
1345
- * @returns Promise<string> - the name of the choosen PoT.
1368
+ * @returns Promise<string> - the name of the chosen PoT.
1346
1369
  */
1347
1370
  const promptPotSelector = async (options) => {
1348
1371
  const { potFilename } = await prompts({
1349
1372
  type: "select",
1350
1373
  name: "potFilename",
1351
- message: theme.text.bold("Select the Powers of Tau file choosen for the circuit"),
1374
+ message: theme.text.bold("Select the Powers of Tau file chosen for the circuit"),
1352
1375
  choices: options.map((option) => {
1353
1376
  console.log(option);
1354
1377
  return { title: option, value: option };
@@ -1418,7 +1441,7 @@ const promptToTypeEntropyOrBeacon = async (isEntropy = true) => {
1418
1441
  * @return <Promise<string>> - the entropy.
1419
1442
  */
1420
1443
  const promptForEntropy = async () => {
1421
- // Prompt for entropy generation prefered method.
1444
+ // Prompt for entropy generation preferred method.
1422
1445
  const { confirmation } = await askForConfirmation(`Do you prefer to type your entropy or generate it randomly?`, "Manually", "Randomly");
1423
1446
  if (confirmation === undefined)
1424
1447
  showError(COMMAND_ERRORS.COMMAND_ABORT_PROMPT, true);
@@ -1635,7 +1658,7 @@ const handleAdditionOfCircuitsToCeremony = async (r1csOptions, wasmOptions, cere
1635
1658
  wasmFilename.split(`.${commonTerms.foldersAndPathsTerms.wasm}`)[0]);
1636
1659
  if (matchingWasms.length !== 1)
1637
1660
  showError(COMMAND_ERRORS.COMMAND_SETUP_MISMATCH_R1CS_WASM, true);
1638
- // Get input data for choosen circuit.
1661
+ // Get input data for chosen circuit.
1639
1662
  const circuitInputData = await getInputDataToAddCircuitToCeremony(choosenCircuitFilename, matchingWasms[0], ceremonyTimeoutMechanismType, sameCircomCompiler, circuitSequencePosition, sharedCircomCompilerData);
1640
1663
  // Store circuit data.
1641
1664
  inputDataForCircuits.push(circuitInputData);
@@ -1725,7 +1748,7 @@ const checkAndDownloadSmallestPowersOfTau = async (powers, ptauCompleteFilename)
1725
1748
  * number of powers greater than or equal to the powers needed by the zKey), the coordinator will be asked
1726
1749
  * to provide a number of powers manually, ranging from the smallest possible to the largest.
1727
1750
  * @param neededPowers <number> - the smallest amount of powers needed by the zKey.
1728
- * @returns Promise<string, string> - the information about the choosen Powers of Tau file for the pre-computed zKey
1751
+ * @returns Promise<string, string> - the information about the chosen Powers of Tau file for the pre-computed zKey
1729
1752
  * along with related powers.
1730
1753
  */
1731
1754
  const handlePreComputedZkeyPowersOfTauSelection = async (neededPowers) => {
@@ -1826,7 +1849,9 @@ const setup = async (cmd) => {
1826
1849
  let ceremonyId = ""; // The unique identifier of the ceremony.
1827
1850
  const { firebaseApp, firebaseFunctions, firestoreDatabase } = await bootstrapCommandExecutionAndServices();
1828
1851
  // Check for authentication.
1829
- const { user, providerUserId } = cmd.auth ? await authWithToken(firebaseApp, cmd.auth) : await checkAuth(firebaseApp);
1852
+ const { user, providerUserId } = cmd.auth
1853
+ ? await authWithToken(firebaseApp, cmd.auth)
1854
+ : await checkAuth(firebaseApp);
1830
1855
  // Preserve command execution only for coordinators.
1831
1856
  if (!(await isCoordinator(user)))
1832
1857
  showError(COMMAND_ERRORS.COMMAND_NOT_COORDINATOR, true);
@@ -1843,7 +1868,7 @@ const setup = async (cmd) => {
1843
1868
  // if there is the file option, then set up the non interactively
1844
1869
  if (cmd.template) {
1845
1870
  // 1. parse the file
1846
- // tmp data - do not cleanup files as we need them
1871
+ // tmp data - do not cleanup files as we need them
1847
1872
  const spinner = customSpinner(`Parsing ${theme.text.bold(cmd.template)} setup configuration file...`, `clock`);
1848
1873
  spinner.start();
1849
1874
  const setupCeremonyData = await parseCeremonyFile(cmd.template);
@@ -1853,8 +1878,6 @@ const setup = async (cmd) => {
1853
1878
  // create a new bucket
1854
1879
  const bucketName = await handleCeremonyBucketCreation(firebaseFunctions, ceremonySetupData.ceremonyPrefix);
1855
1880
  console.log(`\n${theme.symbols.success} Ceremony bucket name: ${theme.text.bold(bucketName)}`);
1856
- // create S3 clienbt
1857
- const s3 = new S3Client({ region: 'us-east-1' });
1858
1881
  // loop through each circuit
1859
1882
  for await (const circuit of setupCeremonyData.circuits) {
1860
1883
  // Local paths.
@@ -1864,25 +1887,24 @@ const setup = async (cmd) => {
1864
1887
  const potLocalPathAndFileName = getPotLocalFilePath(circuit.files.potFilename);
1865
1888
  const zkeyLocalPathAndFileName = getZkeyLocalFilePath(circuit.files.initialZkeyFilename);
1866
1889
  // 2. download the pot and wasm files
1867
- const streamPipeline = promisify(pipeline);
1868
1890
  await checkAndDownloadSmallestPowersOfTau(convertToDoubleDigits(circuit.metadata?.pot), circuit.files.potFilename);
1869
- // download the wasm to calculate the hash
1870
- const spinner = customSpinner(`Downloading the ${theme.text.bold(`#${circuit.name}`)} WASM file from the project's bucket...`, `clock`);
1891
+ // 3. generate the zKey
1892
+ const spinner = customSpinner(`Generating genesis zKey for circuit ${theme.text.bold(circuit.name)}...`, `clock`);
1871
1893
  spinner.start();
1872
- const command = new GetObjectCommand({ Bucket: ceremonySetupData.circuitArtifacts[index].artifacts.bucket, Key: ceremonySetupData.circuitArtifacts[index].artifacts.wasmStoragePath });
1873
- const response = await s3.send(command);
1874
- if (response.$metadata.httpStatusCode !== 200) {
1875
- throw new Error("There was an error while trying to download the wasm file. Please check that the file has the correct permissions (public) set.");
1894
+ if (existsSync(zkeyLocalPathAndFileName)) {
1895
+ spinner.succeed(`The genesis zKey for circuit ${theme.text.bold(circuit.name)} is already present on disk`);
1876
1896
  }
1877
- if (response.Body instanceof Readable)
1878
- await streamPipeline(response.Body, createWriteStream(wasmLocalPathAndFileName));
1879
- spinner.stop();
1880
- // 3. generate the zKey
1881
- await zKey.newZKey(r1csLocalPathAndFileName, getPotLocalFilePath(circuit.files.potFilename), zkeyLocalPathAndFileName, undefined);
1897
+ else {
1898
+ await zKey.newZKey(r1csLocalPathAndFileName, getPotLocalFilePath(circuit.files.potFilename), zkeyLocalPathAndFileName, undefined);
1899
+ spinner.succeed(`Generation of the genesis zKey for circuit ${theme.text.bold(circuit.name)} completed successfully`);
1900
+ }
1901
+ const hashSpinner = customSpinner(`Calculating hashes for circuit ${theme.text.bold(circuit.name)}...`, `clock`);
1902
+ hashSpinner.start();
1882
1903
  // 4. calculate the hashes
1883
1904
  const wasmBlake2bHash = await blake512FromPath(wasmLocalPathAndFileName);
1884
1905
  const potBlake2bHash = await blake512FromPath(getPotLocalFilePath(circuit.files.potFilename));
1885
1906
  const initialZkeyBlake2bHash = await blake512FromPath(zkeyLocalPathAndFileName);
1907
+ hashSpinner.succeed(`Hashes for circuit ${theme.text.bold(circuit.name)} calculated successfully`);
1886
1908
  // 5. upload the artifacts
1887
1909
  // Upload zKey to Storage.
1888
1910
  await handleCircuitArtifactUploadToStorage(firebaseFunctions, bucketName, circuit.files.initialZkeyStoragePath, zkeyLocalPathAndFileName, circuit.files.initialZkeyFilename);
@@ -1900,9 +1922,9 @@ const setup = async (cmd) => {
1900
1922
  // 6 update the setup data object
1901
1923
  ceremonySetupData.circuits[index].files = {
1902
1924
  ...circuit.files,
1903
- potBlake2bHash: potBlake2bHash,
1904
- wasmBlake2bHash: wasmBlake2bHash,
1905
- initialZkeyBlake2bHash: initialZkeyBlake2bHash
1925
+ potBlake2bHash,
1926
+ wasmBlake2bHash,
1927
+ initialZkeyBlake2bHash
1906
1928
  };
1907
1929
  ceremonySetupData.circuits[index].zKeySizeInBytes = getFileStats(zkeyLocalPathAndFileName).size;
1908
1930
  }
@@ -2110,17 +2132,29 @@ const expirationCountdownForGithubOAuth = (expirationInSeconds) => {
2110
2132
  */
2111
2133
  const onVerification = async (verification) => {
2112
2134
  // Copy code to clipboard.
2113
- clipboard.writeSync(verification.user_code);
2114
- clipboard.readSync();
2135
+ let noClipboard = false;
2136
+ try {
2137
+ clipboard.writeSync(verification.user_code);
2138
+ clipboard.readSync();
2139
+ }
2140
+ catch (error) {
2141
+ noClipboard = true;
2142
+ }
2115
2143
  // Display data.
2116
2144
  console.log(`${theme.symbols.warning} Visit ${theme.text.bold(theme.text.underlined(verification.verification_uri))} on this device to generate a new token and authenticate\n`);
2117
- console.log(theme.colors.magenta(figlet.textSync(verification.user_code, { font: "ANSI Shadow" })), '\n');
2118
- console.log(`${theme.symbols.info} Your auth code: ${theme.text.bold(verification.user_code)} has been copied to your clipboard (${theme.emojis.clipboard} ${theme.symbols.success})\n`);
2145
+ console.log(theme.colors.magenta(figlet.textSync("Code is Below", { font: "ANSI Shadow" })), "\n");
2146
+ const message = !noClipboard ? `has been copied to your clipboard (${theme.emojis.clipboard})` : ``;
2147
+ console.log(`${theme.symbols.info} Your auth code: ${theme.text.bold(verification.user_code)} ${message} ${theme.symbols.success}\n`);
2119
2148
  const spinner = customSpinner(`Redirecting to Github...`, `clock`);
2120
2149
  spinner.start();
2121
2150
  await sleep(10000); // ~10s to make users able to read the CLI.
2122
- // Automatically open the page (# Step 2).
2123
- await open(verification.verification_uri);
2151
+ try {
2152
+ // Automatically open the page (# Step 2).
2153
+ await open(verification.verification_uri);
2154
+ }
2155
+ catch (error) {
2156
+ console.log(`${theme.symbols.info} Please authenticate via GitHub at ${verification.verification_uri}`);
2157
+ }
2124
2158
  spinner.stop();
2125
2159
  // Countdown for time expiration.
2126
2160
  expirationCountdownForGithubOAuth(verification.expires_in);
@@ -2566,8 +2600,8 @@ const listenToParticipantDocumentChanges = async (firestoreDatabase, cloudFuncti
2566
2600
  // Communicate resume / start of the contribution to participant.
2567
2601
  await simpleLoader(`${changedContributionStep === "DOWNLOADING" /* ParticipantContributionStep.DOWNLOADING */ ? `Starting` : `Resuming`} your contribution...`, `clock`, 3000);
2568
2602
  // Start / Resume the contribution for the participant.
2569
- await handleStartOrResumeContribution(cloudFunctions, firestoreDatabase, ceremony, circuit, participant, entropy, providerUserId, false // not finalizing.
2570
- );
2603
+ await handleStartOrResumeContribution(cloudFunctions, firestoreDatabase, ceremony, circuit, participant, entropy, providerUserId, false, // not finalizing.
2604
+ circuits.length);
2571
2605
  }
2572
2606
  // Scenario (3.A).
2573
2607
  else if (isWaitingForContribution)
@@ -2616,7 +2650,9 @@ const listenToParticipantDocumentChanges = async (firestoreDatabase, cloudFuncti
2616
2650
  // Get latest contribution verification result.
2617
2651
  await getLatestVerificationResult(firestoreDatabase, ceremony.id, circuit.id, participant.id);
2618
2652
  // Get next circuit for contribution.
2619
- const nextCircuit = timeoutExpired ? getCircuitBySequencePosition(circuits, changedContributionProgress) : getCircuitBySequencePosition(circuits, changedContributionProgress + 1);
2653
+ const nextCircuit = timeoutExpired
2654
+ ? getCircuitBySequencePosition(circuits, changedContributionProgress)
2655
+ : getCircuitBySequencePosition(circuits, changedContributionProgress + 1);
2620
2656
  // Check disk space requirements for participant.
2621
2657
  const wannaGenerateAttestation = await handleDiskSpaceRequirementForNextContribution(cloudFunctions, ceremony.id, nextCircuit.data.sequencePosition, nextCircuit.data.zKeySizeInBytes, timeoutExpired, providerUserId);
2622
2658
  // Check if the participant would like to generate a new attestation.
@@ -2654,11 +2690,12 @@ const listenToParticipantDocumentChanges = async (firestoreDatabase, cloudFuncti
2654
2690
  */
2655
2691
  const contribute = async (opt) => {
2656
2692
  const { firebaseApp, firebaseFunctions, firestoreDatabase } = await bootstrapCommandExecutionAndServices();
2657
- // Check for authentication.
2658
- const { user, providerUserId, token } = await checkAuth(firebaseApp);
2659
2693
  // Get options.
2660
2694
  const ceremonyOpt = opt.ceremony;
2661
2695
  const entropyOpt = opt.entropy;
2696
+ const { auth } = opt;
2697
+ // Check for authentication.
2698
+ const { user, providerUserId, token } = auth ? await authWithToken(firebaseApp, auth) : await checkAuth(firebaseApp);
2662
2699
  // Prepare data.
2663
2700
  let selectedCeremony;
2664
2701
  // Retrieve the opened ceremonies.
@@ -2694,7 +2731,7 @@ const contribute = async (opt) => {
2694
2731
  const userDoc = await getDocumentById(firestoreDatabase, commonTerms.collections.users.name, user.uid);
2695
2732
  const userData = userDoc.data();
2696
2733
  if (!userData) {
2697
- spinner.fail(`Unfortunately we could not find a user document with your information. This likely means that you did not pass the GitHub reputation checks and therefore are not elegible to contribute to any ceremony. Please contact the coordinator if you believe this to be an error.`);
2734
+ spinner.fail(`Unfortunately we could not find a user document with your information. This likely means that you did not pass the GitHub reputation checks and therefore are not elegible to contribute to any ceremony. If you believe you pass the requirements, it might be possible that your profile is private and we were not able to fetch your real statistics, in this case please consider making your profile public for the duration of the contribution. Please contact the coordinator if you believe this to be an error.`);
2698
2735
  process.exit(0);
2699
2736
  }
2700
2737
  // Check the user's current participant readiness for contribution status (eligible, already contributed, timed out).
@@ -2845,7 +2882,7 @@ const observe = async () => {
2845
2882
  // Preserve command execution only for coordinators].
2846
2883
  if (!(await isCoordinator(user)))
2847
2884
  showError(COMMAND_ERRORS.COMMAND_NOT_COORDINATOR, true);
2848
- // Get running cerimonies info (if any).
2885
+ // Get running ceremonies info (if any).
2849
2886
  const runningCeremoniesDocs = await getOpenedCeremonies(firestoreDatabase);
2850
2887
  // Ask to select a ceremony.
2851
2888
  const ceremony = await promptForCeremonySelection(runningCeremoniesDocs, false);
@@ -2914,7 +2951,7 @@ const handleVerifierSmartContract = async (cloudFunctions, bucketName, finalZkey
2914
2951
  const packagePath = `${dirname(fileURLToPath(import.meta.url))}`;
2915
2952
  const verifierPath = packagePath.includes(`src/commands`)
2916
2953
  ? `${dirname(fileURLToPath(import.meta.url))}/../../../../node_modules/snarkjs/templates/verifier_groth16.sol.ejs`
2917
- : `${dirname(fileURLToPath(import.meta.url))}/../../../node_modules/snarkjs/templates/verifier_groth16.sol.ejs`;
2954
+ : `${dirname(fileURLToPath(import.meta.url))}/../node_modules/snarkjs/templates/verifier_groth16.sol.ejs`;
2918
2955
  // Export the Solidity verifier smart contract.
2919
2956
  const verifierCode = await exportVerifierContract(finalZkeyLocalFilePath, verifierPath);
2920
2957
  spinner.text = `Writing verifier smart contract...`;
@@ -2941,10 +2978,11 @@ const handleVerifierSmartContract = async (cloudFunctions, bucketName, finalZkey
2941
2978
  * @param participant <FirebaseDocumentInfo> - the Firestore document of the participant (coordinator).
2942
2979
  * @param beacon <string> - the value used to compute the final contribution while finalizing the ceremony.
2943
2980
  * @param coordinatorIdentifier <string> - the identifier of the coordinator.
2981
+ * @param circuitsLength <number> - the number of circuits in the ceremony.
2944
2982
  */
2945
- const handleCircuitFinalization = async (cloudFunctions, firestoreDatabase, ceremony, circuit, participant, beacon, coordinatorIdentifier) => {
2983
+ const handleCircuitFinalization = async (cloudFunctions, firestoreDatabase, ceremony, circuit, participant, beacon, coordinatorIdentifier, circuitsLength) => {
2946
2984
  // Step (1).
2947
- await handleStartOrResumeContribution(cloudFunctions, firestoreDatabase, ceremony, circuit, participant, computeSHA256ToHex(beacon), coordinatorIdentifier, true);
2985
+ await handleStartOrResumeContribution(cloudFunctions, firestoreDatabase, ceremony, circuit, participant, computeSHA256ToHex(beacon), coordinatorIdentifier, true, circuitsLength);
2948
2986
  await sleep(2000); // workaound for descriptors.
2949
2987
  // Extract data.
2950
2988
  const { prefix: circuitPrefix } = circuit.data;
@@ -2979,10 +3017,11 @@ const handleCircuitFinalization = async (cloudFunctions, firestoreDatabase, cere
2979
3017
  * @dev For proper execution, the command requires the coordinator to be authenticated with a GitHub account (run auth command first) in order to
2980
3018
  * handle sybil-resistance and connect to GitHub APIs to publish the gist containing the final public attestation.
2981
3019
  */
2982
- const finalize = async () => {
3020
+ const finalize = async (opt) => {
2983
3021
  const { firebaseApp, firebaseFunctions, firestoreDatabase } = await bootstrapCommandExecutionAndServices();
2984
3022
  // Check for authentication.
2985
- const { user, providerUserId, token: coordinatorAccessToken } = await checkAuth(firebaseApp);
3023
+ const { auth } = opt;
3024
+ const { user, providerUserId, token: coordinatorAccessToken } = auth ? await authWithToken(firebaseApp, auth) : await checkAuth(firebaseApp);
2986
3025
  // Preserve command execution only for coordinators.
2987
3026
  if (!(await isCoordinator(user)))
2988
3027
  showError(COMMAND_ERRORS.COMMAND_NOT_COORDINATOR, true);
@@ -3017,7 +3056,7 @@ const finalize = async () => {
3017
3056
  const circuits = await getCeremonyCircuits(firestoreDatabase, selectedCeremony.id);
3018
3057
  // Handle finalization for each ceremony circuit.
3019
3058
  for await (const circuit of circuits)
3020
- await handleCircuitFinalization(firebaseFunctions, firestoreDatabase, selectedCeremony, circuit, participant, beacon, providerUserId);
3059
+ await handleCircuitFinalization(firebaseFunctions, firestoreDatabase, selectedCeremony, circuit, participant, beacon, providerUserId, circuits.length);
3021
3060
  process.stdout.write(`\n`);
3022
3061
  const spinner = customSpinner(`Wrapping up the finalization of the ceremony...`, "clock");
3023
3062
  spinner.start();
@@ -3032,7 +3071,7 @@ const finalize = async () => {
3032
3071
  // Generate attestation with final contributions.
3033
3072
  const publicAttestation = await generateValidContributionsAttestation(firestoreDatabase, circuits, selectedCeremony.id, participant.id, contributions, providerUserId, ceremonyName, true);
3034
3073
  // Write public attestation locally.
3035
- writeFile(getAttestationLocalFilePath(`${prefix}_${finalContributionIndex}_${commonTerms.foldersAndPathsTerms.attestation}.log`), Buffer.from(publicAttestation));
3074
+ writeFile(getFinalAttestationLocalFilePath(`${prefix}_${finalContributionIndex}_${commonTerms.foldersAndPathsTerms.attestation}.log`), Buffer.from(publicAttestation));
3036
3075
  await sleep(3000); // workaround for file descriptor unexpected close.
3037
3076
  const gistUrl = await publishGist(coordinatorAccessToken, publicAttestation, ceremonyName, prefix);
3038
3077
  console.log(`\n${theme.symbols.info} Your public final attestation has been successfully posted as Github Gist (${theme.text.bold(theme.text.underlined(gistUrl))})`);
@@ -3168,15 +3207,13 @@ program
3168
3207
  .description("compute contributions for a Phase2 Trusted Setup ceremony circuits")
3169
3208
  .option("-c, --ceremony <string>", "the prefix of the ceremony you want to contribute for", "")
3170
3209
  .option("-e, --entropy <string>", "the entropy (aka toxic waste) of your contribution", "")
3210
+ .option("-a, --auth <string>", "the Github OAuth 2.0 token", "")
3171
3211
  .action(contribute);
3172
3212
  program
3173
3213
  .command("clean")
3174
3214
  .description("clean up output generated by commands from the current working directory")
3175
3215
  .action(clean);
3176
- program
3177
- .command("list")
3178
- .description("List all ceremonies prefixes")
3179
- .action(listCeremonies);
3216
+ program.command("list").description("List all ceremonies prefixes").action(listCeremonies);
3180
3217
  program
3181
3218
  .command("logout")
3182
3219
  .description("sign out from Firebae Auth service and delete Github OAuth 2.0 token from local storage")
@@ -3192,8 +3229,8 @@ const ceremony = program.command("coordinate").description("commands for coordin
3192
3229
  ceremony
3193
3230
  .command("setup")
3194
3231
  .description("setup a Groth16 Phase 2 Trusted Setup ceremony for zk-SNARK circuits")
3195
- .option('-t, --template <path>', 'The path to the ceremony setup template', '')
3196
- .option('-a, --auth <string>', 'The Github OAuth 2.0 token', '')
3232
+ .option("-t, --template <path>", "The path to the ceremony setup template", "")
3233
+ .option("-a, --auth <string>", "The Github OAuth 2.0 token", "")
3197
3234
  .action(setup);
3198
3235
  ceremony
3199
3236
  .command("observe")
@@ -3202,5 +3239,6 @@ ceremony
3202
3239
  ceremony
3203
3240
  .command("finalize")
3204
3241
  .description("finalize a Phase2 Trusted Setup ceremony by applying a beacon, exporting verification key and verifier contract")
3242
+ .option("-a, --auth <string>", "the Github OAuth 2.0 token", "")
3205
3243
  .action(finalize);
3206
3244
  program.parseAsync(process.argv);
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { Contribution, ContributionValidity, FirebaseDocumentInfo } from "@p0tion/actions";
2
+ import { Contribution, ContributionValidity, FirebaseDocumentInfo } from "@devtion/actions";
3
3
  import { DocumentSnapshot, DocumentData, Firestore } from "firebase/firestore";
4
4
  import { Functions } from "firebase/functions";
5
5
  /**
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { FirebaseDocumentInfo } from "@p0tion/actions";
2
+ import { FirebaseDocumentInfo } from "@devtion/actions";
3
3
  import { Functions } from "firebase/functions";
4
4
  import { Firestore } from "firebase/firestore";
5
5
  /**
@@ -36,8 +36,9 @@ export declare const handleVerifierSmartContract: (cloudFunctions: Functions, bu
36
36
  * @param participant <FirebaseDocumentInfo> - the Firestore document of the participant (coordinator).
37
37
  * @param beacon <string> - the value used to compute the final contribution while finalizing the ceremony.
38
38
  * @param coordinatorIdentifier <string> - the identifier of the coordinator.
39
+ * @param circuitsLength <number> - the number of circuits in the ceremony.
39
40
  */
40
- export declare const handleCircuitFinalization: (cloudFunctions: Functions, firestoreDatabase: Firestore, ceremony: FirebaseDocumentInfo, circuit: FirebaseDocumentInfo, participant: FirebaseDocumentInfo, beacon: string, coordinatorIdentifier: string) => Promise<void>;
41
+ export declare const handleCircuitFinalization: (cloudFunctions: Functions, firestoreDatabase: Firestore, ceremony: FirebaseDocumentInfo, circuit: FirebaseDocumentInfo, participant: FirebaseDocumentInfo, beacon: string, coordinatorIdentifier: string, circuitsLength: number) => Promise<void>;
41
42
  /**
42
43
  * Finalize command.
43
44
  * @notice The finalize command allows a coordinator to finalize a Trusted Setup Phase 2 ceremony by providing the final beacon,
@@ -47,5 +48,5 @@ export declare const handleCircuitFinalization: (cloudFunctions: Functions, fire
47
48
  * @dev For proper execution, the command requires the coordinator to be authenticated with a GitHub account (run auth command first) in order to
48
49
  * handle sybil-resistance and connect to GitHub APIs to publish the gist containing the final public attestation.
49
50
  */
50
- declare const finalize: () => Promise<void>;
51
+ declare const finalize: (opt: any) => Promise<void>;
51
52
  export default finalize;
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { FirebaseDocumentInfo } from "@p0tion/actions";
2
+ import { FirebaseDocumentInfo } from "@devtion/actions";
3
3
  import { Firestore } from "firebase/firestore";
4
4
  /**
5
5
  * Clean cursor lines from current position back to root (default: zero).
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { Functions } from "firebase/functions";
3
- import { CeremonyTimeoutType, CircomCompilerData, CircuitInputData, CeremonyInputData, CircuitDocument } from "@p0tion/actions";
3
+ import { CeremonyTimeoutType, CircomCompilerData, CircuitInputData, CeremonyInputData, CircuitDocument } from "@devtion/actions";
4
4
  /**
5
5
  * Handle whatever is needed to obtain the input data for a circuit that the coordinator would like to add to the ceremony.
6
6
  * @param choosenCircuitFilename <string> - the name of the circuit to add.
@@ -39,7 +39,7 @@ export declare const checkAndDownloadSmallestPowersOfTau: (powers: string, ptauC
39
39
  * number of powers greater than or equal to the powers needed by the zKey), the coordinator will be asked
40
40
  * to provide a number of powers manually, ranging from the smallest possible to the largest.
41
41
  * @param neededPowers <number> - the smallest amount of powers needed by the zKey.
42
- * @returns Promise<string, string> - the information about the choosen Powers of Tau file for the pre-computed zKey
42
+ * @returns Promise<string, string> - the information about the chosen Powers of Tau file for the pre-computed zKey
43
43
  * along with related powers.
44
44
  */
45
45
  export declare const handlePreComputedZkeyPowersOfTauSelection: (neededPowers: number) => Promise<{