@devtion/devcli 0.0.0-5d170d3 → 0.0.0-67a4629

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 +3 -1
  2. package/dist/.env +10 -3
  3. package/dist/index.js +315 -104
  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/ceremony/index.d.ts +3 -0
  8. package/dist/types/commands/ceremony/listParticipants.d.ts +2 -0
  9. package/dist/types/commands/contribute.d.ts +1 -1
  10. package/dist/types/commands/finalize.d.ts +4 -3
  11. package/dist/types/commands/index.d.ts +1 -0
  12. package/dist/types/commands/observe.d.ts +1 -1
  13. package/dist/types/commands/setup.d.ts +2 -2
  14. package/dist/types/lib/bandada.d.ts +6 -0
  15. package/dist/types/lib/files.d.ts +1 -0
  16. package/dist/types/lib/localConfigs.d.ts +19 -0
  17. package/dist/types/lib/prompts.d.ts +6 -6
  18. package/dist/types/lib/utils.d.ts +3 -2
  19. package/dist/types/types/index.d.ts +12 -0
  20. package/package.json +11 -4
  21. package/src/commands/auth.ts +21 -7
  22. package/src/commands/authBandada.ts +110 -0
  23. package/src/commands/ceremony/index.ts +20 -0
  24. package/src/commands/ceremony/listParticipants.ts +30 -0
  25. package/src/commands/contribute.ts +25 -17
  26. package/src/commands/finalize.ts +22 -13
  27. package/src/commands/index.ts +2 -1
  28. package/src/commands/listCeremonies.ts +2 -3
  29. package/src/commands/logout.ts +2 -1
  30. package/src/commands/observe.ts +3 -3
  31. package/src/commands/setup.ts +56 -45
  32. package/src/commands/validate.ts +2 -3
  33. package/src/index.ts +30 -13
  34. package/src/lib/bandada.ts +51 -0
  35. package/src/lib/errors.ts +1 -1
  36. package/src/lib/localConfigs.ts +28 -1
  37. package/src/lib/prompts.ts +20 -20
  38. package/src/lib/services.ts +27 -16
  39. package/src/lib/utils.ts +45 -10
  40. package/src/types/index.ts +13 -0
@@ -22,7 +22,7 @@ import {
22
22
  exportVerifierContract,
23
23
  FirebaseDocumentInfo,
24
24
  exportVkey
25
- } from "@p0tion/actions"
25
+ } from "@devtion/actions"
26
26
  import { Functions } from "firebase/functions"
27
27
  import { Firestore } from "firebase/firestore"
28
28
  import { dirname } from "path"
@@ -36,9 +36,9 @@ import {
36
36
  sleep,
37
37
  terminate
38
38
  } from "../lib/utils.js"
39
- import { bootstrapCommandExecutionAndServices, checkAuth } from "../lib/services.js"
39
+ import { authWithToken, bootstrapCommandExecutionAndServices, checkAuth } from "../lib/services.js"
40
40
  import {
41
- getAttestationLocalFilePath,
41
+ getFinalAttestationLocalFilePath,
42
42
  getFinalZkeyLocalFilePath,
43
43
  getVerificationKeyLocalFilePath,
44
44
  getVerifierContractLocalFilePath,
@@ -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(
@@ -112,7 +112,7 @@ export const handleVerifierSmartContract = async (
112
112
  ? `${dirname(
113
113
  fileURLToPath(import.meta.url)
114
114
  )}/../../../../node_modules/snarkjs/templates/verifier_groth16.sol.ejs`
115
- : `${dirname(fileURLToPath(import.meta.url))}/../../../node_modules/snarkjs/templates/verifier_groth16.sol.ejs`
115
+ : `${dirname(fileURLToPath(import.meta.url))}/../node_modules/snarkjs/templates/verifier_groth16.sol.ejs`
116
116
 
117
117
  // Export the Solidity verifier smart contract.
118
118
  const verifierCode = await exportVerifierContract(finalZkeyLocalFilePath, verifierPath)
@@ -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(
@@ -152,6 +152,7 @@ export const handleVerifierSmartContract = async (
152
152
  * @param participant <FirebaseDocumentInfo> - the Firestore document of the participant (coordinator).
153
153
  * @param beacon <string> - the value used to compute the final contribution while finalizing the ceremony.
154
154
  * @param coordinatorIdentifier <string> - the identifier of the coordinator.
155
+ * @param circuitsLength <number> - the number of circuits in the ceremony.
155
156
  */
156
157
  export const handleCircuitFinalization = async (
157
158
  cloudFunctions: Functions,
@@ -160,7 +161,8 @@ export const handleCircuitFinalization = async (
160
161
  circuit: FirebaseDocumentInfo,
161
162
  participant: FirebaseDocumentInfo,
162
163
  beacon: string,
163
- coordinatorIdentifier: string
164
+ coordinatorIdentifier: string,
165
+ circuitsLength: number
164
166
  ) => {
165
167
  // Step (1).
166
168
  await handleStartOrResumeContribution(
@@ -171,10 +173,11 @@ export const handleCircuitFinalization = async (
171
173
  participant,
172
174
  computeSHA256ToHex(beacon),
173
175
  coordinatorIdentifier,
174
- true
176
+ true,
177
+ circuitsLength
175
178
  )
176
179
 
177
- await sleep(2000) // workaound for descriptors.
180
+ await sleep(2000) // workaround for descriptors.
178
181
 
179
182
  // Extract data.
180
183
  const { prefix: circuitPrefix } = circuit.data
@@ -241,11 +244,16 @@ export const handleCircuitFinalization = async (
241
244
  * @dev For proper execution, the command requires the coordinator to be authenticated with a GitHub account (run auth command first) in order to
242
245
  * handle sybil-resistance and connect to GitHub APIs to publish the gist containing the final public attestation.
243
246
  */
244
- const finalize = async () => {
247
+ const finalize = async (opt: any) => {
245
248
  const { firebaseApp, firebaseFunctions, firestoreDatabase } = await bootstrapCommandExecutionAndServices()
246
249
 
247
250
  // Check for authentication.
248
- const { user, providerUserId, token: coordinatorAccessToken } = await checkAuth(firebaseApp)
251
+ const { auth } = opt
252
+ const {
253
+ user,
254
+ providerUserId,
255
+ token: coordinatorAccessToken
256
+ } = auth ? await authWithToken(firebaseApp, auth) : await checkAuth(firebaseApp)
249
257
 
250
258
  // Preserve command execution only for coordinators.
251
259
  if (!(await isCoordinator(user))) showError(COMMAND_ERRORS.COMMAND_NOT_COORDINATOR, true)
@@ -306,7 +314,8 @@ const finalize = async () => {
306
314
  circuit,
307
315
  participant,
308
316
  beacon,
309
- providerUserId
317
+ providerUserId,
318
+ circuits.length
310
319
  )
311
320
 
312
321
  process.stdout.write(`\n`)
@@ -344,7 +353,7 @@ const finalize = async () => {
344
353
 
345
354
  // Write public attestation locally.
346
355
  writeFile(
347
- getAttestationLocalFilePath(
356
+ getFinalAttestationLocalFilePath(
348
357
  `${prefix}_${finalContributionIndex}_${commonTerms.foldersAndPathsTerms.attestation}.log`
349
358
  ),
350
359
  Buffer.from(publicAttestation)
@@ -1,9 +1,10 @@
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"
3
4
  export { default as contribute } from "./contribute.js"
4
5
  export { default as observe } from "./observe.js"
5
6
  export { default as finalize } from "./finalize.js"
6
7
  export { default as clean } from "./clean.js"
7
8
  export { default as logout } from "./logout.js"
8
9
  export { default as validate } from "./validate.js"
9
- export { default as listCeremonies} from "./listCeremonies.js"
10
+ export { default as listCeremonies } from "./listCeremonies.js"
@@ -1,4 +1,4 @@
1
- import { commonTerms, getAllCollectionDocs } from "@p0tion/actions"
1
+ import { commonTerms, getAllCollectionDocs } from "@devtion/actions"
2
2
  import { showError } from "../lib/errors.js"
3
3
  import { bootstrapCommandExecutionAndServices } from "../lib/services.js"
4
4
 
@@ -17,11 +17,10 @@ const listCeremonies = async () => {
17
17
 
18
18
  // loop through all ceremonies
19
19
  for (const ceremony of ceremonies) names.push(ceremony.data().prefix)
20
-
20
+
21
21
  // print them to the console
22
22
  console.log(names.join(", "))
23
23
  process.exit(0)
24
-
25
24
  } catch (err: any) {
26
25
  showError(`${err.toString()}`, false)
27
26
  // we want to exit with a non-zero exit code
@@ -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, deleteLocalBandadaIdentity } from "../lib/localConfigs.js"
10
10
 
11
11
  /**
12
12
  * Logout command.
@@ -53,6 +53,7 @@ const logout = async () => {
53
53
 
54
54
  // Delete local token.
55
55
  deleteLocalAccessToken()
56
+ deleteLocalBandadaIdentity()
56
57
 
57
58
  await sleep(3000) // ~3s.
58
59
 
@@ -7,7 +7,7 @@ import {
7
7
  getOpenedCeremonies,
8
8
  isCoordinator,
9
9
  convertToDoubleDigits
10
- } from "@p0tion/actions"
10
+ } from "@devtion/actions"
11
11
  import { Firestore } from "firebase/firestore"
12
12
  import logSymbols from "log-symbols"
13
13
  import readline from "readline"
@@ -15,7 +15,7 @@ import { COMMAND_ERRORS, GENERIC_ERRORS, showError } from "../lib/errors.js"
15
15
  import { promptForCeremonySelection } from "../lib/prompts.js"
16
16
  import { bootstrapCommandExecutionAndServices, checkAuth } from "../lib/services.js"
17
17
  import theme from "../lib/theme.js"
18
- import {customSpinner, getSecondsMinutesHoursFromMillis, sleep } from "../lib/utils.js"
18
+ import { customSpinner, getSecondsMinutesHoursFromMillis, sleep } from "../lib/utils.js"
19
19
 
20
20
  /**
21
21
  * Clean cursor lines from current position back to root (default: zero).
@@ -143,7 +143,7 @@ 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.
@@ -2,12 +2,11 @@
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"
9
9
  import { Functions } from "firebase/functions"
10
- import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3"
11
10
  import {
12
11
  CeremonyTimeoutType,
13
12
  CircomCompilerData,
@@ -37,7 +36,7 @@ import {
37
36
  setupCeremony,
38
37
  parseCeremonyFile,
39
38
  CircuitContributionVerificationMechanism
40
- } from "@p0tion/actions"
39
+ } from "@devtion/actions"
41
40
  import { customSpinner, simpleLoader, sleep, terminate } from "../lib/utils.js"
42
41
  import {
43
42
  promptCeremonyInputData,
@@ -63,7 +62,6 @@ import {
63
62
  getFileStats,
64
63
  checkAndMakeNewDirectoryIfNonexistent
65
64
  } from "../lib/files.js"
66
- import { Readable } from "stream"
67
65
 
68
66
  /**
69
67
  * Handle whatever is needed to obtain the input data for a circuit that the coordinator would like to add to the ceremony.
@@ -183,7 +181,7 @@ export const handleAdditionOfCircuitsToCeremony = async (
183
181
 
184
182
  if (matchingWasms.length !== 1) showError(COMMAND_ERRORS.COMMAND_SETUP_MISMATCH_R1CS_WASM, true)
185
183
 
186
- // Get input data for choosen circuit.
184
+ // Get input data for chosen circuit.
187
185
  const circuitInputData = await getInputDataToAddCircuitToCeremony(
188
186
  choosenCircuitFilename,
189
187
  matchingWasms[0],
@@ -324,7 +322,7 @@ export const checkAndDownloadSmallestPowersOfTau = async (
324
322
  * number of powers greater than or equal to the powers needed by the zKey), the coordinator will be asked
325
323
  * to provide a number of powers manually, ranging from the smallest possible to the largest.
326
324
  * @param neededPowers <number> - the smallest amount of powers needed by the zKey.
327
- * @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
328
326
  * along with related powers.
329
327
  */
330
328
  export const handlePreComputedZkeyPowersOfTauSelection = async (
@@ -468,7 +466,7 @@ export const handleCircuitArtifactUploadToStorage = async (
468
466
  * from Hermez's ceremony Phase 1 Reliable Setup Ceremony.
469
467
  * @param cmd? <any> - the path to the ceremony setup file.
470
468
  */
471
- const setup = async (cmd: { template?: string, auth?: string}) => {
469
+ const setup = async (cmd: { template?: string; auth?: string }) => {
472
470
  // Setup command state.
473
471
  const circuits: Array<CircuitDocument> = [] // Circuits.
474
472
  let ceremonyId: string = "" // The unique identifier of the ceremony.
@@ -476,8 +474,10 @@ const setup = async (cmd: { template?: string, auth?: string}) => {
476
474
  const { firebaseApp, firebaseFunctions, firestoreDatabase } = await bootstrapCommandExecutionAndServices()
477
475
 
478
476
  // Check for authentication.
479
- const { user, providerUserId } = cmd.auth ? await authWithToken(firebaseApp, cmd.auth) : await checkAuth(firebaseApp)
480
-
477
+ const { user, providerUserId } = cmd.auth
478
+ ? await authWithToken(firebaseApp, cmd.auth)
479
+ : await checkAuth(firebaseApp)
480
+
481
481
  // Preserve command execution only for coordinators.
482
482
  if (!(await isCoordinator(user))) showError(COMMAND_ERRORS.COMMAND_NOT_COORDINATOR, true)
483
483
 
@@ -503,7 +503,7 @@ const setup = async (cmd: { template?: string, auth?: string}) => {
503
503
  // if there is the file option, then set up the non interactively
504
504
  if (cmd.template) {
505
505
  // 1. parse the file
506
- // tmp data - do not cleanup files as we need them
506
+ // tmp data - do not cleanup files as we need them
507
507
  const spinner = customSpinner(`Parsing ${theme.text.bold(cmd.template!)} setup configuration file...`, `clock`)
508
508
  spinner.start()
509
509
  const setupCeremonyData = await parseCeremonyFile(cmd.template!)
@@ -516,9 +516,6 @@ const setup = async (cmd: { template?: string, auth?: string}) => {
516
516
  const bucketName = await handleCeremonyBucketCreation(firebaseFunctions, ceremonySetupData.ceremonyPrefix)
517
517
  console.log(`\n${theme.symbols.success} Ceremony bucket name: ${theme.text.bold(bucketName)}`)
518
518
 
519
- // create S3 clienbt
520
- const s3 = new S3Client({region: 'us-east-1'})
521
-
522
519
  // loop through each circuit
523
520
  for await (const circuit of setupCeremonyData.circuits) {
524
521
  // Local paths.
@@ -529,36 +526,44 @@ const setup = async (cmd: { template?: string, auth?: string}) => {
529
526
  const zkeyLocalPathAndFileName = getZkeyLocalFilePath(circuit.files.initialZkeyFilename)
530
527
 
531
528
  // 2. download the pot and wasm files
532
- const streamPipeline = promisify(pipeline)
533
- await checkAndDownloadSmallestPowersOfTau(convertToDoubleDigits(circuit.metadata?.pot!), circuit.files.potFilename)
534
-
535
- // download the wasm to calculate the hash
529
+ await checkAndDownloadSmallestPowersOfTau(
530
+ convertToDoubleDigits(circuit.metadata?.pot!),
531
+ circuit.files.potFilename
532
+ )
533
+
534
+ // 3. generate the zKey
536
535
  const spinner = customSpinner(
537
- `Downloading the ${theme.text.bold(
538
- `#${circuit.name}`
539
- )} WASM file from the project's bucket...`,
536
+ `Generating genesis zKey for circuit ${theme.text.bold(circuit.name)}...`,
540
537
  `clock`
541
538
  )
542
539
  spinner.start()
543
- const command = new GetObjectCommand({ Bucket: ceremonySetupData.circuitArtifacts[index].artifacts.bucket, Key: ceremonySetupData.circuitArtifacts[index].artifacts.wasmStoragePath })
544
-
545
- const response = await s3.send(command)
546
540
 
547
- if (response.$metadata.httpStatusCode !== 200) {
548
- 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.")
541
+ if (existsSync(zkeyLocalPathAndFileName)) {
542
+ spinner.succeed(
543
+ `The genesis zKey for circuit ${theme.text.bold(circuit.name)} is already present on disk`
544
+ )
545
+ } else {
546
+ await zKey.newZKey(
547
+ r1csLocalPathAndFileName,
548
+ getPotLocalFilePath(circuit.files.potFilename),
549
+ zkeyLocalPathAndFileName,
550
+ undefined
551
+ )
552
+ spinner.succeed(
553
+ `Generation of the genesis zKey for circuit ${theme.text.bold(circuit.name)} completed successfully`
554
+ )
549
555
  }
550
556
 
551
- if (response.Body instanceof Readable)
552
- await streamPipeline(response.Body, createWriteStream(wasmLocalPathAndFileName))
553
-
554
- spinner.stop()
555
- // 3. generate the zKey
556
- await zKey.newZKey(r1csLocalPathAndFileName, getPotLocalFilePath(circuit.files.potFilename), zkeyLocalPathAndFileName, undefined)
557
-
557
+ const hashSpinner = customSpinner(
558
+ `Calculating hashes for circuit ${theme.text.bold(circuit.name)}...`,
559
+ `clock`
560
+ )
561
+ hashSpinner.start()
558
562
  // 4. calculate the hashes
559
563
  const wasmBlake2bHash = await blake512FromPath(wasmLocalPathAndFileName)
560
564
  const potBlake2bHash = await blake512FromPath(getPotLocalFilePath(circuit.files.potFilename))
561
565
  const initialZkeyBlake2bHash = await blake512FromPath(zkeyLocalPathAndFileName)
566
+ hashSpinner.succeed(`Hashes for circuit ${theme.text.bold(circuit.name)} calculated successfully`)
562
567
 
563
568
  // 5. upload the artifacts
564
569
 
@@ -570,7 +575,7 @@ const setup = async (cmd: { template?: string, auth?: string}) => {
570
575
  zkeyLocalPathAndFileName,
571
576
  circuit.files.initialZkeyFilename
572
577
  )
573
-
578
+
574
579
  // Check if PoT file has been already uploaded to storage.
575
580
  const alreadyUploadedPot = await checkIfObjectExist(
576
581
  firebaseFunctions,
@@ -611,25 +616,31 @@ const setup = async (cmd: { template?: string, auth?: string}) => {
611
616
  // 6 update the setup data object
612
617
  ceremonySetupData.circuits[index].files = {
613
618
  ...circuit.files,
614
- potBlake2bHash: potBlake2bHash,
615
- wasmBlake2bHash: wasmBlake2bHash,
616
- initialZkeyBlake2bHash: initialZkeyBlake2bHash
619
+ potBlake2bHash,
620
+ wasmBlake2bHash,
621
+ initialZkeyBlake2bHash
617
622
  }
618
623
 
619
624
  ceremonySetupData.circuits[index].zKeySizeInBytes = getFileStats(zkeyLocalPathAndFileName).size
620
625
  }
621
-
622
626
 
623
627
  // 7. setup the ceremony
624
- const ceremonyId = await setupCeremony(firebaseFunctions, ceremonySetupData.ceremonyInputData, ceremonySetupData.ceremonyPrefix, ceremonySetupData.circuits)
625
- console.log( `Congratulations, the setup of ceremony ${theme.text.bold(
626
- ceremonySetupData.ceremonyInputData.title
627
- )} (${`UID: ${theme.text.bold(ceremonyId)}`}) has been successfully completed ${
628
- theme.emojis.tada
629
- }. You will be able to find all the files and info respectively in the ceremony bucket and database document.`)
630
-
628
+ const ceremonyId = await setupCeremony(
629
+ firebaseFunctions,
630
+ ceremonySetupData.ceremonyInputData,
631
+ ceremonySetupData.ceremonyPrefix,
632
+ ceremonySetupData.circuits
633
+ )
634
+ console.log(
635
+ `Congratulations, the setup of ceremony ${theme.text.bold(
636
+ ceremonySetupData.ceremonyInputData.title
637
+ )} (${`UID: ${theme.text.bold(ceremonyId)}`}) has been successfully completed ${
638
+ theme.emojis.tada
639
+ }. You will be able to find all the files and info respectively in the ceremony bucket and database document.`
640
+ )
641
+
631
642
  terminate(providerUserId)
632
- }
643
+ }
633
644
 
634
645
  // Look for R1CS files.
635
646
  const r1csFilePaths = await filterDirectoryFilesByExtension(cwd, `.r1cs`)
@@ -1,10 +1,10 @@
1
- import { parseCeremonyFile } from "@p0tion/actions"
1
+ import { parseCeremonyFile } from "@devtion/actions"
2
2
  import { showError } from "../lib/errors.js"
3
3
 
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,19 @@ 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
+ authBandada,
11
+ contribute,
12
+ observe,
13
+ finalize,
14
+ clean,
15
+ logout,
16
+ validate,
17
+ listCeremonies
18
+ } from "./commands/index.js"
19
+ import setCeremonyCommands from "./commands/ceremony/index.js"
8
20
 
9
21
  // Get pkg info (e.g., name, version).
10
22
  const packagePath = `${dirname(fileURLToPath(import.meta.url))}/..`
@@ -16,51 +28,56 @@ program.name(name).description(description).version(version)
16
28
 
17
29
  // User commands.
18
30
  program.command("auth").description("authenticate yourself using your Github account (OAuth 2.0)").action(auth)
31
+ program
32
+ .command("auth-bandada")
33
+ .description("authenticate yourself in a privacy-perserving manner using Bandada")
34
+ .action(authBandada)
19
35
  program
20
36
  .command("contribute")
21
37
  .description("compute contributions for a Phase2 Trusted Setup ceremony circuits")
22
38
  .option("-c, --ceremony <string>", "the prefix of the ceremony you want to contribute for", "")
23
39
  .option("-e, --entropy <string>", "the entropy (aka toxic waste) of your contribution", "")
40
+ .option("-a, --auth <string>", "the Github OAuth 2.0 token", "")
24
41
  .action(contribute)
25
42
  program
26
43
  .command("clean")
27
44
  .description("clean up output generated by commands from the current working directory")
28
45
  .action(clean)
29
- program
30
- .command("list")
31
- .description("List all ceremonies prefixes")
32
- .action(listCeremonies)
46
+ program.command("list").description("List all ceremonies prefixes").action(listCeremonies)
33
47
  program
34
48
  .command("logout")
35
49
  .description("sign out from Firebae Auth service and delete Github OAuth 2.0 token from local storage")
36
50
  .action(logout)
37
51
  program
38
52
  .command("validate")
39
- .description("Validate that a Ceremony Setup file is correct")
53
+ .description("validate that a Ceremony Setup file is correct")
40
54
  .requiredOption("-t, --template <path>", "The path to the ceremony setup template", "")
41
55
  .option("-c, --constraints <number>", "The number of constraints to check against")
42
56
  .action(validate)
43
57
 
44
58
  // Only coordinator commands.
45
- const ceremony = program.command("coordinate").description("commands for coordinating a ceremony")
59
+ const coordinate = program.command("coordinate").description("commands for coordinating a ceremony")
46
60
 
47
- ceremony
61
+ coordinate
48
62
  .command("setup")
49
63
  .description("setup a Groth16 Phase 2 Trusted Setup ceremony for zk-SNARK circuits")
50
- .option('-t, --template <path>', 'The path to the ceremony setup template', '')
51
- .option('-a, --auth <string>', 'The Github OAuth 2.0 token', '')
64
+ .option("-t, --template <path>", "The path to the ceremony setup template", "")
65
+ .option("-a, --auth <string>", "The Github OAuth 2.0 token", "")
52
66
  .action(setup)
53
-
54
- ceremony
67
+
68
+ coordinate
55
69
  .command("observe")
56
70
  .description("observe in real-time the waiting queue of each ceremony circuit")
57
71
  .action(observe)
58
72
 
59
- ceremony
73
+ coordinate
60
74
  .command("finalize")
61
75
  .description(
62
76
  "finalize a Phase2 Trusted Setup ceremony by applying a beacon, exporting verification key and verifier contract"
63
77
  )
78
+ .option("-a, --auth <string>", "the Github OAuth 2.0 token", "")
64
79
  .action(finalize)
65
80
 
81
+ setCeremonyCommands(program)
82
+
66
83
  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.`,
@@ -1,4 +1,4 @@
1
- import { commonTerms } from "@p0tion/actions"
1
+ import { commonTerms } from "@devtion/actions"
2
2
  import Conf from "conf"
3
3
  import { dirname } from "path"
4
4
  import { readFileSync } from "fs"
@@ -24,6 +24,10 @@ const config = new Conf({
24
24
  accessToken: {
25
25
  type: "string",
26
26
  default: ""
27
+ },
28
+ bandadaIdentity: {
29
+ type: "string",
30
+ default: ""
27
31
  }
28
32
  }
29
33
  })
@@ -91,6 +95,29 @@ export const setLocalAccessToken = (token: string) => config.set("accessToken",
91
95
  */
92
96
  export const deleteLocalAccessToken = () => config.delete("accessToken")
93
97
 
98
+ /**
99
+ * Return the Bandada identity, if present.
100
+ * @returns <string | undefined> - the Bandada identity if present, otherwise undefined.
101
+ */
102
+ export const getLocalBandadaIdentity = (): string | unknown => config.get("bandadaIdentity")
103
+
104
+ /**
105
+ * Check if the Bandada identity exists in the local storage.
106
+ * @returns <boolean>
107
+ */
108
+ export const checkLocalBandadaIdentity = (): boolean => config.has("bandadaIdentity") && !!config.get("bandadaIdentity")
109
+
110
+ /**
111
+ * Set the Bandada identity.
112
+ * @param identity <string> - the Bandada identity to be stored.
113
+ */
114
+ export const setLocalBandadaIdentity = (identity: string) => config.set("bandadaIdentity", identity)
115
+
116
+ /**
117
+ * Delete the stored Bandada identity.
118
+ */
119
+ export const deleteLocalBandadaIdentity = () => config.delete("bandadaIdentity")
120
+
94
121
  /**
95
122
  * Get the complete local file path.
96
123
  * @param cwd <string> - the current working directory path.