@devtion/devcli 0.0.0-5d170d3 → 0.0.0-671e653

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 (42) hide show
  1. package/README.md +3 -1
  2. package/dist/.env +17 -3
  3. package/dist/index.js +516 -108
  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/contribute.d.ts +1 -1
  11. package/dist/types/commands/finalize.d.ts +4 -3
  12. package/dist/types/commands/index.d.ts +2 -0
  13. package/dist/types/commands/observe.d.ts +1 -1
  14. package/dist/types/commands/setup.d.ts +2 -2
  15. package/dist/types/lib/bandada.d.ts +6 -0
  16. package/dist/types/lib/files.d.ts +1 -0
  17. package/dist/types/lib/localConfigs.d.ts +38 -0
  18. package/dist/types/lib/prompts.d.ts +6 -6
  19. package/dist/types/lib/utils.d.ts +3 -2
  20. package/dist/types/types/index.d.ts +63 -0
  21. package/package.json +12 -4
  22. package/src/commands/auth.ts +28 -8
  23. package/src/commands/authBandada.ts +120 -0
  24. package/src/commands/authSIWE.ts +178 -0
  25. package/src/commands/ceremony/index.ts +20 -0
  26. package/src/commands/ceremony/listParticipants.ts +30 -0
  27. package/src/commands/contribute.ts +51 -29
  28. package/src/commands/finalize.ts +22 -13
  29. package/src/commands/index.ts +3 -1
  30. package/src/commands/listCeremonies.ts +2 -3
  31. package/src/commands/logout.ts +3 -1
  32. package/src/commands/observe.ts +3 -3
  33. package/src/commands/setup.ts +56 -45
  34. package/src/commands/validate.ts +2 -3
  35. package/src/index.ts +35 -13
  36. package/src/lib/bandada.ts +51 -0
  37. package/src/lib/errors.ts +1 -1
  38. package/src/lib/localConfigs.ts +55 -1
  39. package/src/lib/prompts.ts +20 -20
  40. package/src/lib/services.ts +39 -16
  41. package/src/lib/utils.ts +45 -10
  42. package/src/types/index.ts +68 -0
@@ -0,0 +1,178 @@
1
+ import open from "open"
2
+ import figlet from "figlet"
3
+ import clipboard from "clipboardy"
4
+ import fetch from "node-fetch"
5
+ import { getAuth, signInWithCustomToken } from "firebase/auth"
6
+ import { httpsCallable } from "firebase/functions"
7
+ import { commonTerms } from "@devtion/actions"
8
+ import { showError } from "../lib/errors.js"
9
+ import { bootstrapCommandExecutionAndServices } from "../lib/services.js"
10
+ import theme from "../lib/theme.js"
11
+ import { customSpinner, sleep } from "../lib/utils.js"
12
+ import { CheckNonceOfSIWEAddressResponse, OAuthDeviceCodeResponse, OAuthTokenResponse } from "../types/index.js"
13
+ import {
14
+ checkLocalAccessToken,
15
+ deleteLocalAccessToken,
16
+ deleteLocalAuthMethod,
17
+ getLocalAccessToken,
18
+ setLocalAccessToken,
19
+ setLocalAuthMethod
20
+ } from "../lib/localConfigs.js"
21
+
22
+ const showVerificationCodeAndUri = async (OAuthDeviceCode: OAuthDeviceCodeResponse) => {
23
+ // Copy code to clipboard.
24
+ let noClipboard = false
25
+ try {
26
+ clipboard.writeSync(OAuthDeviceCode.user_code)
27
+ clipboard.readSync()
28
+ } catch (error) {
29
+ noClipboard = true
30
+ }
31
+ // Display data.
32
+ console.log(
33
+ `${theme.symbols.warning} Visit ${theme.text.bold(
34
+ theme.text.underlined(OAuthDeviceCode.verification_uri)
35
+ )} on this device to generate a new token and authenticate\n`
36
+ )
37
+ console.log(theme.colors.magenta(figlet.textSync("Code is Below", { font: "ANSI Shadow" })), "\n")
38
+
39
+ const message = !noClipboard ? `has been copied to your clipboard (${theme.emojis.clipboard})` : ``
40
+ console.log(
41
+ `${theme.symbols.info} Your auth code: ${theme.text.bold(OAuthDeviceCode.user_code)} ${message} ${
42
+ theme.symbols.success
43
+ }\n`
44
+ )
45
+ const spinner = customSpinner(`Redirecting to Github...`, `clock`)
46
+ spinner.start()
47
+ await sleep(10000) // ~10s to make users able to read the CLI.
48
+ try {
49
+ // Automatically open the page (# Step 2).
50
+ await open(OAuthDeviceCode.verification_uri)
51
+ } catch (error: any) {
52
+ console.log(`${theme.symbols.info} Please authenticate via GitHub at ${OAuthDeviceCode.verification_uri}`)
53
+ }
54
+ spinner.stop()
55
+ }
56
+
57
+ /**
58
+ * Return the token to sign in to Firebase after passing the SIWE Device Flow
59
+ * @param clientId <string> - The client id of the Auth0 application.
60
+ * @param firebaseFunctions <any> - The Firebase functions instance to call the cloud function
61
+ * @returns <string> - The token to sign in to Firebase
62
+ */
63
+ const executeSIWEDeviceFlow = async (clientId: string, firebaseFunctions: any): Promise<string> => {
64
+ // Call Auth0 endpoint to request device code uri
65
+ const OAuthDeviceCode = (await fetch(`${process.env.AUTH0_APPLICATION_URL}/oauth/device/code`, {
66
+ method: "POST",
67
+ headers: { "content-type": "application/json" },
68
+ body: JSON.stringify({
69
+ client_id: clientId,
70
+ scope: "openid",
71
+ audience: `${process.env.AUTH0_APPLICATION_URL}/api/v2/`
72
+ })
73
+ }).then((_res) => _res.json())) as OAuthDeviceCodeResponse
74
+ await showVerificationCodeAndUri(OAuthDeviceCode)
75
+ // Poll Auth0 endpoint until you get token or request expires
76
+ let isSignedIn = false
77
+ let isExpired = false
78
+ let auth0Token = ""
79
+ while (!isSignedIn && !isExpired) {
80
+ // Call Auth0 endpoint to request token
81
+ const OAuthToken = (await fetch(`${process.env.AUTH0_APPLICATION_URL}/oauth/token`, {
82
+ method: "POST",
83
+ headers: { "content-type": "application/json" },
84
+ body: JSON.stringify({
85
+ client_id: clientId,
86
+ device_code: OAuthDeviceCode.device_code,
87
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code"
88
+ })
89
+ }).then((_res) => _res.json())) as OAuthTokenResponse
90
+ if (OAuthToken.error) {
91
+ if (OAuthToken.error === "authorization_pending") {
92
+ // Wait for the user to sign in
93
+ await sleep(OAuthDeviceCode.interval * 1000)
94
+ } else if (OAuthToken.error === "slow_down") {
95
+ // Wait for the user to sign in
96
+ await sleep(OAuthDeviceCode.interval * 1000 * 2)
97
+ } else if (OAuthToken.error === "expired_token") {
98
+ // The user didn't sign in on time
99
+ isExpired = true
100
+ }
101
+ } else {
102
+ // The user signed in
103
+ isSignedIn = true
104
+ auth0Token = OAuthToken.access_token
105
+ }
106
+ }
107
+ // Send token to cloud function to check nonce, create user and retrieve token
108
+ const cf = httpsCallable(firebaseFunctions, commonTerms.cloudFunctionsNames.checkNonceOfSIWEAddress)
109
+ const result = await cf({
110
+ auth0Token
111
+ })
112
+ const { token, valid, message } = result.data as CheckNonceOfSIWEAddressResponse
113
+ if (!valid) {
114
+ showError(message, true)
115
+ deleteLocalAuthMethod()
116
+ deleteLocalAccessToken()
117
+ }
118
+ return token
119
+ }
120
+
121
+ /**
122
+ * Auth command using Sign In With Ethereum
123
+ * @notice The auth command allows a user to make the association of their Ethereum account with the CLI by leveraging SIWE as an authentication mechanism.
124
+ * @dev Under the hood, the command handles a manual Device Flow following the guidelines in the SIWE documentation.
125
+ */
126
+ const authSIWE = async () => {
127
+ try {
128
+ const { firebaseFunctions } = await bootstrapCommandExecutionAndServices()
129
+ // Console more context for the user.
130
+ console.log(
131
+ `${theme.symbols.info} ${theme.text.bold(
132
+ `You are about to authenticate on this CLI using your Ethereum address (device flow - OAuth 2.0 mechanism).\n${theme.symbols.warning} Please, note that only a Sign-in With Ethereum signature will be required`
133
+ )}\n`
134
+ )
135
+ const spinner = customSpinner(`Checking authentication token...`, `clock`)
136
+ spinner.start()
137
+ await sleep(5000)
138
+
139
+ // Manage OAuth Github or SIWE token.
140
+ const isLocalTokenStored = checkLocalAccessToken()
141
+
142
+ if (!isLocalTokenStored) {
143
+ spinner.fail(`No local authentication token found\n`)
144
+
145
+ // Generate a new access token using Github Device Flow (OAuth 2.0).
146
+ const newToken = await executeSIWEDeviceFlow(String(process.env.AUTH_SIWE_CLIENT_ID), firebaseFunctions)
147
+
148
+ // Store the new access token.
149
+ setLocalAuthMethod("siwe")
150
+ setLocalAccessToken(newToken)
151
+ } else spinner.succeed(`Local authentication token found\n`)
152
+
153
+ // Get access token from local store.
154
+ const token = String(getLocalAccessToken())
155
+
156
+ spinner.text = `Authenticating...`
157
+ spinner.start()
158
+
159
+ // Exchange token for credential.
160
+ const credentials = await signInWithCustomToken(getAuth(), token)
161
+ spinner.succeed(`Authenticated as ${theme.text.bold(credentials.user.uid)}.`)
162
+
163
+ console.log(
164
+ `\n${theme.symbols.warning} You can always log out by running the ${theme.text.bold(
165
+ `phase2cli logout`
166
+ )} command`
167
+ )
168
+ process.exit(0)
169
+ } catch (error) {
170
+ // Delete local token.
171
+ console.log("An error crashed the process. Deleting local token and identity.")
172
+ console.error(error)
173
+ deleteLocalAuthMethod()
174
+ deleteLocalAccessToken()
175
+ }
176
+ }
177
+
178
+ export default authSIWE
@@ -0,0 +1,20 @@
1
+ import { Command } from "commander"
2
+ import listParticipants from "./listParticipants.js"
3
+
4
+ const setCeremonyCommands = (program: Command) => {
5
+ const ceremony = program.command("ceremony").description("manage ceremonies")
6
+
7
+ ceremony
8
+ .command("participants")
9
+ .description("retrieve participants list of a ceremony")
10
+ .requiredOption(
11
+ "-c, --ceremony <string>",
12
+ "the prefix of the ceremony you want to retrieve information about",
13
+ ""
14
+ )
15
+ .action(listParticipants)
16
+
17
+ return ceremony
18
+ }
19
+
20
+ export default setCeremonyCommands
@@ -0,0 +1,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
@@ -22,7 +22,7 @@ import {
22
22
  generateValidContributionsAttestation,
23
23
  commonTerms,
24
24
  convertToDoubleDigits
25
- } from "@p0tion/actions"
25
+ } from "@devtion/actions"
26
26
  import { DocumentSnapshot, DocumentData, Firestore, onSnapshot, Timestamp } from "firebase/firestore"
27
27
  import { Functions } from "firebase/functions"
28
28
  import open from "open"
@@ -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 { getAttestationLocalFilePath, getLocalAuthMethod, localPaths } from "../lib/localConfigs.js"
45
45
  import theme from "../lib/theme.js"
46
46
  import { checkAndMakeNewDirectoryIfNonexistent, writeFile } from "../lib/files.js"
47
47
 
@@ -281,12 +281,12 @@ export const handleDiskSpaceRequirementForNextContribution = async (
281
281
  )} since is based on the aggregate free memory on your disks but some may not be detected!\n`
282
282
  )
283
283
 
284
- const { confirmation } = await askForConfirmation(
284
+ const { confirmationEnoughMemory } = await askForConfirmation(
285
285
  `Please, we kindly ask you to continue with the contribution if you have noticed the estimate is wrong and you have enough memory in your machine`,
286
286
  "Continue",
287
287
  "Exit"
288
288
  )
289
- wannaContributeOrHaveEnoughMemory = !!confirmation
289
+ wannaContributeOrHaveEnoughMemory = !!confirmationEnoughMemory
290
290
 
291
291
  if (circuitSequencePosition > 1) {
292
292
  console.log(
@@ -419,14 +419,19 @@ export const handlePublicAttestation = async (
419
419
 
420
420
  await sleep(1000) // workaround for file descriptor unexpected close.
421
421
 
422
- const gistUrl = await publishGist(participantAccessToken, publicAttestation, ceremonyName, ceremonyPrefix)
423
-
424
- console.log(
425
- `\n${theme.symbols.info} Your public attestation has been successfully posted as Github Gist (${theme.text.bold(
426
- theme.text.underlined(gistUrl)
427
- )})`
428
- )
422
+ let gistUrl = ""
423
+ const isGithub = getLocalAuthMethod() === "github"
424
+ if (isGithub) {
425
+ gistUrl = await publishGist(participantAccessToken, publicAttestation, ceremonyName, ceremonyPrefix)
429
426
 
427
+ console.log(
428
+ `\n${
429
+ theme.symbols.info
430
+ } Your public attestation has been successfully posted as Github Gist (${theme.text.bold(
431
+ theme.text.underlined(gistUrl)
432
+ )})`
433
+ )
434
+ }
430
435
  // Prepare a ready-to-share tweet.
431
436
  await handleTweetGeneration(ceremonyName, gistUrl)
432
437
  }
@@ -464,7 +469,6 @@ export const listenToCeremonyCircuitDocumentChanges = (
464
469
  const { fullContribution, verifyCloudFunction } = avgTimings
465
470
  const { currentContributor } = waitingQueue
466
471
 
467
- // Get circuit current contributor participant document.
468
472
  const circuitCurrentContributor = await getDocumentById(
469
473
  firestoreDatabase,
470
474
  getParticipantsCollectionPath(ceremonyId),
@@ -515,6 +519,8 @@ export const listenToCeremonyCircuitDocumentChanges = (
515
519
  })
516
520
  }
517
521
 
522
+ let contributionInProgress = false
523
+
518
524
  /**
519
525
  * Listen to current authenticated participant document changes.
520
526
  * @dev this is the core business logic related to the execution of the contribute command.
@@ -707,6 +713,12 @@ export const listenToParticipantDocumentChanges = async (
707
713
 
708
714
  // Scenario (3.B).
709
715
  if (isCurrentContributor && hasResumableStep && startingOrResumingContribution) {
716
+ if (contributionInProgress) {
717
+ console.warn(
718
+ `\n${theme.symbols.warning} Received instruction to start/resume contribution but contribution is already in progress...[skipping]`
719
+ )
720
+ return
721
+ }
710
722
  // Communicate resume / start of the contribution to participant.
711
723
  await simpleLoader(
712
724
  `${
@@ -716,17 +728,24 @@ export const listenToParticipantDocumentChanges = async (
716
728
  3000
717
729
  )
718
730
 
719
- // Start / Resume the contribution for the participant.
720
- await handleStartOrResumeContribution(
721
- cloudFunctions,
722
- firestoreDatabase,
723
- ceremony,
724
- circuit,
725
- participant,
726
- entropy,
727
- providerUserId,
728
- false // not finalizing.
729
- )
731
+ try {
732
+ contributionInProgress = true
733
+
734
+ // Start / Resume the contribution for the participant.
735
+ await handleStartOrResumeContribution(
736
+ cloudFunctions,
737
+ firestoreDatabase,
738
+ ceremony,
739
+ circuit,
740
+ participant,
741
+ entropy,
742
+ providerUserId,
743
+ false, // not finalizing.
744
+ circuits.length
745
+ )
746
+ } finally {
747
+ contributionInProgress = false
748
+ }
730
749
  }
731
750
  // Scenario (3.A).
732
751
  else if (isWaitingForContribution)
@@ -811,7 +830,9 @@ export const listenToParticipantDocumentChanges = async (
811
830
  await getLatestVerificationResult(firestoreDatabase, ceremony.id, circuit.id, participant.id)
812
831
 
813
832
  // Get next circuit for contribution.
814
- const nextCircuit = getCircuitBySequencePosition(circuits, changedContributionProgress + 1)
833
+ const nextCircuit = timeoutExpired
834
+ ? getCircuitBySequencePosition(circuits, changedContributionProgress)
835
+ : getCircuitBySequencePosition(circuits, changedContributionProgress + 1)
815
836
 
816
837
  // Check disk space requirements for participant.
817
838
  const wannaGenerateAttestation = await handleDiskSpaceRequirementForNextContribution(
@@ -892,12 +913,13 @@ export const listenToParticipantDocumentChanges = async (
892
913
  const contribute = async (opt: any) => {
893
914
  const { firebaseApp, firebaseFunctions, firestoreDatabase } = await bootstrapCommandExecutionAndServices()
894
915
 
895
- // Check for authentication.
896
- const { user, providerUserId, token } = await checkAuth(firebaseApp)
897
-
898
916
  // Get options.
899
917
  const ceremonyOpt = opt.ceremony
900
918
  const entropyOpt = opt.entropy
919
+ const { auth } = opt
920
+
921
+ // Check for authentication.
922
+ const { user, providerUserId, token } = auth ? await authWithToken(firebaseApp, auth) : await checkAuth(firebaseApp)
901
923
 
902
924
  // Prepare data.
903
925
  let selectedCeremony: FirebaseDocumentInfo
@@ -949,7 +971,7 @@ const contribute = async (opt: any) => {
949
971
  const userData = userDoc.data()
950
972
  if (!userData) {
951
973
  spinner.fail(
952
- `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.`
974
+ `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.`
953
975
  )
954
976
  process.exit(0)
955
977
  }
@@ -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,11 @@
1
1
  export { default as setup } from "./setup.js"
2
2
  export { default as auth } from "./auth.js"
3
+ export { default as authBandada } from "./authBandada.js"
4
+ export { default as authSIWE } from "./authSIWE.js"
3
5
  export { default as contribute } from "./contribute.js"
4
6
  export { default as observe } from "./observe.js"
5
7
  export { default as finalize } from "./finalize.js"
6
8
  export { default as clean } from "./clean.js"
7
9
  export { default as logout } from "./logout.js"
8
10
  export { default as validate } from "./validate.js"
9
- export { default as listCeremonies} from "./listCeremonies.js"
11
+ 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, deleteLocalAuthMethod, deleteLocalBandadaIdentity } from "../lib/localConfigs.js"
10
10
 
11
11
  /**
12
12
  * Logout command.
@@ -52,7 +52,9 @@ const logout = async () => {
52
52
  await signOut(auth)
53
53
 
54
54
  // Delete local token.
55
+ deleteLocalAuthMethod()
55
56
  deleteLocalAccessToken()
57
+ deleteLocalBandadaIdentity()
56
58
 
57
59
  await sleep(3000) // ~3s.
58
60
 
@@ -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.