@devtion/devcli 0.0.0-4088679 → 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.
Files changed (40) hide show
  1. package/README.md +2 -2
  2. package/dist/.env +17 -3
  3. package/dist/index.js +544 -106
  4. package/dist/public/mini-semaphore.wasm +0 -0
  5. package/dist/public/mini-semaphore.zkey +0 -0
  6. package/dist/types/commands/authBandada.d.ts +2 -0
  7. package/dist/types/commands/authSIWE.d.ts +7 -0
  8. package/dist/types/commands/ceremony/index.d.ts +3 -0
  9. package/dist/types/commands/ceremony/listParticipants.d.ts +2 -0
  10. package/dist/types/commands/finalize.d.ts +2 -1
  11. package/dist/types/commands/index.d.ts +2 -0
  12. package/dist/types/commands/setup.d.ts +3 -3
  13. package/dist/types/lib/bandada.d.ts +6 -0
  14. package/dist/types/lib/files.d.ts +0 -1
  15. package/dist/types/lib/localConfigs.d.ts +38 -0
  16. package/dist/types/lib/prompts.d.ts +6 -6
  17. package/dist/types/lib/utils.d.ts +2 -1
  18. package/dist/types/types/index.d.ts +69 -0
  19. package/package.json +12 -5
  20. package/src/commands/auth.ts +32 -11
  21. package/src/commands/authBandada.ts +120 -0
  22. package/src/commands/authSIWE.ts +185 -0
  23. package/src/commands/ceremony/index.ts +20 -0
  24. package/src/commands/ceremony/listParticipants.ts +56 -0
  25. package/src/commands/contribute.ts +51 -25
  26. package/src/commands/finalize.ts +21 -9
  27. package/src/commands/index.ts +3 -1
  28. package/src/commands/listCeremonies.ts +1 -2
  29. package/src/commands/logout.ts +3 -1
  30. package/src/commands/observe.ts +7 -3
  31. package/src/commands/setup.ts +63 -28
  32. package/src/commands/validate.ts +1 -2
  33. package/src/index.ts +33 -13
  34. package/src/lib/bandada.ts +51 -0
  35. package/src/lib/errors.ts +2 -2
  36. package/src/lib/localConfigs.ts +54 -0
  37. package/src/lib/prompts.ts +22 -25
  38. package/src/lib/services.ts +38 -15
  39. package/src/lib/utils.ts +51 -13
  40. package/src/types/index.ts +75 -0
@@ -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,10 +464,10 @@ 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
- const setup = async (cmd: { template?: string, auth?: string}) => {
470
+ const setup = async (cmd: { template?: string; auth?: string }) => {
470
471
  // Setup command state.
471
472
  const circuits: Array<CircuitDocument> = [] // Circuits.
472
473
  let ceremonyId: string = "" // The unique identifier of the ceremony.
@@ -474,8 +475,10 @@ const setup = async (cmd: { template?: string, auth?: string}) => {
474
475
  const { firebaseApp, firebaseFunctions, firestoreDatabase } = await bootstrapCommandExecutionAndServices()
475
476
 
476
477
  // Check for authentication.
477
- const { user, providerUserId } = cmd.auth ? await authWithToken(firebaseApp, cmd.auth) : await checkAuth(firebaseApp)
478
-
478
+ const { user, providerUserId } = cmd.auth
479
+ ? await authWithToken(firebaseApp, cmd.auth)
480
+ : await checkAuth(firebaseApp)
481
+
479
482
  // Preserve command execution only for coordinators.
480
483
  if (!(await isCoordinator(user))) showError(COMMAND_ERRORS.COMMAND_NOT_COORDINATOR, true)
481
484
 
@@ -501,7 +504,7 @@ const setup = async (cmd: { template?: string, auth?: string}) => {
501
504
  // if there is the file option, then set up the non interactively
502
505
  if (cmd.template) {
503
506
  // 1. parse the file
504
- // tmp data - do not cleanup files as we need them
507
+ // tmp data - do not cleanup files as we need them
505
508
  const spinner = customSpinner(`Parsing ${theme.text.bold(cmd.template!)} setup configuration file...`, `clock`)
506
509
  spinner.start()
507
510
  const setupCeremonyData = await parseCeremonyFile(cmd.template!)
@@ -524,18 +527,44 @@ const setup = async (cmd: { template?: string, auth?: string}) => {
524
527
  const zkeyLocalPathAndFileName = getZkeyLocalFilePath(circuit.files.initialZkeyFilename)
525
528
 
526
529
  // 2. download the pot and wasm files
527
- await checkAndDownloadSmallestPowersOfTau(convertToDoubleDigits(circuit.metadata?.pot!), circuit.files.potFilename)
528
-
530
+ await checkAndDownloadSmallestPowersOfTau(
531
+ convertToDoubleDigits(circuit.metadata?.pot!),
532
+ circuit.files.potFilename
533
+ )
534
+
529
535
  // 3. generate the zKey
530
- const spinner = customSpinner(`Generating genesis zKey for circuit ${theme.text.bold(circuit.name)}...`, `clock`)
536
+ const spinner = customSpinner(
537
+ `Generating genesis zKey for circuit ${theme.text.bold(circuit.name)}...`,
538
+ `clock`
539
+ )
531
540
  spinner.start()
532
- await zKey.newZKey(r1csLocalPathAndFileName, getPotLocalFilePath(circuit.files.potFilename), zkeyLocalPathAndFileName, undefined)
533
- spinner.succeed(`Generation of the genesis zKey for citcui ${theme.text.bold(circuit.name)} completed successfully`)
534
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()
535
563
  // 4. calculate the hashes
536
564
  const wasmBlake2bHash = await blake512FromPath(wasmLocalPathAndFileName)
537
565
  const potBlake2bHash = await blake512FromPath(getPotLocalFilePath(circuit.files.potFilename))
538
566
  const initialZkeyBlake2bHash = await blake512FromPath(zkeyLocalPathAndFileName)
567
+ hashSpinner.succeed(`Hashes for circuit ${theme.text.bold(circuit.name)} calculated successfully`)
539
568
 
540
569
  // 5. upload the artifacts
541
570
 
@@ -547,7 +576,7 @@ const setup = async (cmd: { template?: string, auth?: string}) => {
547
576
  zkeyLocalPathAndFileName,
548
577
  circuit.files.initialZkeyFilename
549
578
  )
550
-
579
+
551
580
  // Check if PoT file has been already uploaded to storage.
552
581
  const alreadyUploadedPot = await checkIfObjectExist(
553
582
  firebaseFunctions,
@@ -588,25 +617,31 @@ const setup = async (cmd: { template?: string, auth?: string}) => {
588
617
  // 6 update the setup data object
589
618
  ceremonySetupData.circuits[index].files = {
590
619
  ...circuit.files,
591
- potBlake2bHash: potBlake2bHash,
592
- wasmBlake2bHash: wasmBlake2bHash,
593
- initialZkeyBlake2bHash: initialZkeyBlake2bHash
620
+ potBlake2bHash,
621
+ wasmBlake2bHash,
622
+ initialZkeyBlake2bHash
594
623
  }
595
624
 
596
625
  ceremonySetupData.circuits[index].zKeySizeInBytes = getFileStats(zkeyLocalPathAndFileName).size
597
626
  }
598
-
599
627
 
600
628
  // 7. setup the ceremony
601
- const ceremonyId = await setupCeremony(firebaseFunctions, ceremonySetupData.ceremonyInputData, ceremonySetupData.ceremonyPrefix, ceremonySetupData.circuits)
602
- console.log( `Congratulations, the setup of ceremony ${theme.text.bold(
603
- ceremonySetupData.ceremonyInputData.title
604
- )} (${`UID: ${theme.text.bold(ceremonyId)}`}) has been successfully completed ${
605
- theme.emojis.tada
606
- }. You will be able to find all the files and info respectively in the ceremony bucket and database document.`)
607
-
629
+ const ceremonyId = await setupCeremony(
630
+ firebaseFunctions,
631
+ ceremonySetupData.ceremonyInputData,
632
+ ceremonySetupData.ceremonyPrefix,
633
+ ceremonySetupData.circuits
634
+ )
635
+ console.log(
636
+ `Congratulations, the setup of ceremony ${theme.text.bold(
637
+ ceremonySetupData.ceremonyInputData.title
638
+ )} (${`UID: ${theme.text.bold(ceremonyId)}`}) has been successfully completed ${
639
+ theme.emojis.tada
640
+ }. You will be able to find all the files and info respectively in the ceremony bucket and database document.`
641
+ )
642
+
608
643
  terminate(providerUserId)
609
- }
644
+ }
610
645
 
611
646
  // Look for R1CS files.
612
647
  const r1csFilePaths = await filterDirectoryFilesByExtension(cwd, `.r1cs`)
@@ -4,7 +4,7 @@ import { showError } from "../lib/errors.js"
4
4
  /**
5
5
  * Validate ceremony setup command.
6
6
  */
7
- const validate = async (cmd: { template: string, constraints?: number }) => {
7
+ const validate = async (cmd: { template: string; constraints?: number }) => {
8
8
  try {
9
9
  // parse the file and cleanup after
10
10
  const parsedFile = await parseCeremonyFile(cmd.template, true)
@@ -18,7 +18,6 @@ const validate = async (cmd: { template: string, constraints?: number }) => {
18
18
  }
19
19
 
20
20
  console.log(true)
21
-
22
21
  } catch (err: any) {
23
22
  showError(`${err.toString()}`, false)
24
23
  // we want to exit with a non-zero exit code
package/src/index.ts CHANGED
@@ -4,7 +4,20 @@ import { createCommand } from "commander"
4
4
  import { readFileSync } from "fs"
5
5
  import { dirname } from "path"
6
6
  import { fileURLToPath } from "url"
7
- import { setup, auth, contribute, observe, finalize, clean, logout, validate, listCeremonies } from "./commands/index.js"
7
+ import {
8
+ setup,
9
+ auth,
10
+ authSIWE,
11
+ authBandada,
12
+ contribute,
13
+ observe,
14
+ finalize,
15
+ clean,
16
+ logout,
17
+ validate,
18
+ listCeremonies
19
+ } from "./commands/index.js"
20
+ import setCeremonyCommands from "./commands/ceremony/index.js"
8
21
 
9
22
  // Get pkg info (e.g., name, version).
10
23
  const packagePath = `${dirname(fileURLToPath(import.meta.url))}/..`
@@ -16,6 +29,14 @@ program.name(name).description(description).version(version)
16
29
 
17
30
  // User commands.
18
31
  program.command("auth").description("authenticate yourself using your Github account (OAuth 2.0)").action(auth)
32
+ program
33
+ .command("auth-bandada")
34
+ .description("authenticate yourself in a privacy-perserving manner using Bandada")
35
+ .action(authBandada)
36
+ program
37
+ .command("auth-siwe")
38
+ .description("authenticate yourself using your Ethereum account (Sign In With Ethereum - SIWE)")
39
+ .action(authSIWE)
19
40
  program
20
41
  .command("contribute")
21
42
  .description("compute contributions for a Phase2 Trusted Setup ceremony circuits")
@@ -27,37 +48,34 @@ program
27
48
  .command("clean")
28
49
  .description("clean up output generated by commands from the current working directory")
29
50
  .action(clean)
30
- program
31
- .command("list")
32
- .description("List all ceremonies prefixes")
33
- .action(listCeremonies)
51
+ program.command("list").description("List all ceremonies prefixes").action(listCeremonies)
34
52
  program
35
53
  .command("logout")
36
54
  .description("sign out from Firebae Auth service and delete Github OAuth 2.0 token from local storage")
37
55
  .action(logout)
38
56
  program
39
57
  .command("validate")
40
- .description("Validate that a Ceremony Setup file is correct")
58
+ .description("validate that a Ceremony Setup file is correct")
41
59
  .requiredOption("-t, --template <path>", "The path to the ceremony setup template", "")
42
60
  .option("-c, --constraints <number>", "The number of constraints to check against")
43
61
  .action(validate)
44
62
 
45
63
  // Only coordinator commands.
46
- const ceremony = program.command("coordinate").description("commands for coordinating a ceremony")
64
+ const coordinate = program.command("coordinate").description("commands for coordinating a ceremony")
47
65
 
48
- ceremony
66
+ coordinate
49
67
  .command("setup")
50
68
  .description("setup a Groth16 Phase 2 Trusted Setup ceremony for zk-SNARK circuits")
51
- .option('-t, --template <path>', 'The path to the ceremony setup template', '')
52
- .option('-a, --auth <string>', 'The Github OAuth 2.0 token', '')
69
+ .option("-t, --template <path>", "The path to the ceremony setup template", "")
70
+ .option("-a, --auth <string>", "The Github OAuth 2.0 token", "")
53
71
  .action(setup)
54
-
55
- ceremony
72
+
73
+ coordinate
56
74
  .command("observe")
57
75
  .description("observe in real-time the waiting queue of each ceremony circuit")
58
76
  .action(observe)
59
77
 
60
- ceremony
78
+ coordinate
61
79
  .command("finalize")
62
80
  .description(
63
81
  "finalize a Phase2 Trusted Setup ceremony by applying a beacon, exporting verification key and verifier contract"
@@ -65,4 +83,6 @@ ceremony
65
83
  .option("-a, --auth <string>", "the Github OAuth 2.0 token", "")
66
84
  .action(finalize)
67
85
 
86
+ setCeremonyCommands(program)
87
+
68
88
  program.parseAsync(process.argv)
@@ -0,0 +1,51 @@
1
+ import { ApiSdk, GroupResponse } from "@bandada/api-sdk"
2
+ import { Identity } from "@semaphore-protocol/identity"
3
+ import open from "open"
4
+
5
+ import { askForConfirmation } from "../lib/prompts.js"
6
+ import { showError } from "./errors.js"
7
+ import theme from "../lib/theme.js"
8
+
9
+ const { BANDADA_API_URL } = process.env
10
+
11
+ const bandadaApi = new ApiSdk(BANDADA_API_URL)
12
+
13
+ export const getGroup = async (groupId: string): Promise<GroupResponse | null> => {
14
+ try {
15
+ const group = await bandadaApi.getGroup(groupId)
16
+ return group
17
+ } catch (error: any) {
18
+ showError(`Bandada getGroup error: ${error}`, true)
19
+ return null
20
+ }
21
+ }
22
+
23
+ export const getMembersOfGroup = async (groupId: string): Promise<string[] | null> => {
24
+ try {
25
+ const group = await bandadaApi.getGroup(groupId)
26
+ return group.members
27
+ } catch (error: any) {
28
+ showError(`Bandada getMembersOfGroup error: ${error}`, true)
29
+ return null
30
+ }
31
+ }
32
+
33
+ export const addMemberToGroup = async (groupId: string, dashboardUrl: string, identity: Identity) => {
34
+ const commitment = identity.commitment.toString()
35
+ const group = await bandadaApi.getGroup(groupId)
36
+ const providerName = group.credentials.id.split("_")[0].toLowerCase()
37
+
38
+ // 6. open a new window with the url:
39
+ const url = `${dashboardUrl}credentials?group=${groupId}&member=${commitment}&provider=${providerName}`
40
+ console.log(`${theme.text.bold(`Verification URL:`)} ${theme.text.underlined(url)}`)
41
+ open(url)
42
+
43
+ const { confirmation } = await askForConfirmation("Did you join the Bandada group in the browser?")
44
+ if (!confirmation) showError("You must join the Bandada group to continue the login process", true)
45
+ }
46
+
47
+ export const isGroupMember = async (groupId: string, identity: Identity): Promise<boolean> => {
48
+ const commitment = identity.commitment.toString()
49
+ const isMember: boolean = await bandadaApi.isGroupMember(groupId, commitment)
50
+ return isMember
51
+ }
package/src/lib/errors.ts CHANGED
@@ -6,7 +6,7 @@ export const CORE_SERVICES_ERRORS = {
6
6
  FIREBASE_TOKEN_EXPIRED_REMOVED_PERMISSIONS: `The Github authorization has failed due to lack of association between your account and the CLI`,
7
7
  FIREBASE_USER_DISABLED: `The Github account has been suspended by the ceremony coordinator(s), blocking the possibility of contribution. Please, contact them to understand the motivation behind it.`,
8
8
  FIREBASE_FAILED_CREDENTIALS_VERIFICATION: `Firebase cannot verify your Github credentials due to network errors. Please, try once again later.`,
9
- FIREBASE_NETWORK_ERROR: `Unable to reach Firebase due to network erros. Please, try once again later and make sure your Internet connection is stable.`,
9
+ FIREBASE_NETWORK_ERROR: `Unable to reach Firebase due to network errors. Please, try once again later and make sure your Internet connection is stable.`,
10
10
  FIREBASE_CEREMONY_NOT_OPENED: `There are no ceremonies opened to contributions`,
11
11
  FIREBASE_CEREMONY_NOT_CLOSED: `There are no ceremonies ready to finalization`,
12
12
  AWS_CEREMONY_BUCKET_CREATION: `Unable to create a new bucket for the ceremony. Something went wrong during the creation. Please, repeat the process by providing a new ceremony name of the ceremony.`,
@@ -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.`,
@@ -24,6 +24,14 @@ const config = new Conf({
24
24
  accessToken: {
25
25
  type: "string",
26
26
  default: ""
27
+ },
28
+ bandadaIdentity: {
29
+ type: "string",
30
+ default: ""
31
+ },
32
+ authMethod: {
33
+ type: "string",
34
+ default: ""
27
35
  }
28
36
  }
29
37
  })
@@ -91,6 +99,52 @@ export const setLocalAccessToken = (token: string) => config.set("accessToken",
91
99
  */
92
100
  export const deleteLocalAccessToken = () => config.delete("accessToken")
93
101
 
102
+ /**
103
+ * Return the Bandada identity, if present.
104
+ * @returns <string | undefined> - the Bandada identity if present, otherwise undefined.
105
+ */
106
+ export const getLocalBandadaIdentity = (): string | unknown => config.get("bandadaIdentity")
107
+
108
+ /**
109
+ * Check if the Bandada identity exists in the local storage.
110
+ * @returns <boolean>
111
+ */
112
+ export const checkLocalBandadaIdentity = (): boolean => config.has("bandadaIdentity") && !!config.get("bandadaIdentity")
113
+
114
+ /**
115
+ * Set the Bandada identity.
116
+ * @param identity <string> - the Bandada identity to be stored.
117
+ */
118
+ export const setLocalBandadaIdentity = (identity: string) => config.set("bandadaIdentity", identity)
119
+
120
+ /**
121
+ * Delete the stored Bandada identity.
122
+ */
123
+ export const deleteLocalBandadaIdentity = () => config.delete("bandadaIdentity")
124
+
125
+ /**
126
+ * Return the authentication method, if present.
127
+ * @returns <string | undefined> - the authentication method if present, otherwise undefined.
128
+ */
129
+ export const getLocalAuthMethod = (): string | unknown => config.get("authMethod")
130
+
131
+ /**
132
+ * Check if the authentication method exists in the local storage.
133
+ * @returns <boolean>
134
+ */
135
+ export const checkLocalAuthMethod = (): boolean => config.has("authMethod") && !!config.get("authMethod")
136
+
137
+ /**
138
+ * Set the authentication method.
139
+ * @param method <string> - the authentication method to be stored.
140
+ */
141
+ export const setLocalAuthMethod = (method: string) => config.set("authMethod", method)
142
+
143
+ /**
144
+ * Delete the stored authentication method.
145
+ */
146
+ export const deleteLocalAuthMethod = () => config.delete("authMethod")
147
+
94
148
  /**
95
149
  * Get the complete local file path.
96
150
  * @param cwd <string> - the current working directory path.
@@ -203,7 +203,7 @@ export const promptCircomCompiler = async (): Promise<CircomCompilerData> => {
203
203
  * Shows a list of circuits for a single option selection.
204
204
  * @dev the circuit names are derived from local R1CS files.
205
205
  * @param options <Array<string>> - an array of circuits names.
206
- * @returns Promise<string> - the name of the choosen circuit.
206
+ * @returns Promise<string> - the name of the chosen circuit.
207
207
  */
208
208
  export const promptCircuitSelector = async (options: Array<string>): Promise<string> => {
209
209
  const { circuitFilename } = await prompts({
@@ -223,7 +223,7 @@ export const promptCircuitSelector = async (options: Array<string>): Promise<str
223
223
  * Shows a list of standard EC2 VM instance types for a single option selection.
224
224
  * @notice the suggested VM configuration type is calculated based on circuit constraint size.
225
225
  * @param constraintSize <number> - the amount of circuit constraints
226
- * @returns Promise<string> - the name of the choosen VM type.
226
+ * @returns Promise<string> - the name of the chosen VM type.
227
227
  */
228
228
  export const promptVMTypeSelector = async (constraintSize): Promise<string> => {
229
229
  let suggestedConfiguration: number = 0
@@ -325,7 +325,7 @@ export const promptVMDiskTypeSelector = async (): Promise<DiskTypeForVM> => {
325
325
  /**
326
326
  * Show a series of questions about the circuits.
327
327
  * @param constraintSize <number> - the amount of circuit constraints.
328
- * @param timeoutMechanismType <CeremonyTimeoutType> - the choosen timeout mechanism type for the ceremony.
328
+ * @param timeoutMechanismType <CeremonyTimeoutType> - the chosen timeout mechanism type for the ceremony.
329
329
  * @param needPromptCircomCompiler <boolean> - a boolean value indicating if the questions related to the Circom compiler version and commit hash must be asked.
330
330
  * @param enforceVM <boolean> - a boolean value indicating if the contribution verification could be supported by VM-only approach or not.
331
331
  * @returns Promise<Array<Circuit>> - circuit info prompted by the coordinator.
@@ -343,7 +343,7 @@ export const promptCircuitInputData = async (
343
343
  let circomVersion: string = ""
344
344
  let circomCommitHash: string = ""
345
345
  let circuitInputData: CircuitInputData
346
- let useCfOrVm: CircuitContributionVerificationMechanism
346
+ let cfOrVm: CircuitContributionVerificationMechanism
347
347
  let vmDiskType: DiskTypeForVM
348
348
  let vmConfigurationType: string = ""
349
349
 
@@ -422,19 +422,23 @@ export const promptCircuitInputData = async (
422
422
  circomCommitHash = commitHash
423
423
  }
424
424
 
425
- // Ask for prefered contribution verification method (CF vs VM).
425
+ // Ask for preferred contribution verification method (CF vs VM).
426
426
  if (!enforceVM) {
427
427
  const { confirmation } = await askForConfirmation(
428
428
  `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`,
429
429
  `CF`, // eq. true.
430
430
  `VM` // eq. false.
431
431
  )
432
- useCfOrVm = confirmation
433
- } else useCfOrVm = CircuitContributionVerificationMechanism.VM
432
+ cfOrVm = confirmation
433
+ ? CircuitContributionVerificationMechanism.CF
434
+ : CircuitContributionVerificationMechanism.VM
435
+ } else {
436
+ cfOrVm = CircuitContributionVerificationMechanism.VM
437
+ }
434
438
 
435
- if (useCfOrVm === undefined) showError(COMMAND_ERRORS.COMMAND_ABORT_PROMPT, true)
439
+ if (cfOrVm === undefined) showError(COMMAND_ERRORS.COMMAND_ABORT_PROMPT, true)
436
440
 
437
- if (!useCfOrVm) {
441
+ if (cfOrVm === CircuitContributionVerificationMechanism.VM) {
438
442
  // Ask for selecting the specific VM configuration type.
439
443
  vmConfigurationType = await promptVMTypeSelector(constraintSize)
440
444
 
@@ -478,9 +482,7 @@ export const promptCircuitInputData = async (
478
482
  paramsConfiguration: circuitConfigurationValues
479
483
  },
480
484
  verification: {
481
- cfOrVm: useCfOrVm
482
- ? CircuitContributionVerificationMechanism.CF
483
- : CircuitContributionVerificationMechanism.VM,
485
+ cfOrVm,
484
486
  vm: {
485
487
  vmConfigurationType,
486
488
  vmDiskType
@@ -520,9 +522,7 @@ export const promptCircuitInputData = async (
520
522
  paramsConfiguration: circuitConfigurationValues
521
523
  },
522
524
  verification: {
523
- cfOrVm: useCfOrVm
524
- ? CircuitContributionVerificationMechanism.CF
525
- : CircuitContributionVerificationMechanism.VM,
525
+ cfOrVm,
526
526
  vm: {
527
527
  vmConfigurationType,
528
528
  vmDiskType
@@ -586,7 +586,7 @@ export const promptCircuitAddition = async (): Promise<boolean> => {
586
586
  * Shows a list of pre-computed zKeys for a single option selection.
587
587
  * @dev the names are derived from local zKeys files.
588
588
  * @param options <Array<string>> - an array of pre-computed zKeys names.
589
- * @returns Promise<string> - the name of the choosen pre-computed zKey.
589
+ * @returns Promise<string> - the name of the chosen pre-computed zKey.
590
590
  */
591
591
  export const promptPreComputedZkeySelector = async (options: Array<string>): Promise<string> => {
592
592
  const { preComputedZkeyFilename } = await prompts({
@@ -633,13 +633,13 @@ export const promptNeededPowersForCircuit = async (suggestedSmallestNeededPowers
633
633
  * Shows a list of PoT files for a single option selection.
634
634
  * @dev the names are derived from local PoT files.
635
635
  * @param options <Array<string>> - an array of PoT file names.
636
- * @returns Promise<string> - the name of the choosen PoT.
636
+ * @returns Promise<string> - the name of the chosen PoT.
637
637
  */
638
638
  export const promptPotSelector = async (options: Array<string>): Promise<string> => {
639
639
  const { potFilename } = await prompts({
640
640
  type: "select",
641
641
  name: "potFilename",
642
- message: theme.text.bold("Select the Powers of Tau file choosen for the circuit"),
642
+ message: theme.text.bold("Select the Powers of Tau file chosen for the circuit"),
643
643
  choices: options.map((option: string) => {
644
644
  console.log(option)
645
645
  return { title: option, value: option }
@@ -661,7 +661,8 @@ export const promptPotSelector = async (options: Array<string>): Promise<string>
661
661
  */
662
662
  export const promptForCeremonySelection = async (
663
663
  ceremoniesDocuments: Array<FirebaseDocumentInfo>,
664
- isFinalizing: boolean
664
+ isFinalizing: boolean,
665
+ messageToDisplay?: string
665
666
  ): Promise<FirebaseDocumentInfo> => {
666
667
  // Prepare state.
667
668
  const choices: Array<Choice> = []
@@ -686,11 +687,7 @@ export const promptForCeremonySelection = async (
686
687
  const { ceremony } = await prompts({
687
688
  type: "select",
688
689
  name: "ceremony",
689
- message: theme.text.bold(
690
- !isFinalizing
691
- ? "Which ceremony would you like to contribute to?"
692
- : "Which ceremony would you like to finalize?"
693
- ),
690
+ message: theme.text.bold(messageToDisplay),
694
691
  choices,
695
692
  initial: 0
696
693
  })
@@ -731,7 +728,7 @@ export const promptToTypeEntropyOrBeacon = async (isEntropy = true): Promise<str
731
728
  * @return <Promise<string>> - the entropy.
732
729
  */
733
730
  export const promptForEntropy = async (): Promise<string> => {
734
- // Prompt for entropy generation prefered method.
731
+ // Prompt for entropy generation preferred method.
735
732
  const { confirmation } = await askForConfirmation(
736
733
  `Do you prefer to type your entropy or generate it randomly?`,
737
734
  "Manually",
@@ -6,13 +6,18 @@ import {
6
6
  import clear from "clear"
7
7
  import figlet from "figlet"
8
8
  import { FirebaseApp } from "firebase/app"
9
- import { OAuthCredential } from "firebase/auth"
9
+ import { OAuthCredential, getAuth, signInWithCustomToken } from "firebase/auth"
10
10
  import dotenv from "dotenv"
11
11
  import { fileURLToPath } from "url"
12
12
  import { dirname } from "path"
13
13
  import { AuthUser } from "../types/index.js"
14
14
  import { CONFIG_ERRORS, CORE_SERVICES_ERRORS, showError, THIRD_PARTY_SERVICES_ERRORS } from "./errors.js"
15
- import { checkLocalAccessToken, deleteLocalAccessToken, getLocalAccessToken } from "./localConfigs.js"
15
+ import {
16
+ checkLocalAccessToken,
17
+ deleteLocalAccessToken,
18
+ getLocalAccessToken,
19
+ getLocalAuthMethod
20
+ } from "./localConfigs.js"
16
21
  import theme from "./theme.js"
17
22
  import { exchangeGithubTokenForCredentials, getGithubProviderUserId, getUserHandleFromProviderUserId } from "./utils.js"
18
23
 
@@ -117,8 +122,6 @@ export const signInToFirebase = async (firebaseApp: FirebaseApp, credentials: OA
117
122
  }
118
123
  }
119
124
 
120
-
121
-
122
125
  /**
123
126
  * Ensure that the callee is an authenticated user.
124
127
  * @notice The token will be passed as parameter.
@@ -166,22 +169,42 @@ export const checkAuth = async (firebaseApp: FirebaseApp): Promise<AuthUser> =>
166
169
  // Retrieve local access token.
167
170
  const token = String(getLocalAccessToken())
168
171
 
169
- // Get credentials.
170
- const credentials = exchangeGithubTokenForCredentials(token)
171
-
172
- // Sign in to Firebase using credentials.
173
- await signInToFirebase(firebaseApp, credentials)
172
+ let providerUserId: string
173
+ let username: string
174
+ const authMethod = getLocalAuthMethod()
175
+ switch (authMethod) {
176
+ case "github": {
177
+ // Get credentials.
178
+ const credentials = exchangeGithubTokenForCredentials(token)
179
+ // Sign in to Firebase using credentials.
180
+ await signInToFirebase(firebaseApp, credentials)
181
+ // Get Github unique identifier (handle-id).
182
+ providerUserId = await getGithubProviderUserId(String(token))
183
+ username = getUserHandleFromProviderUserId(providerUserId)
184
+ break
185
+ }
186
+ case "bandada": {
187
+ const userCredentials = await signInWithCustomToken(getAuth(), token)
188
+ providerUserId = userCredentials.user.uid
189
+ username = providerUserId
190
+ break
191
+ }
192
+ case "siwe": {
193
+ const userCredentials = await signInWithCustomToken(getAuth(), token)
194
+ providerUserId = userCredentials.user.uid
195
+ username = providerUserId
196
+ break
197
+ }
198
+ default: {
199
+ break
200
+ }
201
+ }
174
202
 
175
203
  // Get current authenticated user.
176
204
  const user = getCurrentFirebaseAuthUser(firebaseApp)
177
205
 
178
- // Get Github unique identifier (handle-id).
179
- const providerUserId = await getGithubProviderUserId(String(token))
180
-
181
206
  // Greet the user.
182
- console.log(
183
- `Greetings, @${theme.text.bold(getUserHandleFromProviderUserId(providerUserId))} ${theme.emojis.wave}\n`
184
- )
207
+ console.log(`Greetings, @${theme.text.bold(username)} ${theme.emojis.wave}\n`)
185
208
 
186
209
  return {
187
210
  user,