@devtion/devcli 0.0.0-3df1645 → 0.0.0-477457c

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.
@@ -0,0 +1,185 @@
1
+ import open from "open"
2
+ import figlet from "figlet"
3
+ import clipboard from "clipboardy"
4
+ import fetch from "node-fetch"
5
+ import { getAuth, signInWithCustomToken } from "firebase/auth"
6
+ import { httpsCallable } from "firebase/functions"
7
+ import { commonTerms } from "@devtion/actions"
8
+ import { showError } from "../lib/errors.js"
9
+ import { bootstrapCommandExecutionAndServices } from "../lib/services.js"
10
+ import theme from "../lib/theme.js"
11
+ import { customSpinner, sleep } from "../lib/utils.js"
12
+ import { CheckNonceOfSIWEAddressResponse, OAuthDeviceCodeResponse, OAuthTokenResponse } from "../types/index.js"
13
+ import {
14
+ checkLocalAccessToken,
15
+ deleteLocalAccessToken,
16
+ deleteLocalAuthMethod,
17
+ getLocalAccessToken,
18
+ setLocalAccessToken,
19
+ setLocalAuthMethod
20
+ } from "../lib/localConfigs.js"
21
+
22
+ const showVerificationCodeAndUri = async (OAuthDeviceCode: OAuthDeviceCodeResponse) => {
23
+ // Copy code to clipboard.
24
+ let noClipboard = false
25
+ try {
26
+ clipboard.writeSync(OAuthDeviceCode.user_code)
27
+ clipboard.readSync()
28
+ } catch (error) {
29
+ noClipboard = true
30
+ }
31
+ // Display data.
32
+ console.log(
33
+ `${theme.symbols.warning} Visit ${theme.text.bold(
34
+ theme.text.underlined(OAuthDeviceCode.verification_uri_complete)
35
+ )} on this device to generate a new token and authenticate\n`
36
+ )
37
+ console.log(theme.colors.magenta(figlet.textSync("Code is Below", { font: "ANSI Shadow" })), "\n")
38
+
39
+ const message = !noClipboard ? `has been copied to your clipboard (${theme.emojis.clipboard})` : ``
40
+ console.log(
41
+ `${theme.symbols.info} Your auth code: ${theme.text.bold(OAuthDeviceCode.user_code)} ${message} ${
42
+ theme.symbols.success
43
+ }\n`
44
+ )
45
+ const spinner = customSpinner(`Redirecting to Sign In With Ethereum...`, `clock`)
46
+ spinner.start()
47
+ await sleep(10000) // ~10s to make users able to read the CLI.
48
+ try {
49
+ // Automatically open the page (# Step 2).
50
+ await open(OAuthDeviceCode.verification_uri_complete)
51
+ } catch (error: any) {
52
+ console.log(
53
+ `${theme.symbols.info} Please authenticate via SIWE at ${OAuthDeviceCode.verification_uri_complete}`
54
+ )
55
+ }
56
+ spinner.stop()
57
+ }
58
+
59
+ /**
60
+ * Return the token to sign in to Firebase after passing the SIWE Device Flow
61
+ * @param clientId <string> - The client id of the Auth0 application.
62
+ * @param firebaseFunctions <any> - The Firebase functions instance to call the cloud function
63
+ * @returns <string> - The token to sign in to Firebase
64
+ */
65
+ const executeSIWEDeviceFlow = async (clientId: string, firebaseFunctions: any): Promise<string> => {
66
+ // Call Auth0 endpoint to request device code uri
67
+ const OAuthDeviceCode = (await fetch(`${process.env.AUTH0_APPLICATION_URL}/oauth/device/code`, {
68
+ method: "POST",
69
+ headers: { "content-type": "application/json" },
70
+ body: JSON.stringify({
71
+ client_id: clientId,
72
+ scope: "openid",
73
+ audience: `${process.env.AUTH0_APPLICATION_URL}/api/v2/`
74
+ })
75
+ }).then((_res) => _res.json())) as OAuthDeviceCodeResponse
76
+ if (OAuthDeviceCode.error) {
77
+ showError(OAuthDeviceCode.error_description, true)
78
+ deleteLocalAuthMethod()
79
+ deleteLocalAccessToken()
80
+ }
81
+ await showVerificationCodeAndUri(OAuthDeviceCode)
82
+ // Poll Auth0 endpoint until you get token or request expires
83
+ let isSignedIn = false
84
+ let isExpired = false
85
+ let auth0Token = ""
86
+ while (!isSignedIn && !isExpired) {
87
+ // Call Auth0 endpoint to request token
88
+ const OAuthToken = (await fetch(`${process.env.AUTH0_APPLICATION_URL}/oauth/token`, {
89
+ method: "POST",
90
+ headers: { "content-type": "application/json" },
91
+ body: JSON.stringify({
92
+ client_id: clientId,
93
+ device_code: OAuthDeviceCode.device_code,
94
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code"
95
+ })
96
+ }).then((_res) => _res.json())) as OAuthTokenResponse
97
+ if (OAuthToken.error) {
98
+ if (OAuthToken.error === "authorization_pending") {
99
+ // Wait for the user to sign in
100
+ await sleep(OAuthDeviceCode.interval * 1000)
101
+ } else if (OAuthToken.error === "slow_down") {
102
+ // Wait for the user to sign in
103
+ await sleep(OAuthDeviceCode.interval * 1000 * 2)
104
+ } else if (OAuthToken.error === "expired_token") {
105
+ // The user didn't sign in on time
106
+ isExpired = true
107
+ }
108
+ } else {
109
+ // The user signed in
110
+ isSignedIn = true
111
+ auth0Token = OAuthToken.access_token
112
+ }
113
+ }
114
+ // Send token to cloud function to check nonce, create user and retrieve token
115
+ const cf = httpsCallable(firebaseFunctions, commonTerms.cloudFunctionsNames.checkNonceOfSIWEAddress)
116
+ const result = await cf({
117
+ auth0Token
118
+ })
119
+ const { token, valid, message } = result.data as CheckNonceOfSIWEAddressResponse
120
+ if (!valid) {
121
+ showError(message, true)
122
+ deleteLocalAuthMethod()
123
+ deleteLocalAccessToken()
124
+ }
125
+ return token
126
+ }
127
+
128
+ /**
129
+ * Auth command using Sign In With Ethereum
130
+ * @notice The auth command allows a user to make the association of their Ethereum account with the CLI by leveraging SIWE as an authentication mechanism.
131
+ * @dev Under the hood, the command handles a manual Device Flow following the guidelines in the SIWE documentation.
132
+ */
133
+ const authSIWE = async () => {
134
+ try {
135
+ const { firebaseFunctions } = await bootstrapCommandExecutionAndServices()
136
+ // Console more context for the user.
137
+ console.log(
138
+ `${theme.symbols.info} ${theme.text.bold(
139
+ `You are about to authenticate on this CLI using your Ethereum address (device flow - OAuth 2.0 mechanism).\n${theme.symbols.warning} Please, note that only a Sign-in With Ethereum signature will be required`
140
+ )}\n`
141
+ )
142
+ const spinner = customSpinner(`Checking authentication token...`, `clock`)
143
+ spinner.start()
144
+ await sleep(5000)
145
+
146
+ // Manage OAuth Github or SIWE token.
147
+ const isLocalTokenStored = checkLocalAccessToken()
148
+
149
+ if (!isLocalTokenStored) {
150
+ spinner.fail(`No local authentication token found\n`)
151
+
152
+ // Generate a new access token using Github Device Flow (OAuth 2.0).
153
+ const newToken = await executeSIWEDeviceFlow(String(process.env.AUTH_SIWE_CLIENT_ID), firebaseFunctions)
154
+
155
+ // Store the new access token.
156
+ setLocalAuthMethod("siwe")
157
+ setLocalAccessToken(newToken)
158
+ } else spinner.succeed(`Local authentication token found\n`)
159
+
160
+ // Get access token from local store.
161
+ const token = String(getLocalAccessToken())
162
+
163
+ spinner.text = `Authenticating...`
164
+ spinner.start()
165
+
166
+ // Exchange token for credential.
167
+ const credentials = await signInWithCustomToken(getAuth(), token)
168
+ spinner.succeed(`Authenticated as ${theme.text.bold(credentials.user.uid)}.`)
169
+
170
+ console.log(
171
+ `\n${theme.symbols.warning} You can always log out by running the ${theme.text.bold(
172
+ `phase2cli logout`
173
+ )} command`
174
+ )
175
+ process.exit(0)
176
+ } catch (error) {
177
+ // Delete local token.
178
+ console.log("An error crashed the process. Deleting local token and identity.")
179
+ console.error(error)
180
+ deleteLocalAuthMethod()
181
+ deleteLocalAccessToken()
182
+ }
183
+ }
184
+
185
+ export default authSIWE
@@ -0,0 +1,20 @@
1
+ import { Command } from "commander"
2
+ import listParticipants from "./listParticipants.js"
3
+
4
+ const setCeremonyCommands = (program: Command) => {
5
+ const ceremony = program.command("ceremony").description("manage ceremonies")
6
+
7
+ ceremony
8
+ .command("participants")
9
+ .description("retrieve participants list of a ceremony")
10
+ .requiredOption(
11
+ "-c, --ceremony <string>",
12
+ "the prefix of the ceremony you want to retrieve information about",
13
+ ""
14
+ )
15
+ .action(listParticipants)
16
+
17
+ return ceremony
18
+ }
19
+
20
+ export default setCeremonyCommands
@@ -0,0 +1,56 @@
1
+ import { collection, doc, getDocs } from "firebase/firestore"
2
+ import { ParticipantDocument, UserDocument, commonTerms, getAllCeremonies } from "@devtion/actions"
3
+ import theme from "../../lib/theme.js"
4
+ import { bootstrapCommandExecutionAndServices } from "../../lib/services.js"
5
+ import { showError } from "../../lib/errors.js"
6
+ import { promptForCeremonySelection } from "../../lib/prompts.js"
7
+
8
+ const listParticipants = async () => {
9
+ try {
10
+ const { firestoreDatabase } = await bootstrapCommandExecutionAndServices()
11
+
12
+ const allCeremonies = await getAllCeremonies(firestoreDatabase)
13
+ const selectedCeremony = await promptForCeremonySelection(
14
+ allCeremonies,
15
+ true,
16
+ "Which ceremony would you like to see participants?"
17
+ )
18
+
19
+ const docRef = doc(firestoreDatabase, commonTerms.collections.ceremonies.name, selectedCeremony.id)
20
+ const participantsRef = collection(docRef, "participants")
21
+ const participantsSnapshot = await getDocs(participantsRef)
22
+ const participants = participantsSnapshot.docs.map(
23
+ (participantDoc) => participantDoc.data() as ParticipantDocument
24
+ )
25
+
26
+ const usersRef = collection(firestoreDatabase, "users")
27
+ const usersSnapshot = await getDocs(usersRef)
28
+ const users = usersSnapshot.docs.map((userDoc) => {
29
+ const data = userDoc.data() as UserDocument
30
+ return { id: userDoc.id, ...data }
31
+ })
32
+
33
+ const participantDetails = participants
34
+ .map((participant) => {
35
+ const user = users.find((_user) => _user.id === participant.userId)
36
+ if (!user) return null
37
+ return {
38
+ id: user.name,
39
+ status: participant.status,
40
+ contributionStep: participant.contributionStep,
41
+ lastUpdated: new Date(participant.lastUpdated)
42
+ }
43
+ })
44
+ .filter((user) => user !== null)
45
+
46
+ const participantsDone = participantDetails.filter((participant) => participant.status === "DONE")
47
+ console.log(participantDetails)
48
+ console.log(`${theme.text.underlined("Total participants:")} ${participantDetails.length}`)
49
+ console.log(`${theme.text.underlined("Total participants finished:")} ${participantsDone.length}`)
50
+ } catch (err: any) {
51
+ showError(`Something went wrong: ${err.toString()}`, true)
52
+ }
53
+ process.exit(0)
54
+ }
55
+
56
+ export default listParticipants
@@ -41,7 +41,7 @@ import {
41
41
  } from "../lib/utils.js"
42
42
  import { COMMAND_ERRORS, showError } from "../lib/errors.js"
43
43
  import { authWithToken, bootstrapCommandExecutionAndServices, checkAuth } from "../lib/services.js"
44
- import { getAttestationLocalFilePath, localPaths } from "../lib/localConfigs.js"
44
+ import { getAttestationLocalFilePath, getLocalAuthMethod, localPaths } from "../lib/localConfigs.js"
45
45
  import theme from "../lib/theme.js"
46
46
  import { checkAndMakeNewDirectoryIfNonexistent, writeFile } from "../lib/files.js"
47
47
 
@@ -281,12 +281,12 @@ export const handleDiskSpaceRequirementForNextContribution = async (
281
281
  )} since is based on the aggregate free memory on your disks but some may not be detected!\n`
282
282
  )
283
283
 
284
- const { confirmation } = await askForConfirmation(
284
+ const { confirmationEnoughMemory } = await askForConfirmation(
285
285
  `Please, we kindly ask you to continue with the contribution if you have noticed the estimate is wrong and you have enough memory in your machine`,
286
286
  "Continue",
287
287
  "Exit"
288
288
  )
289
- wannaContributeOrHaveEnoughMemory = !!confirmation
289
+ wannaContributeOrHaveEnoughMemory = !!confirmationEnoughMemory
290
290
 
291
291
  if (circuitSequencePosition > 1) {
292
292
  console.log(
@@ -419,14 +419,19 @@ export const handlePublicAttestation = async (
419
419
 
420
420
  await sleep(1000) // workaround for file descriptor unexpected close.
421
421
 
422
- const gistUrl = await publishGist(participantAccessToken, publicAttestation, ceremonyName, ceremonyPrefix)
423
-
424
- console.log(
425
- `\n${theme.symbols.info} Your public attestation has been successfully posted as Github Gist (${theme.text.bold(
426
- theme.text.underlined(gistUrl)
427
- )})`
428
- )
422
+ let gistUrl = ""
423
+ const isGithub = getLocalAuthMethod() === "github"
424
+ if (isGithub) {
425
+ gistUrl = await publishGist(participantAccessToken, publicAttestation, ceremonyName, ceremonyPrefix)
429
426
 
427
+ console.log(
428
+ `\n${
429
+ theme.symbols.info
430
+ } Your public attestation has been successfully posted as Github Gist (${theme.text.bold(
431
+ theme.text.underlined(gistUrl)
432
+ )})`
433
+ )
434
+ }
430
435
  // Prepare a ready-to-share tweet.
431
436
  await handleTweetGeneration(ceremonyName, gistUrl)
432
437
  }
@@ -514,6 +519,8 @@ export const listenToCeremonyCircuitDocumentChanges = (
514
519
  })
515
520
  }
516
521
 
522
+ let contributionInProgress = false
523
+
517
524
  /**
518
525
  * Listen to current authenticated participant document changes.
519
526
  * @dev this is the core business logic related to the execution of the contribute command.
@@ -706,6 +713,12 @@ export const listenToParticipantDocumentChanges = async (
706
713
 
707
714
  // Scenario (3.B).
708
715
  if (isCurrentContributor && hasResumableStep && startingOrResumingContribution) {
716
+ if (contributionInProgress) {
717
+ console.warn(
718
+ `\n${theme.symbols.warning} Received instruction to start/resume contribution but contribution is already in progress...[skipping]`
719
+ )
720
+ return
721
+ }
709
722
  // Communicate resume / start of the contribution to participant.
710
723
  await simpleLoader(
711
724
  `${
@@ -715,18 +728,24 @@ export const listenToParticipantDocumentChanges = async (
715
728
  3000
716
729
  )
717
730
 
718
- // Start / Resume the contribution for the participant.
719
- await handleStartOrResumeContribution(
720
- cloudFunctions,
721
- firestoreDatabase,
722
- ceremony,
723
- circuit,
724
- participant,
725
- entropy,
726
- providerUserId,
727
- false, // not finalizing.
728
- circuits.length
729
- )
731
+ try {
732
+ contributionInProgress = true
733
+
734
+ // Start / Resume the contribution for the participant.
735
+ await handleStartOrResumeContribution(
736
+ cloudFunctions,
737
+ firestoreDatabase,
738
+ ceremony,
739
+ circuit,
740
+ participant,
741
+ entropy,
742
+ providerUserId,
743
+ false, // not finalizing.
744
+ circuits.length
745
+ )
746
+ } finally {
747
+ contributionInProgress = false
748
+ }
730
749
  }
731
750
  // Scenario (3.A).
732
751
  else if (isWaitingForContribution)
@@ -897,7 +916,7 @@ const contribute = async (opt: any) => {
897
916
  // Get options.
898
917
  const ceremonyOpt = opt.ceremony
899
918
  const entropyOpt = opt.entropy
900
- const auth = opt.auth
919
+ const { auth } = opt
901
920
 
902
921
  // Check for authentication.
903
922
  const { user, providerUserId, token } = auth ? await authWithToken(firebaseApp, auth) : await checkAuth(firebaseApp)
@@ -938,7 +957,11 @@ const contribute = async (opt: any) => {
938
957
  } else selectedCeremony = selectedCeremonyDocument.at(0)
939
958
  } else {
940
959
  // Prompt the user to select a ceremony from the opened ones.
941
- selectedCeremony = await promptForCeremonySelection(ceremoniesOpenedForContributions, false)
960
+ selectedCeremony = await promptForCeremonySelection(
961
+ ceremoniesOpenedForContributions,
962
+ false,
963
+ "Which ceremony would you like to contribute to?"
964
+ )
942
965
  }
943
966
 
944
967
  // Get selected ceremony circuit(s) documents.
@@ -952,7 +975,7 @@ const contribute = async (opt: any) => {
952
975
  const userData = userDoc.data()
953
976
  if (!userData) {
954
977
  spinner.fail(
955
- `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.`
978
+ `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 eligible 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.`
956
979
  )
957
980
  process.exit(0)
958
981
  }
@@ -74,7 +74,7 @@ export const handleVerificationKey = async (
74
74
  // Write the verification key locally.
75
75
  writeLocalJsonFile(verificationKeyLocalFilePath, vKey)
76
76
 
77
- await sleep(3000) // workaound for file descriptor.
77
+ await sleep(3000) // workaround for file descriptor.
78
78
 
79
79
  // Upload verification key to storage.
80
80
  await multiPartUpload(
@@ -122,7 +122,7 @@ export const handleVerifierSmartContract = async (
122
122
  // Write the verification key locally.
123
123
  writeFile(verifierContractLocalFilePath, verifierCode)
124
124
 
125
- await sleep(3000) // workaound for file descriptor.
125
+ await sleep(3000) // workaround for file descriptor.
126
126
 
127
127
  // Upload verifier smart contract to storage.
128
128
  await multiPartUpload(
@@ -177,7 +177,7 @@ export const handleCircuitFinalization = async (
177
177
  circuitsLength
178
178
  )
179
179
 
180
- await sleep(2000) // workaound for descriptors.
180
+ await sleep(2000) // workaround for descriptors.
181
181
 
182
182
  // Extract data.
183
183
  const { prefix: circuitPrefix } = circuit.data
@@ -248,7 +248,7 @@ const finalize = async (opt: any) => {
248
248
  const { firebaseApp, firebaseFunctions, firestoreDatabase } = await bootstrapCommandExecutionAndServices()
249
249
 
250
250
  // Check for authentication.
251
- const auth = opt.auth
251
+ const { auth } = opt
252
252
  const {
253
253
  user,
254
254
  providerUserId,
@@ -269,7 +269,11 @@ const finalize = async (opt: any) => {
269
269
  )
270
270
 
271
271
  // Prompt for ceremony selection.
272
- const selectedCeremony = await promptForCeremonySelection(ceremoniesClosedForFinalization, true)
272
+ const selectedCeremony = await promptForCeremonySelection(
273
+ ceremoniesClosedForFinalization,
274
+ true,
275
+ "Which ceremony would you like to finalize?"
276
+ )
273
277
 
274
278
  // Get coordinator participant document.
275
279
  let participant = await getDocumentById(
@@ -1,5 +1,7 @@
1
1
  export { default as setup } from "./setup.js"
2
2
  export { default as auth } from "./auth.js"
3
+ export { default as authBandada } from "./authBandada.js"
4
+ export { default as authSIWE } from "./authSIWE.js"
3
5
  export { default as contribute } from "./contribute.js"
4
6
  export { default as observe } from "./observe.js"
5
7
  export { default as finalize } from "./finalize.js"
@@ -6,7 +6,7 @@ import { showError } from "../lib/errors.js"
6
6
  import { askForConfirmation } from "../lib/prompts.js"
7
7
  import { customSpinner, sleep, terminate } from "../lib/utils.js"
8
8
  import theme from "../lib/theme.js"
9
- import { deleteLocalAccessToken } from "../lib/localConfigs.js"
9
+ import { deleteLocalAccessToken, deleteLocalAuthMethod, deleteLocalBandadaIdentity } from "../lib/localConfigs.js"
10
10
 
11
11
  /**
12
12
  * Logout command.
@@ -52,7 +52,9 @@ const logout = async () => {
52
52
  await signOut(auth)
53
53
 
54
54
  // Delete local token.
55
+ deleteLocalAuthMethod()
55
56
  deleteLocalAccessToken()
57
+ deleteLocalBandadaIdentity()
56
58
 
57
59
  await sleep(3000) // ~3s.
58
60
 
@@ -143,11 +143,15 @@ const observe = async () => {
143
143
  // Preserve command execution only for coordinators].
144
144
  if (!(await isCoordinator(user))) showError(COMMAND_ERRORS.COMMAND_NOT_COORDINATOR, true)
145
145
 
146
- // Get running cerimonies info (if any).
146
+ // Get running ceremonies info (if any).
147
147
  const runningCeremoniesDocs = await getOpenedCeremonies(firestoreDatabase)
148
148
 
149
149
  // Ask to select a ceremony.
150
- const ceremony = await promptForCeremonySelection(runningCeremoniesDocs, false)
150
+ const ceremony = await promptForCeremonySelection(
151
+ runningCeremoniesDocs,
152
+ false,
153
+ "Which ceremony would you like to observe?"
154
+ )
151
155
 
152
156
  console.log(`${logSymbols.info} Refresh rate set to ~3 seconds for waiting queue updates\n`)
153
157
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { zKey } from "snarkjs"
4
4
  import boxen from "boxen"
5
- import { createWriteStream, Dirent, renameSync } from "fs"
5
+ import { createWriteStream, Dirent, renameSync, existsSync } from "fs"
6
6
  import { pipeline } from "node:stream"
7
7
  import { promisify } from "node:util"
8
8
  import fetch from "node-fetch"
@@ -181,7 +181,7 @@ export const handleAdditionOfCircuitsToCeremony = async (
181
181
 
182
182
  if (matchingWasms.length !== 1) showError(COMMAND_ERRORS.COMMAND_SETUP_MISMATCH_R1CS_WASM, true)
183
183
 
184
- // Get input data for choosen circuit.
184
+ // Get input data for chosen circuit.
185
185
  const circuitInputData = await getInputDataToAddCircuitToCeremony(
186
186
  choosenCircuitFilename,
187
187
  matchingWasms[0],
@@ -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()
@@ -322,7 +322,7 @@ export const checkAndDownloadSmallestPowersOfTau = async (
322
322
  * number of powers greater than or equal to the powers needed by the zKey), the coordinator will be asked
323
323
  * to provide a number of powers manually, ranging from the smallest possible to the largest.
324
324
  * @param neededPowers <number> - the smallest amount of powers needed by the zKey.
325
- * @returns Promise<string, string> - the information about the choosen Powers of Tau file for the pre-computed zKey
325
+ * @returns Promise<string, string> - the information about the chosen Powers of Tau file for the pre-computed zKey
326
326
  * along with related powers.
327
327
  */
328
328
  export const handlePreComputedZkeyPowersOfTauSelection = async (
@@ -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 }) => {
@@ -537,20 +538,33 @@ const setup = async (cmd: { template?: string; auth?: string }) => {
537
538
  `clock`
538
539
  )
539
540
  spinner.start()
540
- await zKey.newZKey(
541
- r1csLocalPathAndFileName,
542
- getPotLocalFilePath(circuit.files.potFilename),
543
- zkeyLocalPathAndFileName,
544
- undefined
545
- )
546
- spinner.succeed(
547
- `Generation of the genesis zKey for citcui ${theme.text.bold(circuit.name)} completed successfully`
548
- )
549
541
 
542
+ if (existsSync(zkeyLocalPathAndFileName)) {
543
+ spinner.succeed(
544
+ `The genesis zKey for circuit ${theme.text.bold(circuit.name)} is already present on disk`
545
+ )
546
+ } else {
547
+ await zKey.newZKey(
548
+ r1csLocalPathAndFileName,
549
+ getPotLocalFilePath(circuit.files.potFilename),
550
+ zkeyLocalPathAndFileName,
551
+ undefined
552
+ )
553
+ spinner.succeed(
554
+ `Generation of the genesis zKey for circuit ${theme.text.bold(circuit.name)} completed successfully`
555
+ )
556
+ }
557
+
558
+ const hashSpinner = customSpinner(
559
+ `Calculating hashes for circuit ${theme.text.bold(circuit.name)}...`,
560
+ `clock`
561
+ )
562
+ hashSpinner.start()
550
563
  // 4. calculate the hashes
551
564
  const wasmBlake2bHash = await blake512FromPath(wasmLocalPathAndFileName)
552
565
  const potBlake2bHash = await blake512FromPath(getPotLocalFilePath(circuit.files.potFilename))
553
566
  const initialZkeyBlake2bHash = await blake512FromPath(zkeyLocalPathAndFileName)
567
+ hashSpinner.succeed(`Hashes for circuit ${theme.text.bold(circuit.name)} calculated successfully`)
554
568
 
555
569
  // 5. upload the artifacts
556
570
 
@@ -603,9 +617,9 @@ const setup = async (cmd: { template?: string; auth?: string }) => {
603
617
  // 6 update the setup data object
604
618
  ceremonySetupData.circuits[index].files = {
605
619
  ...circuit.files,
606
- potBlake2bHash: potBlake2bHash,
607
- wasmBlake2bHash: wasmBlake2bHash,
608
- initialZkeyBlake2bHash: initialZkeyBlake2bHash
620
+ potBlake2bHash,
621
+ wasmBlake2bHash,
622
+ initialZkeyBlake2bHash
609
623
  }
610
624
 
611
625
  ceremonySetupData.circuits[index].zKeySizeInBytes = getFileStats(zkeyLocalPathAndFileName).size