@devtion/devcli 0.0.0-9843891 → 0.0.0-9d46256

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -97,7 +97,7 @@ Commands:
97
97
  auth authenticate yourself using your Github Account (Device Flow OAuth 2.0)
98
98
  contribute compute contributions for a Phase2 Trusted Setup ceremony circuits
99
99
  clean clean up output generated by commands from the current working directory
100
- logout sign out from Firebae Auth service and delete Github OAuth 2.0 token from your machine
100
+ logout sign out from Firebase Auth service and delete Github OAuth 2.0 token from your machine
101
101
  coordinate special subset of commands for coordinating a ceremony (coordinator only)
102
102
  help [command] display help for command
103
103
  ```
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  /**
4
4
  * @module @p0tion/phase2cli
5
- * @version 1.2.3
5
+ * @version 1.2.8
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
@@ -17,7 +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 { 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, getAllCeremonies } from '@devtion/actions';
20
+ import { commonTerms, formatZkeyIndex, getZkeyStorageFilePath, finalContributionIndex, createCustomLoggerForFile, getBucketName, progressToNextContributionStep, contribHashRegex, 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, getAllCeremonies } from '@devtion/actions';
21
21
  import fetch from '@adobe/node-fetch-retry';
22
22
  import { request } from '@octokit/request';
23
23
  import { SingleBar, Presets } from 'cli-progress';
@@ -122,7 +122,7 @@ const COMMAND_ERRORS = {
122
122
  COMMAND_SETUP_NO_R1CS: `Unable to retrieve R1CS files from current working directory. Please, run this command from a working directory where the R1CS files are located to continue with the setup process. We kindly ask you to run the command from an empty directory containing only the R1CS and WASM files.`,
123
123
  COMMAND_SETUP_NO_WASM: `Unable to retrieve WASM files from current working directory. Please, run this command from a working directory where the WASM files are located to continue with the setup process. We kindly ask you to run the command from an empty directory containing only the WASM and R1CS files.`,
124
124
  COMMAND_SETUP_MISMATCH_R1CS_WASM: `The folder contains more R1CS files than WASM files (or vice versa). Please, run this command from a working directory where each R1CS is paired with its corresponding file WASM.`,
125
- COMMAND_SETUP_DOWNLOAD_PTAU: `Unable to download Powers of Tau file from Hermez Cryptography Phase 1 Trusted Setup. Possible causes may involve an error while making the request (be sure to have a stable internet connection). Please, we kindly ask you to terminate the current session and repeat the process.`,
125
+ COMMAND_SETUP_DOWNLOAD_PTAU: `Unable to download Powers of Tau file from PPoT Phase 1 Trusted Setup. Possible causes may involve an error while making the request (be sure to have a stable internet connection). Please, we kindly ask you to terminate the current session and repeat the process.`,
126
126
  COMMAND_SETUP_ABORT: `You chose to abort the setup process.`,
127
127
  COMMAND_CONTRIBUTE_NO_OPENED_CEREMONIES: `Unfortunately, there is no ceremony for which you can make a contribution at this time. Please, try again later.`,
128
128
  COMMAND_CONTRIBUTE_NO_PARTICIPANT_DATA: `Unable to retrieve your data as ceremony participant. Please, terminate the current session and try again later. If the error persists, please contact the ceremony coordinator.`,
@@ -523,7 +523,7 @@ const getUserHandleFromProviderUserId = (providerUserId) => {
523
523
  if (providerUserId.indexOf("-") === -1) {
524
524
  return providerUserId;
525
525
  }
526
- return providerUserId.split("-")[0];
526
+ return providerUserId.substring(0, providerUserId.lastIndexOf("-"));
527
527
  };
528
528
  /**
529
529
  * Return a custom spinner.
@@ -637,19 +637,22 @@ const publishGist = async (token, content, ceremonyTitle, ceremonyPrefix) => {
637
637
  * @param isFinalizing <boolean> - flag to discriminate between ceremony finalization (true) and contribution (false).
638
638
  * @returns <string> - the ready to share tweet url.
639
639
  */
640
- const generateCustomUrlToTweetAboutParticipation = (ceremonyName, gistUrl, isFinalizing) => isFinalizing
641
- ? `https://twitter.com/intent/tweet?text=I%20have%20finalized%20the%20${ceremonyName}${ceremonyName.toLowerCase().includes("trusted") ||
642
- ceremonyName.toLowerCase().includes("setup") ||
643
- ceremonyName.toLowerCase().includes("phase2") ||
644
- ceremonyName.toLowerCase().includes("ceremony")
645
- ? "!"
646
- : "%20Phase%202%20Trusted%20Setup%20ceremony!"}%20You%20can%20view%20my%20final%20attestation%20here:%20${gistUrl}%20#Ethereum%20#ZKP%20#PSE`
647
- : `https://twitter.com/intent/tweet?text=I%20contributed%20to%20the%20${ceremonyName}${ceremonyName.toLowerCase().includes("trusted") ||
648
- ceremonyName.toLowerCase().includes("setup") ||
649
- ceremonyName.toLowerCase().includes("phase2") ||
650
- ceremonyName.toLowerCase().includes("ceremony")
651
- ? "!"
652
- : "%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`;
640
+ const generateCustomUrlToTweetAboutParticipation = (ceremonyName, gistUrl, isFinalizing) => {
641
+ ceremonyName = ceremonyName.replace(/ /g, "%20");
642
+ return isFinalizing
643
+ ? `https://twitter.com/intent/tweet?text=I%20have%20finalized%20the%20${ceremonyName}${ceremonyName.toLowerCase().includes("trusted") ||
644
+ ceremonyName.toLowerCase().includes("setup") ||
645
+ ceremonyName.toLowerCase().includes("phase2") ||
646
+ ceremonyName.toLowerCase().includes("ceremony")
647
+ ? "!"
648
+ : "%20Phase%202%20Trusted%20Setup%20ceremony!"}%20You%20can%20view%20my%20final%20attestation%20here:%20${gistUrl}%20#Ethereum%20#ZKP%20#PSE`
649
+ : `https://twitter.com/intent/tweet?text=I%20contributed%20to%20the%20${ceremonyName}${ceremonyName.toLowerCase().includes("trusted") ||
650
+ ceremonyName.toLowerCase().includes("setup") ||
651
+ ceremonyName.toLowerCase().includes("phase2") ||
652
+ ceremonyName.toLowerCase().includes("ceremony")
653
+ ? "!"
654
+ : "%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`;
655
+ };
653
656
  /**
654
657
  * Return a custom progress bar.
655
658
  * @param type <ProgressBarType> - the type of the progress bar.
@@ -853,7 +856,7 @@ const handleStartOrResumeContribution = async (cloudFunctions, firestoreDatabase
853
856
  spinner.start();
854
857
  // Read local transcript file info to get the contribution hash.
855
858
  const transcriptContents = readFile(transcriptLocalFilePath);
856
- const matchContributionHash = transcriptContents.match(/Contribution.+Hash.+\n\t\t.+\n\t\t.+\n.+\n\t\t.+\n/);
859
+ const matchContributionHash = transcriptContents.match(contribHashRegex);
857
860
  if (!matchContributionHash)
858
861
  showError(COMMAND_ERRORS.COMMAND_CONTRIBUTE_FINALIZE_NO_TRANSCRIPT_CONTRIBUTION_HASH_MATCH, true);
859
862
  // Format contribution hash.
@@ -1783,7 +1786,7 @@ const displayCeremonySummary = (ceremonyInputData, circuits) => {
1783
1786
  };
1784
1787
  /**
1785
1788
  * Check if the smallest Powers of Tau has already been downloaded/stored in the correspondent local path
1786
- * @dev we are downloading the Powers of Tau file from Hermez Cryptography Phase 1 Trusted Setup.
1789
+ * @dev we are downloading the Powers of Tau file from Perpetual Powers of Tau Phase 1 Trusted Setup.
1787
1790
  * @param powers <string> - the smallest amount of powers needed for the given circuit (should be in a 'XY' stringified form).
1788
1791
  * @param ptauCompleteFilename <string> - the complete file name of the powers of tau file to be downloaded.
1789
1792
  * @returns <Promise<void>>
@@ -1797,7 +1800,7 @@ const checkAndDownloadSmallestPowersOfTau = async (powers, ptauCompleteFilename)
1797
1800
  .map((dirent) => dirent.name);
1798
1801
  // Check if already downloaded or not.
1799
1802
  if (smallestPtauFileForGivenPowers.length === 0) {
1800
- const spinner = customSpinner(`Downloading the ${theme.text.bold(`#${powers}`)} smallest PoT file needed from the Hermez Cryptography Phase 1 Trusted Setup...`, `clock`);
1803
+ const spinner = customSpinner(`Downloading the ${theme.text.bold(`#${powers}`)} smallest PoT file needed from the Perpetual Powers of Tau Phase 1 Trusted Setup...`, `clock`);
1801
1804
  spinner.start();
1802
1805
  // Download smallest Powers of Tau file from remote server.
1803
1806
  const streamPipeline = promisify(pipeline);
@@ -1883,6 +1886,7 @@ const handleCeremonyBucketCreation = async (firebaseFunctions, ceremonyPrefix) =
1883
1886
  spinner.start();
1884
1887
  try {
1885
1888
  // Make the call to create the bucket.
1889
+ spinner.info(`Creating bucket ${bucketName}`);
1886
1890
  await createS3Bucket(firebaseFunctions, bucketName);
1887
1891
  }
1888
1892
  catch (error) {
@@ -1912,7 +1916,7 @@ const handleCircuitArtifactUploadToStorage = async (firebaseFunctions, bucketNam
1912
1916
  * @notice The setup command allows the coordinator of the ceremony to prepare the next ceremony by interacting with the CLI.
1913
1917
  * @dev For proper execution, the command must be run in a folder containing the R1CS files related to the circuits
1914
1918
  * for which the coordinator wants to create the ceremony. The command will download the necessary Tau powers
1915
- * from Hermez's ceremony Phase 1 Reliable Setup Ceremony.
1919
+ * from PPoT ceremony Phase 1 Setup Ceremony.
1916
1920
  * @param cmd? <any> - the path to the ceremony setup file.
1917
1921
  */
1918
1922
  const setup = async (cmd) => {
@@ -2187,10 +2191,11 @@ const expirationCountdownForGithubOAuth = (expirationInSeconds) => {
2187
2191
  // Update time and seconds counter.
2188
2192
  expirationInSeconds -= interval;
2189
2193
  secondsCounter -= interval;
2190
- if (secondsCounter % 60 === 0)
2191
- secondsCounter = 0;
2194
+ if (secondsCounter === 0) {
2195
+ secondsCounter = 59;
2196
+ }
2192
2197
  // Notify user.
2193
- process.stdout.write(`${theme.symbols.warning} Expires in ${theme.text.bold(theme.colors.magenta(`00:${Math.floor(expirationInSeconds / 60)}:${secondsCounter}`))}\r`);
2198
+ process.stdout.write(`${theme.symbols.warning} Expires in ${theme.text.bold(theme.colors.magenta(`00:${Math.floor(expirationInSeconds / 60)}:${secondsCounter.toString().padStart(2, "0")}`))}\r`);
2194
2199
  }
2195
2200
  else {
2196
2201
  process.stdout.write(`\n\n`); // workaround to \r.
@@ -2203,6 +2208,7 @@ const expirationCountdownForGithubOAuth = (expirationInSeconds) => {
2203
2208
  * @param verification <Verification> - the data from Github OAuth2.0 device flow.
2204
2209
  */
2205
2210
  const onVerification = async (verification) => {
2211
+ verification.interval = 7; // overwrite Github interval with 7 seconds
2206
2212
  // Copy code to clipboard.
2207
2213
  let noClipboard = false;
2208
2214
  try {
@@ -27,7 +27,7 @@ export declare const handleAdditionOfCircuitsToCeremony: (r1csOptions: Array<str
27
27
  export declare const displayCeremonySummary: (ceremonyInputData: CeremonyInputData, circuits: Array<CircuitDocument>) => void;
28
28
  /**
29
29
  * Check if the smallest Powers of Tau has already been downloaded/stored in the correspondent local path
30
- * @dev we are downloading the Powers of Tau file from Hermez Cryptography Phase 1 Trusted Setup.
30
+ * @dev we are downloading the Powers of Tau file from Perpetual Powers of Tau Phase 1 Trusted Setup.
31
31
  * @param powers <string> - the smallest amount of powers needed for the given circuit (should be in a 'XY' stringified form).
32
32
  * @param ptauCompleteFilename <string> - the complete file name of the powers of tau file to be downloaded.
33
33
  * @returns <Promise<void>>
@@ -76,7 +76,7 @@ export declare const handleCircuitArtifactUploadToStorage: (firebaseFunctions: F
76
76
  * @notice The setup command allows the coordinator of the ceremony to prepare the next ceremony by interacting with the CLI.
77
77
  * @dev For proper execution, the command must be run in a folder containing the R1CS files related to the circuits
78
78
  * for which the coordinator wants to create the ceremony. The command will download the necessary Tau powers
79
- * from Hermez's ceremony Phase 1 Reliable Setup Ceremony.
79
+ * from PPoT ceremony Phase 1 Setup Ceremony.
80
80
  * @param cmd? <any> - the path to the ceremony setup file.
81
81
  */
82
82
  declare const setup: (cmd: {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@devtion/devcli",
3
3
  "type": "module",
4
- "version": "0.0.0-9843891",
4
+ "version": "0.0.0-9d46256",
5
5
  "description": "All-in-one interactive command-line for interfacing with zkSNARK Phase 2 Trusted Setup ceremonies",
6
6
  "repository": "git@github.com:privacy-scaling-explorations/p0tion.git",
7
7
  "homepage": "https://github.com/privacy-scaling-explorations/p0tion",
@@ -104,5 +104,5 @@
104
104
  "publishConfig": {
105
105
  "access": "public"
106
106
  },
107
- "gitHead": "438c08492d7e96d2e935cb07aa26492beb79170a"
107
+ "gitHead": "db993436c57f04fae0ee535d013474a6f87f8271"
108
108
  }
@@ -47,12 +47,16 @@ export const expirationCountdownForGithubOAuth = (expirationInSeconds: number) =
47
47
  expirationInSeconds -= interval
48
48
  secondsCounter -= interval
49
49
 
50
- if (secondsCounter % 60 === 0) secondsCounter = 0
50
+ if (secondsCounter === 0) {
51
+ secondsCounter = 59
52
+ }
51
53
 
52
54
  // Notify user.
53
55
  process.stdout.write(
54
56
  `${theme.symbols.warning} Expires in ${theme.text.bold(
55
- theme.colors.magenta(`00:${Math.floor(expirationInSeconds / 60)}:${secondsCounter}`)
57
+ theme.colors.magenta(
58
+ `00:${Math.floor(expirationInSeconds / 60)}:${secondsCounter.toString().padStart(2, "0")}`
59
+ )
56
60
  )}\r`
57
61
  )
58
62
  } else {
@@ -67,6 +71,7 @@ export const expirationCountdownForGithubOAuth = (expirationInSeconds: number) =
67
71
  * @param verification <Verification> - the data from Github OAuth2.0 device flow.
68
72
  */
69
73
  export const onVerification = async (verification: Verification): Promise<void> => {
74
+ verification.interval = 7 // overwrite Github interval with 7 seconds
70
75
  // Copy code to clipboard.
71
76
  let noClipboard = false
72
77
  try {
@@ -271,7 +271,7 @@ export const displayCeremonySummary = (ceremonyInputData: CeremonyInputData, cir
271
271
 
272
272
  /**
273
273
  * Check if the smallest Powers of Tau has already been downloaded/stored in the correspondent local path
274
- * @dev we are downloading the Powers of Tau file from Hermez Cryptography Phase 1 Trusted Setup.
274
+ * @dev we are downloading the Powers of Tau file from Perpetual Powers of Tau Phase 1 Trusted Setup.
275
275
  * @param powers <string> - the smallest amount of powers needed for the given circuit (should be in a 'XY' stringified form).
276
276
  * @param ptauCompleteFilename <string> - the complete file name of the powers of tau file to be downloaded.
277
277
  * @returns <Promise<void>>
@@ -293,7 +293,7 @@ export const checkAndDownloadSmallestPowersOfTau = async (
293
293
  const spinner = customSpinner(
294
294
  `Downloading the ${theme.text.bold(
295
295
  `#${powers}`
296
- )} smallest PoT file needed from the Hermez Cryptography Phase 1 Trusted Setup...`,
296
+ )} smallest PoT file needed from the Perpetual Powers of Tau Phase 1 Trusted Setup...`,
297
297
  `clock`
298
298
  )
299
299
  spinner.start()
@@ -417,6 +417,7 @@ export const handleCeremonyBucketCreation = async (
417
417
 
418
418
  try {
419
419
  // Make the call to create the bucket.
420
+ spinner.info(`Creating bucket ${bucketName}`)
420
421
  await createS3Bucket(firebaseFunctions, bucketName)
421
422
  } catch (error: any) {
422
423
  const errorBody = JSON.parse(JSON.stringify(error))
@@ -463,7 +464,7 @@ export const handleCircuitArtifactUploadToStorage = async (
463
464
  * @notice The setup command allows the coordinator of the ceremony to prepare the next ceremony by interacting with the CLI.
464
465
  * @dev For proper execution, the command must be run in a folder containing the R1CS files related to the circuits
465
466
  * for which the coordinator wants to create the ceremony. The command will download the necessary Tau powers
466
- * from Hermez's ceremony Phase 1 Reliable Setup Ceremony.
467
+ * from PPoT ceremony Phase 1 Setup Ceremony.
467
468
  * @param cmd? <any> - the path to the ceremony setup file.
468
469
  */
469
470
  const setup = async (cmd: { template?: string; auth?: string }) => {
package/src/lib/errors.ts CHANGED
@@ -34,7 +34,7 @@ export const COMMAND_ERRORS = {
34
34
  COMMAND_SETUP_NO_R1CS: `Unable to retrieve R1CS files from current working directory. Please, run this command from a working directory where the R1CS files are located to continue with the setup process. We kindly ask you to run the command from an empty directory containing only the R1CS and WASM files.`,
35
35
  COMMAND_SETUP_NO_WASM: `Unable to retrieve WASM files from current working directory. Please, run this command from a working directory where the WASM files are located to continue with the setup process. We kindly ask you to run the command from an empty directory containing only the WASM and R1CS files.`,
36
36
  COMMAND_SETUP_MISMATCH_R1CS_WASM: `The folder contains more R1CS files than WASM files (or vice versa). Please, run this command from a working directory where each R1CS is paired with its corresponding file WASM.`,
37
- COMMAND_SETUP_DOWNLOAD_PTAU: `Unable to download Powers of Tau file from Hermez Cryptography Phase 1 Trusted Setup. Possible causes may involve an error while making the request (be sure to have a stable internet connection). Please, we kindly ask you to terminate the current session and repeat the process.`,
37
+ COMMAND_SETUP_DOWNLOAD_PTAU: `Unable to download Powers of Tau file from PPoT Phase 1 Trusted Setup. Possible causes may involve an error while making the request (be sure to have a stable internet connection). Please, we kindly ask you to terminate the current session and repeat the process.`,
38
38
  COMMAND_SETUP_ABORT: `You chose to abort the setup process.`,
39
39
  COMMAND_CONTRIBUTE_NO_OPENED_CEREMONIES: `Unfortunately, there is no ceremony for which you can make a contribution at this time. Please, try again later.`,
40
40
  COMMAND_CONTRIBUTE_NO_PARTICIPANT_DATA: `Unable to retrieve your data as ceremony participant. Please, terminate the current session and try again later. If the error persists, please contact the ceremony coordinator.`,
package/src/lib/utils.ts CHANGED
@@ -18,7 +18,8 @@ import {
18
18
  ParticipantContributionStep,
19
19
  permanentlyStoreCurrentContributionTimeAndHash,
20
20
  progressToNextContributionStep,
21
- verifyContribution
21
+ verifyContribution,
22
+ contribHashRegex
22
23
  } from "@devtion/actions"
23
24
  import { Presets, SingleBar } from "cli-progress"
24
25
  import dotenv from "dotenv"
@@ -159,7 +160,7 @@ export const getUserHandleFromProviderUserId = (providerUserId: string): string
159
160
  return providerUserId
160
161
  }
161
162
 
162
- return providerUserId.split("-")[0]
163
+ return providerUserId.substring(0, providerUserId.lastIndexOf("-"))
163
164
  }
164
165
 
165
166
  /**
@@ -311,8 +312,9 @@ export const generateCustomUrlToTweetAboutParticipation = (
311
312
  ceremonyName: string,
312
313
  gistUrl: string,
313
314
  isFinalizing: boolean
314
- ) =>
315
- isFinalizing
315
+ ) => {
316
+ ceremonyName = ceremonyName.replace(/ /g, "%20")
317
+ return isFinalizing
316
318
  ? `https://twitter.com/intent/tweet?text=I%20have%20finalized%20the%20${ceremonyName}${
317
319
  ceremonyName.toLowerCase().includes("trusted") ||
318
320
  ceremonyName.toLowerCase().includes("setup") ||
@@ -329,6 +331,7 @@ export const generateCustomUrlToTweetAboutParticipation = (
329
331
  ? "!"
330
332
  : "%20Phase%202%20Trusted%20Setup%20ceremony!"
331
333
  }%20You%20can%20view%20the%20steps%20to%20contribute%20here:%20https://ceremony.pse.dev%20You%20can%20view%20my%20attestation%20here:%20${gistUrl}%20#Ethereum%20#ZKP`
334
+ }
332
335
 
333
336
  /**
334
337
  * Return a custom progress bar.
@@ -664,7 +667,7 @@ export const handleStartOrResumeContribution = async (
664
667
 
665
668
  // Read local transcript file info to get the contribution hash.
666
669
  const transcriptContents = readFile(transcriptLocalFilePath)
667
- const matchContributionHash = transcriptContents.match(/Contribution.+Hash.+\n\t\t.+\n\t\t.+\n.+\n\t\t.+\n/)
670
+ const matchContributionHash = transcriptContents.match(contribHashRegex)
668
671
 
669
672
  if (!matchContributionHash)
670
673
  showError(COMMAND_ERRORS.COMMAND_CONTRIBUTE_FINALIZE_NO_TRANSCRIPT_CONTRIBUTION_HASH_MATCH, true)