@devtion/devcli 0.0.0-56491a8 → 0.0.0-57a8ab9

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,2 @@
1
+ declare const authBandada: () => Promise<never>;
2
+ export default authBandada;
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ declare const setCeremonyCommands: (program: Command) => Command;
3
+ export default setCeremonyCommands;
@@ -0,0 +1,2 @@
1
+ declare const listParticipants: () => Promise<never>;
2
+ export default listParticipants;
@@ -36,8 +36,9 @@ export declare const handleVerifierSmartContract: (cloudFunctions: Functions, bu
36
36
  * @param participant <FirebaseDocumentInfo> - the Firestore document of the participant (coordinator).
37
37
  * @param beacon <string> - the value used to compute the final contribution while finalizing the ceremony.
38
38
  * @param coordinatorIdentifier <string> - the identifier of the coordinator.
39
+ * @param circuitsLength <number> - the number of circuits in the ceremony.
39
40
  */
40
- export declare const handleCircuitFinalization: (cloudFunctions: Functions, firestoreDatabase: Firestore, ceremony: FirebaseDocumentInfo, circuit: FirebaseDocumentInfo, participant: FirebaseDocumentInfo, beacon: string, coordinatorIdentifier: string) => Promise<void>;
41
+ export declare const handleCircuitFinalization: (cloudFunctions: Functions, firestoreDatabase: Firestore, ceremony: FirebaseDocumentInfo, circuit: FirebaseDocumentInfo, participant: FirebaseDocumentInfo, beacon: string, coordinatorIdentifier: string, circuitsLength: number) => Promise<void>;
41
42
  /**
42
43
  * Finalize command.
43
44
  * @notice The finalize command allows a coordinator to finalize a Trusted Setup Phase 2 ceremony by providing the final beacon,
@@ -47,5 +48,5 @@ export declare const handleCircuitFinalization: (cloudFunctions: Functions, fire
47
48
  * @dev For proper execution, the command requires the coordinator to be authenticated with a GitHub account (run auth command first) in order to
48
49
  * handle sybil-resistance and connect to GitHub APIs to publish the gist containing the final public attestation.
49
50
  */
50
- declare const finalize: () => Promise<void>;
51
+ declare const finalize: (opt: any) => Promise<void>;
51
52
  export default finalize;
@@ -1,5 +1,6 @@
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";
@@ -39,7 +39,7 @@ export declare const checkAndDownloadSmallestPowersOfTau: (powers: string, ptauC
39
39
  * number of powers greater than or equal to the powers needed by the zKey), the coordinator will be asked
40
40
  * to provide a number of powers manually, ranging from the smallest possible to the largest.
41
41
  * @param neededPowers <number> - the smallest amount of powers needed by the zKey.
42
- * @returns Promise<string, string> - the information about the choosen Powers of Tau file for the pre-computed zKey
42
+ * @returns Promise<string, string> - the information about the chosen Powers of Tau file for the pre-computed zKey
43
43
  * along with related powers.
44
44
  */
45
45
  export declare const handlePreComputedZkeyPowersOfTauSelection: (neededPowers: number) => Promise<{
@@ -0,0 +1,6 @@
1
+ import { GroupResponse } from "@bandada/api-sdk";
2
+ import { Identity } from "@semaphore-protocol/identity";
3
+ export declare const getGroup: (groupId: string) => Promise<GroupResponse | null>;
4
+ export declare const getMembersOfGroup: (groupId: string) => Promise<string[] | null>;
5
+ export declare const addMemberToGroup: (groupId: string, dashboardUrl: string, identity: Identity) => Promise<void>;
6
+ export declare const isGroupMember: (groupId: string, identity: Identity) => Promise<boolean>;
@@ -1,4 +1,5 @@
1
1
  /// <reference types="node" />
2
+ /// <reference types="node" />
2
3
  import { Dirent, Stats } from "fs";
3
4
  /**
4
5
  * Check a directory path.
@@ -35,6 +35,25 @@ export declare const setLocalAccessToken: (token: string) => void;
35
35
  * Delete the stored access token.
36
36
  */
37
37
  export declare const deleteLocalAccessToken: () => void;
38
+ /**
39
+ * Return the Bandada identity, if present.
40
+ * @returns <string | undefined> - the Bandada identity if present, otherwise undefined.
41
+ */
42
+ export declare const getLocalBandadaIdentity: () => string | unknown;
43
+ /**
44
+ * Check if the Bandada identity exists in the local storage.
45
+ * @returns <boolean>
46
+ */
47
+ export declare const checkLocalBandadaIdentity: () => boolean;
48
+ /**
49
+ * Set the Bandada identity.
50
+ * @param identity <string> - the Bandada identity to be stored.
51
+ */
52
+ export declare const setLocalBandadaIdentity: (identity: string) => void;
53
+ /**
54
+ * Delete the stored Bandada identity.
55
+ */
56
+ export declare const deleteLocalBandadaIdentity: () => void;
38
57
  /**
39
58
  * Get the complete local file path.
40
59
  * @param cwd <string> - the current working directory path.
@@ -24,14 +24,14 @@ export declare const promptCircomCompiler: () => Promise<CircomCompilerData>;
24
24
  * Shows a list of circuits for a single option selection.
25
25
  * @dev the circuit names are derived from local R1CS files.
26
26
  * @param options <Array<string>> - an array of circuits names.
27
- * @returns Promise<string> - the name of the choosen circuit.
27
+ * @returns Promise<string> - the name of the chosen circuit.
28
28
  */
29
29
  export declare const promptCircuitSelector: (options: Array<string>) => Promise<string>;
30
30
  /**
31
31
  * Shows a list of standard EC2 VM instance types for a single option selection.
32
32
  * @notice the suggested VM configuration type is calculated based on circuit constraint size.
33
33
  * @param constraintSize <number> - the amount of circuit constraints
34
- * @returns Promise<string> - the name of the choosen VM type.
34
+ * @returns Promise<string> - the name of the chosen VM type.
35
35
  */
36
36
  export declare const promptVMTypeSelector: (constraintSize: any) => Promise<string>;
37
37
  /**
@@ -42,7 +42,7 @@ export declare const promptVMDiskTypeSelector: () => Promise<DiskTypeForVM>;
42
42
  /**
43
43
  * Show a series of questions about the circuits.
44
44
  * @param constraintSize <number> - the amount of circuit constraints.
45
- * @param timeoutMechanismType <CeremonyTimeoutType> - the choosen timeout mechanism type for the ceremony.
45
+ * @param timeoutMechanismType <CeremonyTimeoutType> - the chosen timeout mechanism type for the ceremony.
46
46
  * @param needPromptCircomCompiler <boolean> - a boolean value indicating if the questions related to the Circom compiler version and commit hash must be asked.
47
47
  * @param enforceVM <boolean> - a boolean value indicating if the contribution verification could be supported by VM-only approach or not.
48
48
  * @returns Promise<Array<Circuit>> - circuit info prompted by the coordinator.
@@ -67,7 +67,7 @@ export declare const promptCircuitAddition: () => Promise<boolean>;
67
67
  * Shows a list of pre-computed zKeys for a single option selection.
68
68
  * @dev the names are derived from local zKeys files.
69
69
  * @param options <Array<string>> - an array of pre-computed zKeys names.
70
- * @returns Promise<string> - the name of the choosen pre-computed zKey.
70
+ * @returns Promise<string> - the name of the chosen pre-computed zKey.
71
71
  */
72
72
  export declare const promptPreComputedZkeySelector: (options: Array<string>) => Promise<string>;
73
73
  /**
@@ -80,7 +80,7 @@ export declare const promptNeededPowersForCircuit: (suggestedSmallestNeededPower
80
80
  * Shows a list of PoT files for a single option selection.
81
81
  * @dev the names are derived from local PoT files.
82
82
  * @param options <Array<string>> - an array of PoT file names.
83
- * @returns Promise<string> - the name of the choosen PoT.
83
+ * @returns Promise<string> - the name of the chosen PoT.
84
84
  */
85
85
  export declare const promptPotSelector: (options: Array<string>) => Promise<string>;
86
86
  /**
@@ -154,5 +154,6 @@ export declare const getLatestUpdatesFromParticipant: (firestoreDatabase: Firest
154
154
  * @param entropyOrBeaconHash <string> - the entropy or beacon hash (only when finalizing) for the contribution.
155
155
  * @param contributorOrCoordinatorIdentifier <string> - the identifier of the contributor or coordinator (only when finalizing).
156
156
  * @param isFinalizing <boolean> - flag to discriminate between ceremony finalization (true) and contribution (false).
157
+ * @param circuitsLength <number> - the total number of circuits in the ceremony.
157
158
  */
158
- export declare const handleStartOrResumeContribution: (cloudFunctions: Functions, firestoreDatabase: Firestore, ceremony: FirebaseDocumentInfo, circuit: FirebaseDocumentInfo, participant: FirebaseDocumentInfo, entropyOrBeaconHash: any, contributorOrCoordinatorIdentifier: string, isFinalizing: boolean) => Promise<void>;
159
+ export declare const handleStartOrResumeContribution: (cloudFunctions: Functions, firestoreDatabase: Firestore, ceremony: FirebaseDocumentInfo, circuit: FirebaseDocumentInfo, participant: FirebaseDocumentInfo, entropyOrBeaconHash: any, contributorOrCoordinatorIdentifier: string, isFinalizing: boolean, circuitsLength: number) => Promise<void>;
@@ -63,3 +63,15 @@ export type GithubGistFile = {
63
63
  raw_url: string;
64
64
  size: number;
65
65
  };
66
+ /**
67
+ * Define the return object of the function that verifies the Bandada membership and proof.
68
+ * @typedef {Object} VerifiedBandadaResponse
69
+ * @property {boolean} valid - true if the proof is valid and the user is a member of the group; otherwise false.
70
+ * @property {string} message - a message describing the result of the verification.
71
+ * @property {string} token - the custom access token.
72
+ */
73
+ export type VerifiedBandadaResponse = {
74
+ valid: boolean;
75
+ message: string;
76
+ token: string;
77
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@devtion/devcli",
3
3
  "type": "module",
4
- "version": "0.0.0-56491a8",
4
+ "version": "0.0.0-57a8ab9",
5
5
  "description": "All-in-one interactive command-line for interfacing with zkSNARK Phase 2 Trusted Setup ceremonies",
6
6
  "repository": "git@github.com:privacy-scaling-explorations/p0tion.git",
7
7
  "homepage": "https://github.com/privacy-scaling-explorations/p0tion",
@@ -34,11 +34,13 @@
34
34
  "build:watch": "rollup -c rollup.config.ts -w --configPlugin typescript",
35
35
  "start": "ts-node --esm ./src/index.ts",
36
36
  "auth": "yarn start auth",
37
+ "auth:bandada": "yarn start auth-bandada",
37
38
  "contribute": "yarn start contribute",
38
39
  "clean": "yarn start clean",
39
40
  "list": "yarn start list",
40
41
  "logout": "yarn start logout",
41
42
  "validate": "yarn start validate",
43
+ "ceremony:participants": "yarn start ceremony participants",
42
44
  "coordinate:setup": "yarn start coordinate setup",
43
45
  "coordinate:observe": "yarn start coordinate observe",
44
46
  "coordinate:finalize": "yarn start coordinate finalize",
@@ -65,10 +67,12 @@
65
67
  "dependencies": {
66
68
  "@adobe/node-fetch-retry": "^2.2.0",
67
69
  "@aws-sdk/client-s3": "^3.329.0",
70
+ "@bandada/api-sdk": "^1.0.0-beta.1",
68
71
  "@devtion/actions": "latest",
69
72
  "@octokit/auth-oauth-app": "^5.0.5",
70
73
  "@octokit/auth-oauth-device": "^4.0.4",
71
74
  "@octokit/request": "^6.2.3",
75
+ "@semaphore-protocol/identity": "^3.15.1",
72
76
  "blakejs": "^1.2.1",
73
77
  "boxen": "^7.1.0",
74
78
  "chalk": "^5.2.0",
@@ -78,6 +82,7 @@
78
82
  "commander": "^10.0.1",
79
83
  "conf": "^11.0.1",
80
84
  "dotenv": "^16.0.3",
85
+ "ethers": "^6.9.0",
81
86
  "figlet": "^1.6.0",
82
87
  "firebase": "^9.21.0",
83
88
  "log-symbols": "^5.1.0",
@@ -90,12 +95,12 @@
90
95
  "prompts": "^2.4.2",
91
96
  "rimraf": "^5.0.0",
92
97
  "rollup": "^3.21.6",
93
- "snarkjs": "^0.6.11",
98
+ "snarkjs": "0.7.3",
94
99
  "timer-node": "^5.0.7",
95
100
  "winston": "^3.8.2"
96
101
  },
97
102
  "publishConfig": {
98
103
  "access": "public"
99
104
  },
100
- "gitHead": "29b556076a3424c9aaf3a5a35b40df8520fa2217"
105
+ "gitHead": "6bddf60f1121786c19ad4437e0448de4c859b829"
101
106
  }
@@ -63,8 +63,13 @@ export const expirationCountdownForGithubOAuth = (expirationInSeconds: number) =
63
63
  */
64
64
  export const onVerification = async (verification: Verification): Promise<void> => {
65
65
  // Copy code to clipboard.
66
- clipboard.writeSync(verification.user_code)
67
- clipboard.readSync()
66
+ let noClipboard = false
67
+ try {
68
+ clipboard.writeSync(verification.user_code)
69
+ clipboard.readSync()
70
+ } catch (error) {
71
+ noClipboard = true
72
+ }
68
73
 
69
74
  // Display data.
70
75
  console.log(
@@ -73,12 +78,13 @@ export const onVerification = async (verification: Verification): Promise<void>
73
78
  )} on this device to generate a new token and authenticate\n`
74
79
  )
75
80
 
76
- console.log(theme.colors.magenta(figlet.textSync("Code is Below", { font: "ANSI Shadow" })), '\n')
77
-
81
+ console.log(theme.colors.magenta(figlet.textSync("Code is Below", { font: "ANSI Shadow" })), "\n")
82
+
83
+ const message = !noClipboard ? `has been copied to your clipboard (${theme.emojis.clipboard})` : ``
78
84
  console.log(
79
- `${theme.symbols.info} Your auth code: ${theme.text.bold(verification.user_code)} has been copied to your clipboard (${theme.emojis.clipboard} ${
85
+ `${theme.symbols.info} Your auth code: ${theme.text.bold(verification.user_code)} ${message} ${
80
86
  theme.symbols.success
81
- })\n`
87
+ }\n`
82
88
  )
83
89
 
84
90
  const spinner = customSpinner(`Redirecting to Github...`, `clock`)
@@ -86,8 +92,12 @@ export const onVerification = async (verification: Verification): Promise<void>
86
92
 
87
93
  await sleep(10000) // ~10s to make users able to read the CLI.
88
94
 
89
- // Automatically open the page (# Step 2).
90
- await open(verification.verification_uri)
95
+ try {
96
+ // Automatically open the page (# Step 2).
97
+ await open(verification.verification_uri)
98
+ } catch (error: any) {
99
+ console.log(`${theme.symbols.info} Please authenticate via GitHub at ${verification.verification_uri}`)
100
+ }
91
101
 
92
102
  spinner.stop()
93
103
 
@@ -0,0 +1,99 @@
1
+ import { Identity } from "@semaphore-protocol/identity"
2
+
3
+ import { commonTerms } from "@devtion/actions"
4
+ import { httpsCallable } from "firebase/functions"
5
+ import { groth16 } from "snarkjs"
6
+ import path from "path"
7
+ import { getAuth, signInWithCustomToken } from "firebase/auth"
8
+ import theme from "../lib/theme.js"
9
+ import { customSpinner } from "../lib/utils.js"
10
+ import { VerifiedBandadaResponse } from "../types/index.js"
11
+ import { showError } from "../lib/errors.js"
12
+ import { bootstrapCommandExecutionAndServices } from "../lib/services.js"
13
+ import { addMemberToGroup, isGroupMember } from "../lib/bandada.js"
14
+ import {
15
+ checkLocalBandadaIdentity,
16
+ getLocalBandadaIdentity,
17
+ setLocalAccessToken,
18
+ setLocalBandadaIdentity
19
+ } from "../lib/localConfigs.js"
20
+ import prompts from "prompts"
21
+
22
+ const { BANDADA_DASHBOARD_URL, BANDADA_GROUP_ID } = process.env
23
+
24
+ const authBandada = async () => {
25
+ const { firebaseFunctions } = await bootstrapCommandExecutionAndServices()
26
+ const spinner = customSpinner(`Checking identity string for Semaphore...`, `clock`)
27
+ spinner.start()
28
+ // 1. check if _identity string exists in local storage
29
+ let identityString: string | unknown
30
+ const isIdentityStringStored = checkLocalBandadaIdentity()
31
+ if (isIdentityStringStored) {
32
+ identityString = getLocalBandadaIdentity()
33
+ spinner.succeed(`Identity seed found\n`)
34
+ } else {
35
+ spinner.warn(`Identity seed not found\n`)
36
+ // 2. generate a random _identity string and save it in local storage
37
+ const { seed } = await prompts({
38
+ type: "text",
39
+ name: "seed",
40
+ message: theme.text.bold(`Enter a secret string to use as your identity seed in Semaphore:`),
41
+ initial: false
42
+ })
43
+ identityString = seed as string
44
+ setLocalBandadaIdentity(identityString as string)
45
+ }
46
+ // 3. create a semaphore identity with _identity string as a seed
47
+ const identity = new Identity(identityString as string)
48
+
49
+ // 4. check if the user is a member of the group
50
+ console.log(`Checking Bandada membership...`)
51
+ const isMember = await isGroupMember(BANDADA_GROUP_ID, identity)
52
+ if (!isMember) {
53
+ await addMemberToGroup(BANDADA_GROUP_ID, BANDADA_DASHBOARD_URL, identity)
54
+ }
55
+
56
+ // 5. generate a proof that the user owns the commitment.
57
+ spinner.text = `Generating proof of identity...`
58
+ spinner.start()
59
+ // publicSignals = [hash(externalNullifier, identityNullifier), commitment]
60
+ const { proof, publicSignals } = await groth16.fullProve(
61
+ {
62
+ identityTrapdoor: identity.trapdoor,
63
+ identityNullifier: identity.nullifier,
64
+ externalNullifier: BANDADA_GROUP_ID
65
+ },
66
+ path.join(path.resolve(), "/public/mini-semaphore.wasm"),
67
+ path.join(path.resolve(), "/public/mini-semaphore.zkey")
68
+ )
69
+ spinner.succeed(`Proof generated.\n`)
70
+ spinner.text = `Sending proof to verification...`
71
+ spinner.start()
72
+ // 6. send proof to a cloud function that verifies it and checks membership
73
+ const cf = httpsCallable(firebaseFunctions, commonTerms.cloudFunctionsNames.bandadaValidateProof)
74
+ const result = await cf({
75
+ proof,
76
+ publicSignals
77
+ })
78
+ const { valid, token, message } = result.data as VerifiedBandadaResponse
79
+ if (!valid) {
80
+ showError(message, true)
81
+ }
82
+ spinner.succeed(`Proof verified.\n`)
83
+ spinner.text = `Authenticating...`
84
+ spinner.start()
85
+ // 7. Auth to p0tion firebase
86
+ const userCredentials = await signInWithCustomToken(getAuth(), token)
87
+ setLocalAccessToken(token)
88
+ spinner.succeed(`Authenticated as ${theme.text.bold(userCredentials.user.uid)}.`)
89
+
90
+ console.log(
91
+ `\n${theme.symbols.warning} You can always log out by running the ${theme.text.bold(
92
+ `phase2cli logout`
93
+ )} command`
94
+ )
95
+
96
+ process.exit(0)
97
+ }
98
+
99
+ export default authBandada
@@ -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,30 @@
1
+ import { collection, doc, getDocs } from "firebase/firestore"
2
+ import { commonTerms, getAllCeremonies } from "@devtion/actions"
3
+ import { bootstrapCommandExecutionAndServices } from "../../lib/services.js"
4
+ import { showError } from "../../lib/errors.js"
5
+ import { promptForCeremonySelection } from "../../lib/prompts.js"
6
+
7
+ const listParticipants = async () => {
8
+ try {
9
+ const { firestoreDatabase } = await bootstrapCommandExecutionAndServices()
10
+
11
+ const allCeremonies = await getAllCeremonies(firestoreDatabase)
12
+ const selectedCeremony = await promptForCeremonySelection(allCeremonies, true)
13
+
14
+ const docRef = doc(firestoreDatabase, commonTerms.collections.ceremonies.name, selectedCeremony.id)
15
+ const participantsRef = collection(docRef, "participants")
16
+ const participantsSnapshot = await getDocs(participantsRef)
17
+ const participants = participantsSnapshot.docs.map((participantDoc) => participantDoc.data().userId)
18
+ console.log(participants)
19
+
20
+ /* const usersRef = collection(firestoreDatabase, "users")
21
+ const usersSnapshot = await getDocs(usersRef)
22
+ const users = usersSnapshot.docs.map((userDoc) => userDoc.data())
23
+ console.log(users) */
24
+ } catch (err: any) {
25
+ showError(`Something went wrong: ${err.toString()}`, true)
26
+ }
27
+ process.exit(0)
28
+ }
29
+
30
+ export default listParticipants
@@ -40,8 +40,8 @@ import {
40
40
  estimateParticipantFreeGlobalDiskSpace
41
41
  } from "../lib/utils.js"
42
42
  import { COMMAND_ERRORS, showError } from "../lib/errors.js"
43
- import { bootstrapCommandExecutionAndServices, checkAuth } from "../lib/services.js"
44
- import { getAttestationLocalFilePath, localPaths } from "../lib/localConfigs.js"
43
+ import { authWithToken, bootstrapCommandExecutionAndServices, checkAuth } from "../lib/services.js"
44
+ import { checkLocalBandadaIdentity, getAttestationLocalFilePath, localPaths } from "../lib/localConfigs.js"
45
45
  import theme from "../lib/theme.js"
46
46
  import { checkAndMakeNewDirectoryIfNonexistent, writeFile } from "../lib/files.js"
47
47
 
@@ -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 isBandada = checkLocalBandadaIdentity()
424
+ if (!isBandada) {
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
  }
@@ -724,7 +729,8 @@ export const listenToParticipantDocumentChanges = async (
724
729
  participant,
725
730
  entropy,
726
731
  providerUserId,
727
- false // not finalizing.
732
+ false, // not finalizing.
733
+ circuits.length
728
734
  )
729
735
  }
730
736
  // Scenario (3.A).
@@ -810,7 +816,9 @@ export const listenToParticipantDocumentChanges = async (
810
816
  await getLatestVerificationResult(firestoreDatabase, ceremony.id, circuit.id, participant.id)
811
817
 
812
818
  // Get next circuit for contribution.
813
- const nextCircuit = timeoutExpired ? getCircuitBySequencePosition(circuits, changedContributionProgress) : getCircuitBySequencePosition(circuits, changedContributionProgress + 1)
819
+ const nextCircuit = timeoutExpired
820
+ ? getCircuitBySequencePosition(circuits, changedContributionProgress)
821
+ : getCircuitBySequencePosition(circuits, changedContributionProgress + 1)
814
822
 
815
823
  // Check disk space requirements for participant.
816
824
  const wannaGenerateAttestation = await handleDiskSpaceRequirementForNextContribution(
@@ -891,12 +899,13 @@ export const listenToParticipantDocumentChanges = async (
891
899
  const contribute = async (opt: any) => {
892
900
  const { firebaseApp, firebaseFunctions, firestoreDatabase } = await bootstrapCommandExecutionAndServices()
893
901
 
894
- // Check for authentication.
895
- const { user, providerUserId, token } = await checkAuth(firebaseApp)
896
-
897
902
  // Get options.
898
903
  const ceremonyOpt = opt.ceremony
899
904
  const entropyOpt = opt.entropy
905
+ const { auth } = opt
906
+
907
+ // Check for authentication.
908
+ const { user, providerUserId, token } = auth ? await authWithToken(firebaseApp, auth) : await checkAuth(firebaseApp)
900
909
 
901
910
  // Prepare data.
902
911
  let selectedCeremony: FirebaseDocumentInfo
@@ -948,7 +957,7 @@ const contribute = async (opt: any) => {
948
957
  const userData = userDoc.data()
949
958
  if (!userData) {
950
959
  spinner.fail(
951
- `Unfortunately we could not find a user document with your information. This likely means that you did not pass the GitHub reputation checks and therefore are not elegible to contribute to any ceremony. Please contact the coordinator if you believe this to be an error.`
960
+ `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.`
952
961
  )
953
962
  process.exit(0)
954
963
  }
@@ -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"
@@ -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