@devtion/devcli 0.0.0-a9e4cd4 → 0.0.0-bbc217a
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/dist/.env +14 -0
- package/dist/index.js +437 -46
- 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/index.d.ts +2 -0
- package/dist/types/commands/setup.d.ts +2 -2
- package/dist/types/lib/bandada.d.ts +6 -0
- package/dist/types/lib/files.d.ts +1 -0
- package/dist/types/lib/localConfigs.d.ts +38 -0
- package/dist/types/lib/prompts.d.ts +1 -1
- package/dist/types/types/index.d.ts +69 -0
- package/package.json +10 -3
- package/src/commands/auth.ts +7 -1
- 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 +47 -24
- package/src/commands/finalize.ts +8 -4
- package/src/commands/index.ts +2 -0
- package/src/commands/logout.ts +3 -1
- package/src/commands/observe.ts +5 -1
- package/src/commands/setup.ts +3 -3
- package/src/index.ts +18 -5
- 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 +3 -6
- package/src/lib/services.ts +38 -13
- package/src/lib/utils.ts +7 -4
- package/src/types/index.ts +75 -0
|
@@ -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,18 +728,24 @@ export const listenToParticipantDocumentChanges = async (
|
|
|
715
728
|
3000
|
|
716
729
|
)
|
|
717
730
|
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
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)
|
|
@@ -938,7 +957,11 @@ const contribute = async (opt: any) => {
|
|
|
938
957
|
} else selectedCeremony = selectedCeremonyDocument.at(0)
|
|
939
958
|
} else {
|
|
940
959
|
// Prompt the user to select a ceremony from the opened ones.
|
|
941
|
-
selectedCeremony = await promptForCeremonySelection(
|
|
960
|
+
selectedCeremony = await promptForCeremonySelection(
|
|
961
|
+
ceremoniesOpenedForContributions,
|
|
962
|
+
false,
|
|
963
|
+
"Which ceremony would you like to contribute to?"
|
|
964
|
+
)
|
|
942
965
|
}
|
|
943
966
|
|
|
944
967
|
// Get selected ceremony circuit(s) documents.
|
|
@@ -952,7 +975,7 @@ const contribute = async (opt: any) => {
|
|
|
952
975
|
const userData = userDoc.data()
|
|
953
976
|
if (!userData) {
|
|
954
977
|
spinner.fail(
|
|
955
|
-
`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.`
|
|
956
979
|
)
|
|
957
980
|
process.exit(0)
|
|
958
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(
|
|
@@ -177,7 +177,7 @@ export const handleCircuitFinalization = async (
|
|
|
177
177
|
circuitsLength
|
|
178
178
|
)
|
|
179
179
|
|
|
180
|
-
await sleep(2000) //
|
|
180
|
+
await sleep(2000) // workaround for descriptors.
|
|
181
181
|
|
|
182
182
|
// Extract data.
|
|
183
183
|
const { prefix: circuitPrefix } = circuit.data
|
|
@@ -269,7 +269,11 @@ const finalize = async (opt: any) => {
|
|
|
269
269
|
)
|
|
270
270
|
|
|
271
271
|
// Prompt for ceremony selection.
|
|
272
|
-
const selectedCeremony = await promptForCeremonySelection(
|
|
272
|
+
const selectedCeremony = await promptForCeremonySelection(
|
|
273
|
+
ceremoniesClosedForFinalization,
|
|
274
|
+
true,
|
|
275
|
+
"Which ceremony would you like to finalize?"
|
|
276
|
+
)
|
|
273
277
|
|
|
274
278
|
// Get coordinator participant document.
|
|
275
279
|
let participant = await getDocumentById(
|
package/src/commands/index.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
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"
|
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
|
@@ -147,7 +147,11 @@ const observe = async () => {
|
|
|
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
|
|
package/src/commands/setup.ts
CHANGED
|
@@ -271,7 +271,7 @@ export const displayCeremonySummary = (ceremonyInputData: CeremonyInputData, cir
|
|
|
271
271
|
|
|
272
272
|
/**
|
|
273
273
|
* Check if the smallest Powers of Tau has already been downloaded/stored in the correspondent local path
|
|
274
|
-
* @dev we are downloading the Powers of Tau file from
|
|
274
|
+
* @dev we are downloading the Powers of Tau file from Perpetual Powers of Tau Phase 1 Trusted Setup.
|
|
275
275
|
* @param powers <string> - the smallest amount of powers needed for the given circuit (should be in a 'XY' stringified form).
|
|
276
276
|
* @param ptauCompleteFilename <string> - the complete file name of the powers of tau file to be downloaded.
|
|
277
277
|
* @returns <Promise<void>>
|
|
@@ -293,7 +293,7 @@ export const checkAndDownloadSmallestPowersOfTau = async (
|
|
|
293
293
|
const spinner = customSpinner(
|
|
294
294
|
`Downloading the ${theme.text.bold(
|
|
295
295
|
`#${powers}`
|
|
296
|
-
)} smallest PoT file needed from the
|
|
296
|
+
)} smallest PoT file needed from the Perpetual Powers of Tau Phase 1 Trusted Setup...`,
|
|
297
297
|
`clock`
|
|
298
298
|
)
|
|
299
299
|
spinner.start()
|
|
@@ -463,7 +463,7 @@ export const handleCircuitArtifactUploadToStorage = async (
|
|
|
463
463
|
* @notice The setup command allows the coordinator of the ceremony to prepare the next ceremony by interacting with the CLI.
|
|
464
464
|
* @dev For proper execution, the command must be run in a folder containing the R1CS files related to the circuits
|
|
465
465
|
* for which the coordinator wants to create the ceremony. The command will download the necessary Tau powers
|
|
466
|
-
* from
|
|
466
|
+
* from PPoT ceremony Phase 1 Setup Ceremony.
|
|
467
467
|
* @param cmd? <any> - the path to the ceremony setup file.
|
|
468
468
|
*/
|
|
469
469
|
const setup = async (cmd: { template?: string; auth?: string }) => {
|
package/src/index.ts
CHANGED
|
@@ -7,6 +7,8 @@ import { fileURLToPath } from "url"
|
|
|
7
7
|
import {
|
|
8
8
|
setup,
|
|
9
9
|
auth,
|
|
10
|
+
authSIWE,
|
|
11
|
+
authBandada,
|
|
10
12
|
contribute,
|
|
11
13
|
observe,
|
|
12
14
|
finalize,
|
|
@@ -15,6 +17,7 @@ import {
|
|
|
15
17
|
validate,
|
|
16
18
|
listCeremonies
|
|
17
19
|
} from "./commands/index.js"
|
|
20
|
+
import setCeremonyCommands from "./commands/ceremony/index.js"
|
|
18
21
|
|
|
19
22
|
// Get pkg info (e.g., name, version).
|
|
20
23
|
const packagePath = `${dirname(fileURLToPath(import.meta.url))}/..`
|
|
@@ -26,6 +29,14 @@ program.name(name).description(description).version(version)
|
|
|
26
29
|
|
|
27
30
|
// User commands.
|
|
28
31
|
program.command("auth").description("authenticate yourself using your Github account (OAuth 2.0)").action(auth)
|
|
32
|
+
program
|
|
33
|
+
.command("auth-bandada")
|
|
34
|
+
.description("authenticate yourself in a privacy-perserving manner using Bandada")
|
|
35
|
+
.action(authBandada)
|
|
36
|
+
program
|
|
37
|
+
.command("auth-siwe")
|
|
38
|
+
.description("authenticate yourself using your Ethereum account (Sign In With Ethereum - SIWE)")
|
|
39
|
+
.action(authSIWE)
|
|
29
40
|
program
|
|
30
41
|
.command("contribute")
|
|
31
42
|
.description("compute contributions for a Phase2 Trusted Setup ceremony circuits")
|
|
@@ -44,27 +55,27 @@ program
|
|
|
44
55
|
.action(logout)
|
|
45
56
|
program
|
|
46
57
|
.command("validate")
|
|
47
|
-
.description("
|
|
58
|
+
.description("validate that a Ceremony Setup file is correct")
|
|
48
59
|
.requiredOption("-t, --template <path>", "The path to the ceremony setup template", "")
|
|
49
60
|
.option("-c, --constraints <number>", "The number of constraints to check against")
|
|
50
61
|
.action(validate)
|
|
51
62
|
|
|
52
63
|
// Only coordinator commands.
|
|
53
|
-
const
|
|
64
|
+
const coordinate = program.command("coordinate").description("commands for coordinating a ceremony")
|
|
54
65
|
|
|
55
|
-
|
|
66
|
+
coordinate
|
|
56
67
|
.command("setup")
|
|
57
68
|
.description("setup a Groth16 Phase 2 Trusted Setup ceremony for zk-SNARK circuits")
|
|
58
69
|
.option("-t, --template <path>", "The path to the ceremony setup template", "")
|
|
59
70
|
.option("-a, --auth <string>", "The Github OAuth 2.0 token", "")
|
|
60
71
|
.action(setup)
|
|
61
72
|
|
|
62
|
-
|
|
73
|
+
coordinate
|
|
63
74
|
.command("observe")
|
|
64
75
|
.description("observe in real-time the waiting queue of each ceremony circuit")
|
|
65
76
|
.action(observe)
|
|
66
77
|
|
|
67
|
-
|
|
78
|
+
coordinate
|
|
68
79
|
.command("finalize")
|
|
69
80
|
.description(
|
|
70
81
|
"finalize a Phase2 Trusted Setup ceremony by applying a beacon, exporting verification key and verifier contract"
|
|
@@ -72,4 +83,6 @@ ceremony
|
|
|
72
83
|
.option("-a, --auth <string>", "the Github OAuth 2.0 token", "")
|
|
73
84
|
.action(finalize)
|
|
74
85
|
|
|
86
|
+
setCeremonyCommands(program)
|
|
87
|
+
|
|
75
88
|
program.parseAsync(process.argv)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { ApiSdk, GroupResponse } from "@bandada/api-sdk"
|
|
2
|
+
import { Identity } from "@semaphore-protocol/identity"
|
|
3
|
+
import open from "open"
|
|
4
|
+
|
|
5
|
+
import { askForConfirmation } from "../lib/prompts.js"
|
|
6
|
+
import { showError } from "./errors.js"
|
|
7
|
+
import theme from "../lib/theme.js"
|
|
8
|
+
|
|
9
|
+
const { BANDADA_API_URL } = process.env
|
|
10
|
+
|
|
11
|
+
const bandadaApi = new ApiSdk(BANDADA_API_URL)
|
|
12
|
+
|
|
13
|
+
export const getGroup = async (groupId: string): Promise<GroupResponse | null> => {
|
|
14
|
+
try {
|
|
15
|
+
const group = await bandadaApi.getGroup(groupId)
|
|
16
|
+
return group
|
|
17
|
+
} catch (error: any) {
|
|
18
|
+
showError(`Bandada getGroup error: ${error}`, true)
|
|
19
|
+
return null
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const getMembersOfGroup = async (groupId: string): Promise<string[] | null> => {
|
|
24
|
+
try {
|
|
25
|
+
const group = await bandadaApi.getGroup(groupId)
|
|
26
|
+
return group.members
|
|
27
|
+
} catch (error: any) {
|
|
28
|
+
showError(`Bandada getMembersOfGroup error: ${error}`, true)
|
|
29
|
+
return null
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const addMemberToGroup = async (groupId: string, dashboardUrl: string, identity: Identity) => {
|
|
34
|
+
const commitment = identity.commitment.toString()
|
|
35
|
+
const group = await bandadaApi.getGroup(groupId)
|
|
36
|
+
const providerName = group.credentials.id.split("_")[0].toLowerCase()
|
|
37
|
+
|
|
38
|
+
// 6. open a new window with the url:
|
|
39
|
+
const url = `${dashboardUrl}credentials?group=${groupId}&member=${commitment}&provider=${providerName}`
|
|
40
|
+
console.log(`${theme.text.bold(`Verification URL:`)} ${theme.text.underlined(url)}`)
|
|
41
|
+
open(url)
|
|
42
|
+
|
|
43
|
+
const { confirmation } = await askForConfirmation("Did you join the Bandada group in the browser?")
|
|
44
|
+
if (!confirmation) showError("You must join the Bandada group to continue the login process", true)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const isGroupMember = async (groupId: string, identity: Identity): Promise<boolean> => {
|
|
48
|
+
const commitment = identity.commitment.toString()
|
|
49
|
+
const isMember: boolean = await bandadaApi.isGroupMember(groupId, commitment)
|
|
50
|
+
return isMember
|
|
51
|
+
}
|
package/src/lib/errors.ts
CHANGED
|
@@ -6,7 +6,7 @@ export const CORE_SERVICES_ERRORS = {
|
|
|
6
6
|
FIREBASE_TOKEN_EXPIRED_REMOVED_PERMISSIONS: `The Github authorization has failed due to lack of association between your account and the CLI`,
|
|
7
7
|
FIREBASE_USER_DISABLED: `The Github account has been suspended by the ceremony coordinator(s), blocking the possibility of contribution. Please, contact them to understand the motivation behind it.`,
|
|
8
8
|
FIREBASE_FAILED_CREDENTIALS_VERIFICATION: `Firebase cannot verify your Github credentials due to network errors. Please, try once again later.`,
|
|
9
|
-
FIREBASE_NETWORK_ERROR: `Unable to reach Firebase due to network
|
|
9
|
+
FIREBASE_NETWORK_ERROR: `Unable to reach Firebase due to network errors. Please, try once again later and make sure your Internet connection is stable.`,
|
|
10
10
|
FIREBASE_CEREMONY_NOT_OPENED: `There are no ceremonies opened to contributions`,
|
|
11
11
|
FIREBASE_CEREMONY_NOT_CLOSED: `There are no ceremonies ready to finalization`,
|
|
12
12
|
AWS_CEREMONY_BUCKET_CREATION: `Unable to create a new bucket for the ceremony. Something went wrong during the creation. Please, repeat the process by providing a new ceremony name of the ceremony.`,
|
|
@@ -34,7 +34,7 @@ export const COMMAND_ERRORS = {
|
|
|
34
34
|
COMMAND_SETUP_NO_R1CS: `Unable to retrieve R1CS files from current working directory. Please, run this command from a working directory where the R1CS files are located to continue with the setup process. We kindly ask you to run the command from an empty directory containing only the R1CS and WASM files.`,
|
|
35
35
|
COMMAND_SETUP_NO_WASM: `Unable to retrieve WASM files from current working directory. Please, run this command from a working directory where the WASM files are located to continue with the setup process. We kindly ask you to run the command from an empty directory containing only the WASM and R1CS files.`,
|
|
36
36
|
COMMAND_SETUP_MISMATCH_R1CS_WASM: `The folder contains more R1CS files than WASM files (or vice versa). Please, run this command from a working directory where each R1CS is paired with its corresponding file WASM.`,
|
|
37
|
-
COMMAND_SETUP_DOWNLOAD_PTAU: `Unable to download Powers of Tau file from
|
|
37
|
+
COMMAND_SETUP_DOWNLOAD_PTAU: `Unable to download Powers of Tau file from PPoT Phase 1 Trusted Setup. Possible causes may involve an error while making the request (be sure to have a stable internet connection). Please, we kindly ask you to terminate the current session and repeat the process.`,
|
|
38
38
|
COMMAND_SETUP_ABORT: `You chose to abort the setup process.`,
|
|
39
39
|
COMMAND_CONTRIBUTE_NO_OPENED_CEREMONIES: `Unfortunately, there is no ceremony for which you can make a contribution at this time. Please, try again later.`,
|
|
40
40
|
COMMAND_CONTRIBUTE_NO_PARTICIPANT_DATA: `Unable to retrieve your data as ceremony participant. Please, terminate the current session and try again later. If the error persists, please contact the ceremony coordinator.`,
|
package/src/lib/localConfigs.ts
CHANGED
|
@@ -24,6 +24,14 @@ const config = new Conf({
|
|
|
24
24
|
accessToken: {
|
|
25
25
|
type: "string",
|
|
26
26
|
default: ""
|
|
27
|
+
},
|
|
28
|
+
bandadaIdentity: {
|
|
29
|
+
type: "string",
|
|
30
|
+
default: ""
|
|
31
|
+
},
|
|
32
|
+
authMethod: {
|
|
33
|
+
type: "string",
|
|
34
|
+
default: ""
|
|
27
35
|
}
|
|
28
36
|
}
|
|
29
37
|
})
|
|
@@ -91,6 +99,52 @@ export const setLocalAccessToken = (token: string) => config.set("accessToken",
|
|
|
91
99
|
*/
|
|
92
100
|
export const deleteLocalAccessToken = () => config.delete("accessToken")
|
|
93
101
|
|
|
102
|
+
/**
|
|
103
|
+
* Return the Bandada identity, if present.
|
|
104
|
+
* @returns <string | undefined> - the Bandada identity if present, otherwise undefined.
|
|
105
|
+
*/
|
|
106
|
+
export const getLocalBandadaIdentity = (): string | unknown => config.get("bandadaIdentity")
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Check if the Bandada identity exists in the local storage.
|
|
110
|
+
* @returns <boolean>
|
|
111
|
+
*/
|
|
112
|
+
export const checkLocalBandadaIdentity = (): boolean => config.has("bandadaIdentity") && !!config.get("bandadaIdentity")
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Set the Bandada identity.
|
|
116
|
+
* @param identity <string> - the Bandada identity to be stored.
|
|
117
|
+
*/
|
|
118
|
+
export const setLocalBandadaIdentity = (identity: string) => config.set("bandadaIdentity", identity)
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Delete the stored Bandada identity.
|
|
122
|
+
*/
|
|
123
|
+
export const deleteLocalBandadaIdentity = () => config.delete("bandadaIdentity")
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Return the authentication method, if present.
|
|
127
|
+
* @returns <string | undefined> - the authentication method if present, otherwise undefined.
|
|
128
|
+
*/
|
|
129
|
+
export const getLocalAuthMethod = (): string | unknown => config.get("authMethod")
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Check if the authentication method exists in the local storage.
|
|
133
|
+
* @returns <boolean>
|
|
134
|
+
*/
|
|
135
|
+
export const checkLocalAuthMethod = (): boolean => config.has("authMethod") && !!config.get("authMethod")
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Set the authentication method.
|
|
139
|
+
* @param method <string> - the authentication method to be stored.
|
|
140
|
+
*/
|
|
141
|
+
export const setLocalAuthMethod = (method: string) => config.set("authMethod", method)
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Delete the stored authentication method.
|
|
145
|
+
*/
|
|
146
|
+
export const deleteLocalAuthMethod = () => config.delete("authMethod")
|
|
147
|
+
|
|
94
148
|
/**
|
|
95
149
|
* Get the complete local file path.
|
|
96
150
|
* @param cwd <string> - the current working directory path.
|
package/src/lib/prompts.ts
CHANGED
|
@@ -661,7 +661,8 @@ export const promptPotSelector = async (options: Array<string>): Promise<string>
|
|
|
661
661
|
*/
|
|
662
662
|
export const promptForCeremonySelection = async (
|
|
663
663
|
ceremoniesDocuments: Array<FirebaseDocumentInfo>,
|
|
664
|
-
isFinalizing: boolean
|
|
664
|
+
isFinalizing: boolean,
|
|
665
|
+
messageToDisplay?: string
|
|
665
666
|
): Promise<FirebaseDocumentInfo> => {
|
|
666
667
|
// Prepare state.
|
|
667
668
|
const choices: Array<Choice> = []
|
|
@@ -686,11 +687,7 @@ export const promptForCeremonySelection = async (
|
|
|
686
687
|
const { ceremony } = await prompts({
|
|
687
688
|
type: "select",
|
|
688
689
|
name: "ceremony",
|
|
689
|
-
message: theme.text.bold(
|
|
690
|
-
!isFinalizing
|
|
691
|
-
? "Which ceremony would you like to contribute to?"
|
|
692
|
-
: "Which ceremony would you like to finalize?"
|
|
693
|
-
),
|
|
690
|
+
message: theme.text.bold(messageToDisplay),
|
|
694
691
|
choices,
|
|
695
692
|
initial: 0
|
|
696
693
|
})
|
package/src/lib/services.ts
CHANGED
|
@@ -6,13 +6,18 @@ import {
|
|
|
6
6
|
import clear from "clear"
|
|
7
7
|
import figlet from "figlet"
|
|
8
8
|
import { FirebaseApp } from "firebase/app"
|
|
9
|
-
import { OAuthCredential } from "firebase/auth"
|
|
9
|
+
import { OAuthCredential, getAuth, signInWithCustomToken } from "firebase/auth"
|
|
10
10
|
import dotenv from "dotenv"
|
|
11
11
|
import { fileURLToPath } from "url"
|
|
12
12
|
import { dirname } from "path"
|
|
13
13
|
import { AuthUser } from "../types/index.js"
|
|
14
14
|
import { CONFIG_ERRORS, CORE_SERVICES_ERRORS, showError, THIRD_PARTY_SERVICES_ERRORS } from "./errors.js"
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
checkLocalAccessToken,
|
|
17
|
+
deleteLocalAccessToken,
|
|
18
|
+
getLocalAccessToken,
|
|
19
|
+
getLocalAuthMethod
|
|
20
|
+
} from "./localConfigs.js"
|
|
16
21
|
import theme from "./theme.js"
|
|
17
22
|
import { exchangeGithubTokenForCredentials, getGithubProviderUserId, getUserHandleFromProviderUserId } from "./utils.js"
|
|
18
23
|
|
|
@@ -164,22 +169,42 @@ export const checkAuth = async (firebaseApp: FirebaseApp): Promise<AuthUser> =>
|
|
|
164
169
|
// Retrieve local access token.
|
|
165
170
|
const token = String(getLocalAccessToken())
|
|
166
171
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
+
let providerUserId: string
|
|
173
|
+
let username: string
|
|
174
|
+
const authMethod = getLocalAuthMethod()
|
|
175
|
+
switch (authMethod) {
|
|
176
|
+
case "github": {
|
|
177
|
+
// Get credentials.
|
|
178
|
+
const credentials = exchangeGithubTokenForCredentials(token)
|
|
179
|
+
// Sign in to Firebase using credentials.
|
|
180
|
+
await signInToFirebase(firebaseApp, credentials)
|
|
181
|
+
// Get Github unique identifier (handle-id).
|
|
182
|
+
providerUserId = await getGithubProviderUserId(String(token))
|
|
183
|
+
username = getUserHandleFromProviderUserId(providerUserId)
|
|
184
|
+
break
|
|
185
|
+
}
|
|
186
|
+
case "bandada": {
|
|
187
|
+
const userCredentials = await signInWithCustomToken(getAuth(), token)
|
|
188
|
+
providerUserId = userCredentials.user.uid
|
|
189
|
+
username = providerUserId
|
|
190
|
+
break
|
|
191
|
+
}
|
|
192
|
+
case "siwe": {
|
|
193
|
+
const userCredentials = await signInWithCustomToken(getAuth(), token)
|
|
194
|
+
providerUserId = userCredentials.user.uid
|
|
195
|
+
username = providerUserId
|
|
196
|
+
break
|
|
197
|
+
}
|
|
198
|
+
default: {
|
|
199
|
+
break
|
|
200
|
+
}
|
|
201
|
+
}
|
|
172
202
|
|
|
173
203
|
// Get current authenticated user.
|
|
174
204
|
const user = getCurrentFirebaseAuthUser(firebaseApp)
|
|
175
205
|
|
|
176
|
-
// Get Github unique identifier (handle-id).
|
|
177
|
-
const providerUserId = await getGithubProviderUserId(String(token))
|
|
178
|
-
|
|
179
206
|
// Greet the user.
|
|
180
|
-
console.log(
|
|
181
|
-
`Greetings, @${theme.text.bold(getUserHandleFromProviderUserId(providerUserId))} ${theme.emojis.wave}\n`
|
|
182
|
-
)
|
|
207
|
+
console.log(`Greetings, @${theme.text.bold(username)} ${theme.emojis.wave}\n`)
|
|
183
208
|
|
|
184
209
|
return {
|
|
185
210
|
user,
|
package/src/lib/utils.ts
CHANGED
|
@@ -18,7 +18,8 @@ import {
|
|
|
18
18
|
ParticipantContributionStep,
|
|
19
19
|
permanentlyStoreCurrentContributionTimeAndHash,
|
|
20
20
|
progressToNextContributionStep,
|
|
21
|
-
verifyContribution
|
|
21
|
+
verifyContribution,
|
|
22
|
+
contribHashRegex
|
|
22
23
|
} from "@devtion/actions"
|
|
23
24
|
import { Presets, SingleBar } from "cli-progress"
|
|
24
25
|
import dotenv from "dotenv"
|
|
@@ -155,9 +156,11 @@ export const getPublicAttestationGist = async (
|
|
|
155
156
|
* @returns <string> - the third-party provider handle of the user.
|
|
156
157
|
*/
|
|
157
158
|
export const getUserHandleFromProviderUserId = (providerUserId: string): string => {
|
|
158
|
-
if (providerUserId.indexOf("-") === -1)
|
|
159
|
+
if (providerUserId.indexOf("-") === -1) {
|
|
160
|
+
return providerUserId
|
|
161
|
+
}
|
|
159
162
|
|
|
160
|
-
return providerUserId.
|
|
163
|
+
return providerUserId.substring(0, providerUserId.lastIndexOf("-"))
|
|
161
164
|
}
|
|
162
165
|
|
|
163
166
|
/**
|
|
@@ -662,7 +665,7 @@ export const handleStartOrResumeContribution = async (
|
|
|
662
665
|
|
|
663
666
|
// Read local transcript file info to get the contribution hash.
|
|
664
667
|
const transcriptContents = readFile(transcriptLocalFilePath)
|
|
665
|
-
const matchContributionHash = transcriptContents.match(
|
|
668
|
+
const matchContributionHash = transcriptContents.match(contribHashRegex)
|
|
666
669
|
|
|
667
670
|
if (!matchContributionHash)
|
|
668
671
|
showError(COMMAND_ERRORS.COMMAND_CONTRIBUTE_FINALIZE_NO_TRANSCRIPT_CONTRIBUTION_HASH_MATCH, true)
|
package/src/types/index.ts
CHANGED
|
@@ -68,3 +68,78 @@ export type GithubGistFile = {
|
|
|
68
68
|
raw_url: string
|
|
69
69
|
size: number
|
|
70
70
|
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Define the return object of the function that verifies the Bandada membership and proof.
|
|
74
|
+
* @typedef {Object} VerifiedBandadaResponse
|
|
75
|
+
* @property {boolean} valid - true if the proof is valid and the user is a member of the group; otherwise false.
|
|
76
|
+
* @property {string} message - a message describing the result of the verification.
|
|
77
|
+
* @property {string} token - the custom access token.
|
|
78
|
+
*/
|
|
79
|
+
export type VerifiedBandadaResponse = {
|
|
80
|
+
valid: boolean
|
|
81
|
+
message: string
|
|
82
|
+
token: string
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Define the return object of the device code uri request.
|
|
87
|
+
* @typedef {Object} OAuthDeviceCodeResponse
|
|
88
|
+
* @property {string} device_code - the device code.
|
|
89
|
+
* @property {string} user_code - the user code.
|
|
90
|
+
* @property {string} verification_uri - the verification uri.
|
|
91
|
+
* @property {number} expires_in - the expiration time in seconds.
|
|
92
|
+
* @property {number} interval - the interval time in seconds.
|
|
93
|
+
* @property {string} verification_uri_complete - the complete verification uri.
|
|
94
|
+
* @property {string} error - in case there was an error, show the code
|
|
95
|
+
* @property {string} error_description - error details.
|
|
96
|
+
* @property {string} error_uri - error uri.
|
|
97
|
+
*/
|
|
98
|
+
export type OAuthDeviceCodeResponse = {
|
|
99
|
+
device_code: string
|
|
100
|
+
user_code: string
|
|
101
|
+
verification_uri: string
|
|
102
|
+
expires_in: number
|
|
103
|
+
interval: number
|
|
104
|
+
verification_uri_complete: string
|
|
105
|
+
// error response should contain
|
|
106
|
+
error?: string
|
|
107
|
+
error_description?: string
|
|
108
|
+
error_uri?: string
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Define the return object of the polling endpoint
|
|
113
|
+
* @typedef {Object} OAuthTokenResponse
|
|
114
|
+
* @property {string} access_token - the resulting device flow token
|
|
115
|
+
* @property {string} token_type - token type
|
|
116
|
+
* @property {number} expires_in - when does the token expires
|
|
117
|
+
* @property {string} scope - the scope requested by the initial device flow endpoint
|
|
118
|
+
* @property {string} refresh_token - refresh token
|
|
119
|
+
* @property {string} id_token - id token
|
|
120
|
+
* @property {string} error - in case there was an error, show the code
|
|
121
|
+
* @property {string} error_description - error details
|
|
122
|
+
*/
|
|
123
|
+
export type OAuthTokenResponse = {
|
|
124
|
+
access_token: string
|
|
125
|
+
token_type: string
|
|
126
|
+
expires_in: number
|
|
127
|
+
scope: string
|
|
128
|
+
refresh_token: string
|
|
129
|
+
id_token: string
|
|
130
|
+
// error response should contain
|
|
131
|
+
error?: string
|
|
132
|
+
error_description?: string
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* @typedef {Object} CheckNonceOfSIWEAddressResponse
|
|
137
|
+
* @property {boolean} valid - if the checking was valid or not
|
|
138
|
+
* @property {string} message - more information about the validity
|
|
139
|
+
* @property {string} token - token to sign into Firebase
|
|
140
|
+
*/
|
|
141
|
+
export type CheckNonceOfSIWEAddressResponse = {
|
|
142
|
+
valid: boolean
|
|
143
|
+
message: string
|
|
144
|
+
token: string
|
|
145
|
+
}
|