@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.
- package/README.md +2 -2
- package/dist/.env +17 -3
- package/dist/index.js +544 -106
- package/dist/public/mini-semaphore.wasm +0 -0
- package/dist/public/mini-semaphore.zkey +0 -0
- package/dist/types/commands/authBandada.d.ts +2 -0
- package/dist/types/commands/authSIWE.d.ts +7 -0
- package/dist/types/commands/ceremony/index.d.ts +3 -0
- package/dist/types/commands/ceremony/listParticipants.d.ts +2 -0
- package/dist/types/commands/finalize.d.ts +2 -1
- package/dist/types/commands/index.d.ts +2 -0
- package/dist/types/commands/setup.d.ts +3 -3
- package/dist/types/lib/bandada.d.ts +6 -0
- package/dist/types/lib/files.d.ts +0 -1
- package/dist/types/lib/localConfigs.d.ts +38 -0
- package/dist/types/lib/prompts.d.ts +6 -6
- package/dist/types/lib/utils.d.ts +2 -1
- package/dist/types/types/index.d.ts +69 -0
- package/package.json +12 -5
- package/src/commands/auth.ts +32 -11
- package/src/commands/authBandada.ts +120 -0
- package/src/commands/authSIWE.ts +185 -0
- package/src/commands/ceremony/index.ts +20 -0
- package/src/commands/ceremony/listParticipants.ts +56 -0
- package/src/commands/contribute.ts +51 -25
- package/src/commands/finalize.ts +21 -9
- package/src/commands/index.ts +3 -1
- package/src/commands/listCeremonies.ts +1 -2
- package/src/commands/logout.ts +3 -1
- package/src/commands/observe.ts +7 -3
- package/src/commands/setup.ts +63 -28
- package/src/commands/validate.ts +1 -2
- package/src/index.ts +33 -13
- package/src/lib/bandada.ts +51 -0
- package/src/lib/errors.ts +2 -2
- package/src/lib/localConfigs.ts +54 -0
- package/src/lib/prompts.ts +22 -25
- package/src/lib/services.ts +38 -15
- package/src/lib/utils.ts +51 -13
- package/src/types/index.ts +75 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import open from "open"
|
|
2
|
+
import figlet from "figlet"
|
|
3
|
+
import clipboard from "clipboardy"
|
|
4
|
+
import fetch from "node-fetch"
|
|
5
|
+
import { getAuth, signInWithCustomToken } from "firebase/auth"
|
|
6
|
+
import { httpsCallable } from "firebase/functions"
|
|
7
|
+
import { commonTerms } from "@devtion/actions"
|
|
8
|
+
import { showError } from "../lib/errors.js"
|
|
9
|
+
import { bootstrapCommandExecutionAndServices } from "../lib/services.js"
|
|
10
|
+
import theme from "../lib/theme.js"
|
|
11
|
+
import { customSpinner, sleep } from "../lib/utils.js"
|
|
12
|
+
import { CheckNonceOfSIWEAddressResponse, OAuthDeviceCodeResponse, OAuthTokenResponse } from "../types/index.js"
|
|
13
|
+
import {
|
|
14
|
+
checkLocalAccessToken,
|
|
15
|
+
deleteLocalAccessToken,
|
|
16
|
+
deleteLocalAuthMethod,
|
|
17
|
+
getLocalAccessToken,
|
|
18
|
+
setLocalAccessToken,
|
|
19
|
+
setLocalAuthMethod
|
|
20
|
+
} from "../lib/localConfigs.js"
|
|
21
|
+
|
|
22
|
+
const showVerificationCodeAndUri = async (OAuthDeviceCode: OAuthDeviceCodeResponse) => {
|
|
23
|
+
// Copy code to clipboard.
|
|
24
|
+
let noClipboard = false
|
|
25
|
+
try {
|
|
26
|
+
clipboard.writeSync(OAuthDeviceCode.user_code)
|
|
27
|
+
clipboard.readSync()
|
|
28
|
+
} catch (error) {
|
|
29
|
+
noClipboard = true
|
|
30
|
+
}
|
|
31
|
+
// Display data.
|
|
32
|
+
console.log(
|
|
33
|
+
`${theme.symbols.warning} Visit ${theme.text.bold(
|
|
34
|
+
theme.text.underlined(OAuthDeviceCode.verification_uri_complete)
|
|
35
|
+
)} on this device to generate a new token and authenticate\n`
|
|
36
|
+
)
|
|
37
|
+
console.log(theme.colors.magenta(figlet.textSync("Code is Below", { font: "ANSI Shadow" })), "\n")
|
|
38
|
+
|
|
39
|
+
const message = !noClipboard ? `has been copied to your clipboard (${theme.emojis.clipboard})` : ``
|
|
40
|
+
console.log(
|
|
41
|
+
`${theme.symbols.info} Your auth code: ${theme.text.bold(OAuthDeviceCode.user_code)} ${message} ${
|
|
42
|
+
theme.symbols.success
|
|
43
|
+
}\n`
|
|
44
|
+
)
|
|
45
|
+
const spinner = customSpinner(`Redirecting to Sign In With Ethereum...`, `clock`)
|
|
46
|
+
spinner.start()
|
|
47
|
+
await sleep(10000) // ~10s to make users able to read the CLI.
|
|
48
|
+
try {
|
|
49
|
+
// Automatically open the page (# Step 2).
|
|
50
|
+
await open(OAuthDeviceCode.verification_uri_complete)
|
|
51
|
+
} catch (error: any) {
|
|
52
|
+
console.log(
|
|
53
|
+
`${theme.symbols.info} Please authenticate via SIWE at ${OAuthDeviceCode.verification_uri_complete}`
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
spinner.stop()
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Return the token to sign in to Firebase after passing the SIWE Device Flow
|
|
61
|
+
* @param clientId <string> - The client id of the Auth0 application.
|
|
62
|
+
* @param firebaseFunctions <any> - The Firebase functions instance to call the cloud function
|
|
63
|
+
* @returns <string> - The token to sign in to Firebase
|
|
64
|
+
*/
|
|
65
|
+
const executeSIWEDeviceFlow = async (clientId: string, firebaseFunctions: any): Promise<string> => {
|
|
66
|
+
// Call Auth0 endpoint to request device code uri
|
|
67
|
+
const OAuthDeviceCode = (await fetch(`${process.env.AUTH0_APPLICATION_URL}/oauth/device/code`, {
|
|
68
|
+
method: "POST",
|
|
69
|
+
headers: { "content-type": "application/json" },
|
|
70
|
+
body: JSON.stringify({
|
|
71
|
+
client_id: clientId,
|
|
72
|
+
scope: "openid",
|
|
73
|
+
audience: `${process.env.AUTH0_APPLICATION_URL}/api/v2/`
|
|
74
|
+
})
|
|
75
|
+
}).then((_res) => _res.json())) as OAuthDeviceCodeResponse
|
|
76
|
+
if (OAuthDeviceCode.error) {
|
|
77
|
+
showError(OAuthDeviceCode.error_description, true)
|
|
78
|
+
deleteLocalAuthMethod()
|
|
79
|
+
deleteLocalAccessToken()
|
|
80
|
+
}
|
|
81
|
+
await showVerificationCodeAndUri(OAuthDeviceCode)
|
|
82
|
+
// Poll Auth0 endpoint until you get token or request expires
|
|
83
|
+
let isSignedIn = false
|
|
84
|
+
let isExpired = false
|
|
85
|
+
let auth0Token = ""
|
|
86
|
+
while (!isSignedIn && !isExpired) {
|
|
87
|
+
// Call Auth0 endpoint to request token
|
|
88
|
+
const OAuthToken = (await fetch(`${process.env.AUTH0_APPLICATION_URL}/oauth/token`, {
|
|
89
|
+
method: "POST",
|
|
90
|
+
headers: { "content-type": "application/json" },
|
|
91
|
+
body: JSON.stringify({
|
|
92
|
+
client_id: clientId,
|
|
93
|
+
device_code: OAuthDeviceCode.device_code,
|
|
94
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code"
|
|
95
|
+
})
|
|
96
|
+
}).then((_res) => _res.json())) as OAuthTokenResponse
|
|
97
|
+
if (OAuthToken.error) {
|
|
98
|
+
if (OAuthToken.error === "authorization_pending") {
|
|
99
|
+
// Wait for the user to sign in
|
|
100
|
+
await sleep(OAuthDeviceCode.interval * 1000)
|
|
101
|
+
} else if (OAuthToken.error === "slow_down") {
|
|
102
|
+
// Wait for the user to sign in
|
|
103
|
+
await sleep(OAuthDeviceCode.interval * 1000 * 2)
|
|
104
|
+
} else if (OAuthToken.error === "expired_token") {
|
|
105
|
+
// The user didn't sign in on time
|
|
106
|
+
isExpired = true
|
|
107
|
+
}
|
|
108
|
+
} else {
|
|
109
|
+
// The user signed in
|
|
110
|
+
isSignedIn = true
|
|
111
|
+
auth0Token = OAuthToken.access_token
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// Send token to cloud function to check nonce, create user and retrieve token
|
|
115
|
+
const cf = httpsCallable(firebaseFunctions, commonTerms.cloudFunctionsNames.checkNonceOfSIWEAddress)
|
|
116
|
+
const result = await cf({
|
|
117
|
+
auth0Token
|
|
118
|
+
})
|
|
119
|
+
const { token, valid, message } = result.data as CheckNonceOfSIWEAddressResponse
|
|
120
|
+
if (!valid) {
|
|
121
|
+
showError(message, true)
|
|
122
|
+
deleteLocalAuthMethod()
|
|
123
|
+
deleteLocalAccessToken()
|
|
124
|
+
}
|
|
125
|
+
return token
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Auth command using Sign In With Ethereum
|
|
130
|
+
* @notice The auth command allows a user to make the association of their Ethereum account with the CLI by leveraging SIWE as an authentication mechanism.
|
|
131
|
+
* @dev Under the hood, the command handles a manual Device Flow following the guidelines in the SIWE documentation.
|
|
132
|
+
*/
|
|
133
|
+
const authSIWE = async () => {
|
|
134
|
+
try {
|
|
135
|
+
const { firebaseFunctions } = await bootstrapCommandExecutionAndServices()
|
|
136
|
+
// Console more context for the user.
|
|
137
|
+
console.log(
|
|
138
|
+
`${theme.symbols.info} ${theme.text.bold(
|
|
139
|
+
`You are about to authenticate on this CLI using your Ethereum address (device flow - OAuth 2.0 mechanism).\n${theme.symbols.warning} Please, note that only a Sign-in With Ethereum signature will be required`
|
|
140
|
+
)}\n`
|
|
141
|
+
)
|
|
142
|
+
const spinner = customSpinner(`Checking authentication token...`, `clock`)
|
|
143
|
+
spinner.start()
|
|
144
|
+
await sleep(5000)
|
|
145
|
+
|
|
146
|
+
// Manage OAuth Github or SIWE token.
|
|
147
|
+
const isLocalTokenStored = checkLocalAccessToken()
|
|
148
|
+
|
|
149
|
+
if (!isLocalTokenStored) {
|
|
150
|
+
spinner.fail(`No local authentication token found\n`)
|
|
151
|
+
|
|
152
|
+
// Generate a new access token using Github Device Flow (OAuth 2.0).
|
|
153
|
+
const newToken = await executeSIWEDeviceFlow(String(process.env.AUTH_SIWE_CLIENT_ID), firebaseFunctions)
|
|
154
|
+
|
|
155
|
+
// Store the new access token.
|
|
156
|
+
setLocalAuthMethod("siwe")
|
|
157
|
+
setLocalAccessToken(newToken)
|
|
158
|
+
} else spinner.succeed(`Local authentication token found\n`)
|
|
159
|
+
|
|
160
|
+
// Get access token from local store.
|
|
161
|
+
const token = String(getLocalAccessToken())
|
|
162
|
+
|
|
163
|
+
spinner.text = `Authenticating...`
|
|
164
|
+
spinner.start()
|
|
165
|
+
|
|
166
|
+
// Exchange token for credential.
|
|
167
|
+
const credentials = await signInWithCustomToken(getAuth(), token)
|
|
168
|
+
spinner.succeed(`Authenticated as ${theme.text.bold(credentials.user.uid)}.`)
|
|
169
|
+
|
|
170
|
+
console.log(
|
|
171
|
+
`\n${theme.symbols.warning} You can always log out by running the ${theme.text.bold(
|
|
172
|
+
`phase2cli logout`
|
|
173
|
+
)} command`
|
|
174
|
+
)
|
|
175
|
+
process.exit(0)
|
|
176
|
+
} catch (error) {
|
|
177
|
+
// Delete local token.
|
|
178
|
+
console.log("An error crashed the process. Deleting local token and identity.")
|
|
179
|
+
console.error(error)
|
|
180
|
+
deleteLocalAuthMethod()
|
|
181
|
+
deleteLocalAccessToken()
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export default authSIWE
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Command } from "commander"
|
|
2
|
+
import listParticipants from "./listParticipants.js"
|
|
3
|
+
|
|
4
|
+
const setCeremonyCommands = (program: Command) => {
|
|
5
|
+
const ceremony = program.command("ceremony").description("manage ceremonies")
|
|
6
|
+
|
|
7
|
+
ceremony
|
|
8
|
+
.command("participants")
|
|
9
|
+
.description("retrieve participants list of a ceremony")
|
|
10
|
+
.requiredOption(
|
|
11
|
+
"-c, --ceremony <string>",
|
|
12
|
+
"the prefix of the ceremony you want to retrieve information about",
|
|
13
|
+
""
|
|
14
|
+
)
|
|
15
|
+
.action(listParticipants)
|
|
16
|
+
|
|
17
|
+
return ceremony
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default setCeremonyCommands
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { collection, doc, getDocs } from "firebase/firestore"
|
|
2
|
+
import { ParticipantDocument, UserDocument, commonTerms, getAllCeremonies } from "@devtion/actions"
|
|
3
|
+
import theme from "../../lib/theme.js"
|
|
4
|
+
import { bootstrapCommandExecutionAndServices } from "../../lib/services.js"
|
|
5
|
+
import { showError } from "../../lib/errors.js"
|
|
6
|
+
import { promptForCeremonySelection } from "../../lib/prompts.js"
|
|
7
|
+
|
|
8
|
+
const listParticipants = async () => {
|
|
9
|
+
try {
|
|
10
|
+
const { firestoreDatabase } = await bootstrapCommandExecutionAndServices()
|
|
11
|
+
|
|
12
|
+
const allCeremonies = await getAllCeremonies(firestoreDatabase)
|
|
13
|
+
const selectedCeremony = await promptForCeremonySelection(
|
|
14
|
+
allCeremonies,
|
|
15
|
+
true,
|
|
16
|
+
"Which ceremony would you like to see participants?"
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
const docRef = doc(firestoreDatabase, commonTerms.collections.ceremonies.name, selectedCeremony.id)
|
|
20
|
+
const participantsRef = collection(docRef, "participants")
|
|
21
|
+
const participantsSnapshot = await getDocs(participantsRef)
|
|
22
|
+
const participants = participantsSnapshot.docs.map(
|
|
23
|
+
(participantDoc) => participantDoc.data() as ParticipantDocument
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
const usersRef = collection(firestoreDatabase, "users")
|
|
27
|
+
const usersSnapshot = await getDocs(usersRef)
|
|
28
|
+
const users = usersSnapshot.docs.map((userDoc) => {
|
|
29
|
+
const data = userDoc.data() as UserDocument
|
|
30
|
+
return { id: userDoc.id, ...data }
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
const participantDetails = participants
|
|
34
|
+
.map((participant) => {
|
|
35
|
+
const user = users.find((_user) => _user.id === participant.userId)
|
|
36
|
+
if (!user) return null
|
|
37
|
+
return {
|
|
38
|
+
id: user.name,
|
|
39
|
+
status: participant.status,
|
|
40
|
+
contributionStep: participant.contributionStep,
|
|
41
|
+
lastUpdated: new Date(participant.lastUpdated)
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
.filter((user) => user !== null)
|
|
45
|
+
|
|
46
|
+
const participantsDone = participantDetails.filter((participant) => participant.status === "DONE")
|
|
47
|
+
console.log(participantDetails)
|
|
48
|
+
console.log(`${theme.text.underlined("Total participants:")} ${participantDetails.length}`)
|
|
49
|
+
console.log(`${theme.text.underlined("Total participants finished:")} ${participantsDone.length}`)
|
|
50
|
+
} catch (err: any) {
|
|
51
|
+
showError(`Something went wrong: ${err.toString()}`, true)
|
|
52
|
+
}
|
|
53
|
+
process.exit(0)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export default listParticipants
|
|
@@ -41,7 +41,7 @@ import {
|
|
|
41
41
|
} from "../lib/utils.js"
|
|
42
42
|
import { COMMAND_ERRORS, showError } from "../lib/errors.js"
|
|
43
43
|
import { authWithToken, bootstrapCommandExecutionAndServices, checkAuth } from "../lib/services.js"
|
|
44
|
-
import { getAttestationLocalFilePath, localPaths } from "../lib/localConfigs.js"
|
|
44
|
+
import { getAttestationLocalFilePath, getLocalAuthMethod, localPaths } from "../lib/localConfigs.js"
|
|
45
45
|
import theme from "../lib/theme.js"
|
|
46
46
|
import { checkAndMakeNewDirectoryIfNonexistent, writeFile } from "../lib/files.js"
|
|
47
47
|
|
|
@@ -281,12 +281,12 @@ export const handleDiskSpaceRequirementForNextContribution = async (
|
|
|
281
281
|
)} since is based on the aggregate free memory on your disks but some may not be detected!\n`
|
|
282
282
|
)
|
|
283
283
|
|
|
284
|
-
const {
|
|
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 = !!
|
|
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
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
theme.text.underlined(gistUrl)
|
|
427
|
-
)})`
|
|
428
|
-
)
|
|
422
|
+
let gistUrl = ""
|
|
423
|
+
const isGithub = getLocalAuthMethod() === "github"
|
|
424
|
+
if (isGithub) {
|
|
425
|
+
gistUrl = await publishGist(participantAccessToken, publicAttestation, ceremonyName, ceremonyPrefix)
|
|
429
426
|
|
|
427
|
+
console.log(
|
|
428
|
+
`\n${
|
|
429
|
+
theme.symbols.info
|
|
430
|
+
} Your public attestation has been successfully posted as Github Gist (${theme.text.bold(
|
|
431
|
+
theme.text.underlined(gistUrl)
|
|
432
|
+
)})`
|
|
433
|
+
)
|
|
434
|
+
}
|
|
430
435
|
// Prepare a ready-to-share tweet.
|
|
431
436
|
await handleTweetGeneration(ceremonyName, gistUrl)
|
|
432
437
|
}
|
|
@@ -514,6 +519,8 @@ export const listenToCeremonyCircuitDocumentChanges = (
|
|
|
514
519
|
})
|
|
515
520
|
}
|
|
516
521
|
|
|
522
|
+
let contributionInProgress = false
|
|
523
|
+
|
|
517
524
|
/**
|
|
518
525
|
* Listen to current authenticated participant document changes.
|
|
519
526
|
* @dev this is the core business logic related to the execution of the contribute command.
|
|
@@ -706,6 +713,12 @@ export const listenToParticipantDocumentChanges = async (
|
|
|
706
713
|
|
|
707
714
|
// Scenario (3.B).
|
|
708
715
|
if (isCurrentContributor && hasResumableStep && startingOrResumingContribution) {
|
|
716
|
+
if (contributionInProgress) {
|
|
717
|
+
console.warn(
|
|
718
|
+
`\n${theme.symbols.warning} Received instruction to start/resume contribution but contribution is already in progress...[skipping]`
|
|
719
|
+
)
|
|
720
|
+
return
|
|
721
|
+
}
|
|
709
722
|
// Communicate resume / start of the contribution to participant.
|
|
710
723
|
await simpleLoader(
|
|
711
724
|
`${
|
|
@@ -715,17 +728,24 @@ export const listenToParticipantDocumentChanges = async (
|
|
|
715
728
|
3000
|
|
716
729
|
)
|
|
717
730
|
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
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
|
+
}
|
|
729
749
|
}
|
|
730
750
|
// Scenario (3.A).
|
|
731
751
|
else if (isWaitingForContribution)
|
|
@@ -810,7 +830,9 @@ export const listenToParticipantDocumentChanges = async (
|
|
|
810
830
|
await getLatestVerificationResult(firestoreDatabase, ceremony.id, circuit.id, participant.id)
|
|
811
831
|
|
|
812
832
|
// Get next circuit for contribution.
|
|
813
|
-
const nextCircuit = timeoutExpired
|
|
833
|
+
const nextCircuit = timeoutExpired
|
|
834
|
+
? getCircuitBySequencePosition(circuits, changedContributionProgress)
|
|
835
|
+
: getCircuitBySequencePosition(circuits, changedContributionProgress + 1)
|
|
814
836
|
|
|
815
837
|
// Check disk space requirements for participant.
|
|
816
838
|
const wannaGenerateAttestation = await handleDiskSpaceRequirementForNextContribution(
|
|
@@ -894,7 +916,7 @@ const contribute = async (opt: any) => {
|
|
|
894
916
|
// Get options.
|
|
895
917
|
const ceremonyOpt = opt.ceremony
|
|
896
918
|
const entropyOpt = opt.entropy
|
|
897
|
-
const auth = opt
|
|
919
|
+
const { auth } = opt
|
|
898
920
|
|
|
899
921
|
// Check for authentication.
|
|
900
922
|
const { user, providerUserId, token } = auth ? await authWithToken(firebaseApp, auth) : await checkAuth(firebaseApp)
|
|
@@ -935,7 +957,11 @@ const contribute = async (opt: any) => {
|
|
|
935
957
|
} else selectedCeremony = selectedCeremonyDocument.at(0)
|
|
936
958
|
} else {
|
|
937
959
|
// Prompt the user to select a ceremony from the opened ones.
|
|
938
|
-
selectedCeremony = await promptForCeremonySelection(
|
|
960
|
+
selectedCeremony = await promptForCeremonySelection(
|
|
961
|
+
ceremoniesOpenedForContributions,
|
|
962
|
+
false,
|
|
963
|
+
"Which ceremony would you like to contribute to?"
|
|
964
|
+
)
|
|
939
965
|
}
|
|
940
966
|
|
|
941
967
|
// Get selected ceremony circuit(s) documents.
|
|
@@ -949,7 +975,7 @@ const contribute = async (opt: any) => {
|
|
|
949
975
|
const userData = userDoc.data()
|
|
950
976
|
if (!userData) {
|
|
951
977
|
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
|
|
978
|
+
`Unfortunately we could not find a user document with your information. This likely means that you did not pass the GitHub reputation checks and therefore are not eligible to contribute to any ceremony. If you believe you pass the requirements, it might be possible that your profile is private and we were not able to fetch your real statistics, in this case please consider making your profile public for the duration of the contribution. Please contact the coordinator if you believe this to be an error.`
|
|
953
979
|
)
|
|
954
980
|
process.exit(0)
|
|
955
981
|
}
|
package/src/commands/finalize.ts
CHANGED
|
@@ -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) //
|
|
77
|
+
await sleep(3000) // workaround for file descriptor.
|
|
78
78
|
|
|
79
79
|
// Upload verification key to storage.
|
|
80
80
|
await multiPartUpload(
|
|
@@ -122,7 +122,7 @@ export const handleVerifierSmartContract = async (
|
|
|
122
122
|
// Write the verification key locally.
|
|
123
123
|
writeFile(verifierContractLocalFilePath, verifierCode)
|
|
124
124
|
|
|
125
|
-
await sleep(3000) //
|
|
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) //
|
|
180
|
+
await sleep(2000) // workaround for descriptors.
|
|
178
181
|
|
|
179
182
|
// Extract data.
|
|
180
183
|
const { prefix: circuitPrefix } = circuit.data
|
|
@@ -245,8 +248,12 @@ const finalize = async (opt: any) => {
|
|
|
245
248
|
const { firebaseApp, firebaseFunctions, firestoreDatabase } = await bootstrapCommandExecutionAndServices()
|
|
246
249
|
|
|
247
250
|
// Check for authentication.
|
|
248
|
-
const auth = opt
|
|
249
|
-
const {
|
|
251
|
+
const { auth } = opt
|
|
252
|
+
const {
|
|
253
|
+
user,
|
|
254
|
+
providerUserId,
|
|
255
|
+
token: coordinatorAccessToken
|
|
256
|
+
} = auth ? await authWithToken(firebaseApp, auth) : await checkAuth(firebaseApp)
|
|
250
257
|
|
|
251
258
|
// Preserve command execution only for coordinators.
|
|
252
259
|
if (!(await isCoordinator(user))) showError(COMMAND_ERRORS.COMMAND_NOT_COORDINATOR, true)
|
|
@@ -262,7 +269,11 @@ const finalize = async (opt: any) => {
|
|
|
262
269
|
)
|
|
263
270
|
|
|
264
271
|
// Prompt for ceremony selection.
|
|
265
|
-
const selectedCeremony = await promptForCeremonySelection(
|
|
272
|
+
const selectedCeremony = await promptForCeremonySelection(
|
|
273
|
+
ceremoniesClosedForFinalization,
|
|
274
|
+
true,
|
|
275
|
+
"Which ceremony would you like to finalize?"
|
|
276
|
+
)
|
|
266
277
|
|
|
267
278
|
// Get coordinator participant document.
|
|
268
279
|
let participant = await getDocumentById(
|
|
@@ -307,7 +318,8 @@ const finalize = async (opt: any) => {
|
|
|
307
318
|
circuit,
|
|
308
319
|
participant,
|
|
309
320
|
beacon,
|
|
310
|
-
providerUserId
|
|
321
|
+
providerUserId,
|
|
322
|
+
circuits.length
|
|
311
323
|
)
|
|
312
324
|
|
|
313
325
|
process.stdout.write(`\n`)
|
package/src/commands/index.ts
CHANGED
|
@@ -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"
|
|
@@ -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
|
package/src/commands/logout.ts
CHANGED
|
@@ -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
|
|
package/src/commands/observe.ts
CHANGED
|
@@ -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,11 +143,15 @@ const observe = async () => {
|
|
|
143
143
|
// Preserve command execution only for coordinators].
|
|
144
144
|
if (!(await isCoordinator(user))) showError(COMMAND_ERRORS.COMMAND_NOT_COORDINATOR, true)
|
|
145
145
|
|
|
146
|
-
// Get running
|
|
146
|
+
// Get running ceremonies info (if any).
|
|
147
147
|
const runningCeremoniesDocs = await getOpenedCeremonies(firestoreDatabase)
|
|
148
148
|
|
|
149
149
|
// Ask to select a ceremony.
|
|
150
|
-
const ceremony = await promptForCeremonySelection(
|
|
150
|
+
const ceremony = await promptForCeremonySelection(
|
|
151
|
+
runningCeremoniesDocs,
|
|
152
|
+
false,
|
|
153
|
+
"Which ceremony would you like to observe?"
|
|
154
|
+
)
|
|
151
155
|
|
|
152
156
|
console.log(`${logSymbols.info} Refresh rate set to ~3 seconds for waiting queue updates\n`)
|
|
153
157
|
|