@devtion/devcli 0.0.0-36caea1 → 0.0.0-3df1645
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 +3 -3
- package/dist/index.js +86 -45
- package/dist/types/commands/finalize.d.ts +3 -2
- package/dist/types/lib/utils.d.ts +2 -1
- package/package.json +2 -2
- package/src/commands/auth.ts +20 -9
- package/src/commands/contribute.ts +7 -4
- package/src/commands/finalize.ts +18 -9
- package/src/commands/index.ts +1 -1
- package/src/commands/listCeremonies.ts +1 -2
- package/src/commands/observe.ts +1 -1
- package/src/commands/setup.ts +40 -19
- package/src/commands/validate.ts +1 -2
- package/src/index.ts +16 -8
- package/src/lib/prompts.ts +12 -11
- package/src/lib/services.ts +0 -2
- package/src/lib/utils.ts +40 -6
package/dist/.env
CHANGED
|
@@ -29,9 +29,9 @@ AUTH_GITHUB_CLIENT_ID=e9f8a5fabdfe0d95618c
|
|
|
29
29
|
### AWS S3 bucket used as storage for ceremony artifacts.
|
|
30
30
|
|
|
31
31
|
# The chunk size to be used when executing multi-part upload or downloads.
|
|
32
|
-
# default
|
|
33
|
-
# (e.g. a 200 MB file setting a stream chunk size of
|
|
34
|
-
CONFIG_STREAM_CHUNK_SIZE_IN_MB=
|
|
32
|
+
# default 25 MBs.
|
|
33
|
+
# (e.g. a 200 MB file setting a stream chunk size of 25 MB is going to be splitted and uploaded/downloaded in 4 chunks).
|
|
34
|
+
CONFIG_STREAM_CHUNK_SIZE_IN_MB=25
|
|
35
35
|
# The postfix string for each ceremony bucket.
|
|
36
36
|
# default -ph2-ceremony
|
|
37
37
|
CONFIG_CEREMONY_BUCKET_POSTFIX=-p0tion-development-environment
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* @module @
|
|
5
|
-
* @version 1.0.
|
|
4
|
+
* @module @p0tion/phase2cli
|
|
5
|
+
* @version 1.0.9
|
|
6
6
|
* @file All-in-one interactive command-line for interfacing with zkSNARK Phase 2 Trusted Setup ceremonies
|
|
7
7
|
* @copyright Ethereum Foundation 2022
|
|
8
8
|
* @license MIT
|
|
@@ -365,6 +365,12 @@ const getVerificationKeyLocalFilePath = (completeFilename) => `${verificationKey
|
|
|
365
365
|
* @returns <string> - the complete final verifier contract path to the file.
|
|
366
366
|
*/
|
|
367
367
|
const getVerifierContractLocalFilePath = (completeFilename) => `${verifierContractsLocalFolderPath}/${completeFilename}`;
|
|
368
|
+
/**
|
|
369
|
+
* Get the complete final attestation file path.
|
|
370
|
+
* @param completeFilename <string> - the complete filename of the file (name.ext).
|
|
371
|
+
* @returns <string> - the complete final final attestation path to the file.
|
|
372
|
+
*/
|
|
373
|
+
const getFinalAttestationLocalFilePath = (completeFilename) => `${finalAttestationsLocalFolderPath}/${completeFilename}`;
|
|
368
374
|
/**
|
|
369
375
|
* Get the final transcript file path.
|
|
370
376
|
* @param completeFilename <string> - the complete filename of the file (name.ext).
|
|
@@ -579,8 +585,18 @@ const publishGist = async (token, content, ceremonyTitle, ceremonyPrefix) => {
|
|
|
579
585
|
* @returns <string> - the ready to share tweet url.
|
|
580
586
|
*/
|
|
581
587
|
const generateCustomUrlToTweetAboutParticipation = (ceremonyName, gistUrl, isFinalizing) => isFinalizing
|
|
582
|
-
? `https://twitter.com/intent/tweet?text=I%20have%20finalized%20the%20${ceremonyName}
|
|
583
|
-
|
|
588
|
+
? `https://twitter.com/intent/tweet?text=I%20have%20finalized%20the%20${ceremonyName}${ceremonyName.toLowerCase().includes("trusted") ||
|
|
589
|
+
ceremonyName.toLowerCase().includes("setup") ||
|
|
590
|
+
ceremonyName.toLowerCase().includes("phase2") ||
|
|
591
|
+
ceremonyName.toLowerCase().includes("ceremony")
|
|
592
|
+
? "!"
|
|
593
|
+
: "%20Phase%202%20Trusted%20Setup%20ceremony!"}%20You%20can%20view%20my%20final%20attestation%20here:%20${gistUrl}%20#Ethereum%20#ZKP%20#PSE`
|
|
594
|
+
: `https://twitter.com/intent/tweet?text=I%20contributed%20to%20the%20${ceremonyName}${ceremonyName.toLowerCase().includes("trusted") ||
|
|
595
|
+
ceremonyName.toLowerCase().includes("setup") ||
|
|
596
|
+
ceremonyName.toLowerCase().includes("phase2") ||
|
|
597
|
+
ceremonyName.toLowerCase().includes("ceremony")
|
|
598
|
+
? "!"
|
|
599
|
+
: "%20Phase%202%20Trusted%20Setup%20ceremony!"}%20You%20can%20view%20the%20steps%20to%20contribute%20here:%20https://ceremony.pse.dev%20You%20can%20view%20my%20attestation%20here:%20${gistUrl}%20#Ethereum%20#ZKP`;
|
|
584
600
|
/**
|
|
585
601
|
* Return a custom progress bar.
|
|
586
602
|
* @param type <ProgressBarType> - the type of the progress bar.
|
|
@@ -708,13 +724,14 @@ const getLatestUpdatesFromParticipant = async (firestoreDatabase, ceremonyId, pa
|
|
|
708
724
|
* @param entropyOrBeaconHash <string> - the entropy or beacon hash (only when finalizing) for the contribution.
|
|
709
725
|
* @param contributorOrCoordinatorIdentifier <string> - the identifier of the contributor or coordinator (only when finalizing).
|
|
710
726
|
* @param isFinalizing <boolean> - flag to discriminate between ceremony finalization (true) and contribution (false).
|
|
727
|
+
* @param circuitsLength <number> - the total number of circuits in the ceremony.
|
|
711
728
|
*/
|
|
712
|
-
const handleStartOrResumeContribution = async (cloudFunctions, firestoreDatabase, ceremony, circuit, participant, entropyOrBeaconHash, contributorOrCoordinatorIdentifier, isFinalizing) => {
|
|
729
|
+
const handleStartOrResumeContribution = async (cloudFunctions, firestoreDatabase, ceremony, circuit, participant, entropyOrBeaconHash, contributorOrCoordinatorIdentifier, isFinalizing, circuitsLength) => {
|
|
713
730
|
// Extract data.
|
|
714
731
|
const { prefix: ceremonyPrefix } = ceremony.data;
|
|
715
732
|
const { waitingQueue, avgTimings, prefix: circuitPrefix, sequencePosition } = circuit.data;
|
|
716
733
|
const { completedContributions } = waitingQueue; // = current progress.
|
|
717
|
-
console.log(`${theme.text.bold(`\n- Circuit # ${theme.colors.magenta(`${sequencePosition}`)}`)} (Contribution Steps)`);
|
|
734
|
+
console.log(`${theme.text.bold(`\n- Circuit # ${theme.colors.magenta(`${sequencePosition}/${circuitsLength}`)}`)} (Contribution Steps)`);
|
|
718
735
|
// Get most up-to-date data from the participant document.
|
|
719
736
|
let participantData = await getLatestUpdatesFromParticipant(firestoreDatabase, ceremony.id, participant.id);
|
|
720
737
|
const spinner = customSpinner(`${participantData.contributionStep === "DOWNLOADING" /* ParticipantContributionStep.DOWNLOADING */
|
|
@@ -760,6 +777,7 @@ const handleStartOrResumeContribution = async (cloudFunctions, firestoreDatabase
|
|
|
760
777
|
// Download the latest contribution from bucket.
|
|
761
778
|
await downloadCeremonyArtifact(cloudFunctions, bucketName, lastZkeyStorageFilePath, lastZkeyLocalFilePath);
|
|
762
779
|
console.log(`${theme.symbols.success} Contribution ${theme.text.bold(`#${lastZkeyIndex}`)} correctly downloaded`);
|
|
780
|
+
await sleep(3000);
|
|
763
781
|
// Advance to next contribution step (COMPUTING) if not finalizing.
|
|
764
782
|
if (!isFinalizing) {
|
|
765
783
|
spinner.text = `Preparing for contribution computation...`;
|
|
@@ -787,11 +805,14 @@ const handleStartOrResumeContribution = async (cloudFunctions, firestoreDatabase
|
|
|
787
805
|
showError(COMMAND_ERRORS.COMMAND_CONTRIBUTE_FINALIZE_NO_TRANSCRIPT_CONTRIBUTION_HASH_MATCH, true);
|
|
788
806
|
// Format contribution hash.
|
|
789
807
|
const contributionHash = matchContributionHash?.at(0)?.replace("\n\t\t", "");
|
|
808
|
+
await sleep(500);
|
|
790
809
|
// Make request to cloud functions to permanently store the information.
|
|
791
810
|
await permanentlyStoreCurrentContributionTimeAndHash(cloudFunctions, ceremony.id, computingTime, contributionHash);
|
|
792
811
|
// Format computing time.
|
|
793
812
|
const { seconds: computationSeconds, minutes: computationMinutes, hours: computationHours } = getSecondsMinutesHoursFromMillis(computingTime);
|
|
794
813
|
spinner.succeed(`${isFinalizing ? "Contribution" : `Contribution ${theme.text.bold(`#${nextZkeyIndex}`)}`} computation took ${theme.text.bold(`${convertToDoubleDigits(computationHours)}:${convertToDoubleDigits(computationMinutes)}:${convertToDoubleDigits(computationSeconds)}`)}`);
|
|
814
|
+
// ensure the previous step is completed
|
|
815
|
+
await sleep(5000);
|
|
795
816
|
// Advance to next contribution step (UPLOADING) if not finalizing.
|
|
796
817
|
if (!isFinalizing) {
|
|
797
818
|
spinner.text = `Preparing for uploading the contribution...`;
|
|
@@ -809,10 +830,15 @@ const handleStartOrResumeContribution = async (cloudFunctions, firestoreDatabase
|
|
|
809
830
|
if (isFinalizing || participantData.contributionStep === "UPLOADING" /* ParticipantContributionStep.UPLOADING */) {
|
|
810
831
|
spinner.text = `Uploading ${isFinalizing ? "final" : "your"} contribution ${!isFinalizing ? theme.text.bold(`#${nextZkeyIndex}`) : ""} to storage.\n${theme.symbols.warning} This step may take a while based on circuit size and your internet speed. Everything's fine, just be patient.`;
|
|
811
832
|
spinner.start();
|
|
812
|
-
|
|
813
|
-
|
|
833
|
+
const progressBar = customProgressBar(ProgressBarType.UPLOAD, `your contribution`);
|
|
834
|
+
if (!isFinalizing) {
|
|
835
|
+
await multiPartUpload(cloudFunctions, bucketName, nextZkeyStorageFilePath, nextZkeyLocalFilePath, Number(process.env.CONFIG_STREAM_CHUNK_SIZE_IN_MB), ceremony.id, participantData.tempContributionData, progressBar);
|
|
836
|
+
progressBar.stop();
|
|
837
|
+
}
|
|
814
838
|
else
|
|
815
839
|
await multiPartUpload(cloudFunctions, bucketName, nextZkeyStorageFilePath, nextZkeyLocalFilePath, Number(process.env.CONFIG_STREAM_CHUNK_SIZE_IN_MB));
|
|
840
|
+
// small sleep to ensure the previous step is completed
|
|
841
|
+
await sleep(5000);
|
|
816
842
|
spinner.succeed(`${isFinalizing ? `Contribution` : `Contribution ${theme.text.bold(`#${nextZkeyIndex}`)}`} correctly saved to storage`);
|
|
817
843
|
// Advance to next contribution step (VERIFYING) if not finalizing.
|
|
818
844
|
if (!isFinalizing) {
|
|
@@ -1118,7 +1144,7 @@ const promptCircuitInputData = async (constraintSize, timeoutMechanismType, same
|
|
|
1118
1144
|
let circomVersion = "";
|
|
1119
1145
|
let circomCommitHash = "";
|
|
1120
1146
|
let circuitInputData;
|
|
1121
|
-
let
|
|
1147
|
+
let cfOrVm;
|
|
1122
1148
|
let vmDiskType;
|
|
1123
1149
|
let vmConfigurationType = "";
|
|
1124
1150
|
const questions = [
|
|
@@ -1178,13 +1204,16 @@ const promptCircuitInputData = async (constraintSize, timeoutMechanismType, same
|
|
|
1178
1204
|
const { confirmation } = await askForConfirmation(`The contribution verification can be performed using Cloud Functions (CF, cheaper for small contributions but limited to 1M constraints) or custom virtual machines (expensive but could scale up to 30M constraints). Be aware about VM costs and if you wanna learn more, please visit the documentation to have a complete overview about cost estimation of the two mechanisms.\nChoose the contribution verification mechanism`, `CF`, // eq. true.
|
|
1179
1205
|
`VM` // eq. false.
|
|
1180
1206
|
);
|
|
1181
|
-
|
|
1207
|
+
cfOrVm = confirmation
|
|
1208
|
+
? "CF" /* CircuitContributionVerificationMechanism.CF */
|
|
1209
|
+
: "VM" /* CircuitContributionVerificationMechanism.VM */;
|
|
1182
1210
|
}
|
|
1183
|
-
else
|
|
1184
|
-
|
|
1185
|
-
|
|
1211
|
+
else {
|
|
1212
|
+
cfOrVm = "VM" /* CircuitContributionVerificationMechanism.VM */;
|
|
1213
|
+
}
|
|
1214
|
+
if (cfOrVm === undefined)
|
|
1186
1215
|
showError(COMMAND_ERRORS.COMMAND_ABORT_PROMPT, true);
|
|
1187
|
-
if (
|
|
1216
|
+
if (cfOrVm === "VM" /* CircuitContributionVerificationMechanism.VM */) {
|
|
1188
1217
|
// Ask for selecting the specific VM configuration type.
|
|
1189
1218
|
vmConfigurationType = await promptVMTypeSelector(constraintSize);
|
|
1190
1219
|
// Ask for selecting the specific VM disk (volume) type.
|
|
@@ -1218,9 +1247,7 @@ const promptCircuitInputData = async (constraintSize, timeoutMechanismType, same
|
|
|
1218
1247
|
paramsConfiguration: circuitConfigurationValues
|
|
1219
1248
|
},
|
|
1220
1249
|
verification: {
|
|
1221
|
-
cfOrVm
|
|
1222
|
-
? "CF" /* CircuitContributionVerificationMechanism.CF */
|
|
1223
|
-
: "VM" /* CircuitContributionVerificationMechanism.VM */,
|
|
1250
|
+
cfOrVm,
|
|
1224
1251
|
vm: {
|
|
1225
1252
|
vmConfigurationType,
|
|
1226
1253
|
vmDiskType
|
|
@@ -1256,9 +1283,7 @@ const promptCircuitInputData = async (constraintSize, timeoutMechanismType, same
|
|
|
1256
1283
|
paramsConfiguration: circuitConfigurationValues
|
|
1257
1284
|
},
|
|
1258
1285
|
verification: {
|
|
1259
|
-
cfOrVm
|
|
1260
|
-
? "CF" /* CircuitContributionVerificationMechanism.CF */
|
|
1261
|
-
: "VM" /* CircuitContributionVerificationMechanism.VM */,
|
|
1286
|
+
cfOrVm,
|
|
1262
1287
|
vm: {
|
|
1263
1288
|
vmConfigurationType,
|
|
1264
1289
|
vmDiskType
|
|
@@ -1824,7 +1849,9 @@ const setup = async (cmd) => {
|
|
|
1824
1849
|
let ceremonyId = ""; // The unique identifier of the ceremony.
|
|
1825
1850
|
const { firebaseApp, firebaseFunctions, firestoreDatabase } = await bootstrapCommandExecutionAndServices();
|
|
1826
1851
|
// Check for authentication.
|
|
1827
|
-
const { user, providerUserId } = cmd.auth
|
|
1852
|
+
const { user, providerUserId } = cmd.auth
|
|
1853
|
+
? await authWithToken(firebaseApp, cmd.auth)
|
|
1854
|
+
: await checkAuth(firebaseApp);
|
|
1828
1855
|
// Preserve command execution only for coordinators.
|
|
1829
1856
|
if (!(await isCoordinator(user)))
|
|
1830
1857
|
showError(COMMAND_ERRORS.COMMAND_NOT_COORDINATOR, true);
|
|
@@ -1841,7 +1868,7 @@ const setup = async (cmd) => {
|
|
|
1841
1868
|
// if there is the file option, then set up the non interactively
|
|
1842
1869
|
if (cmd.template) {
|
|
1843
1870
|
// 1. parse the file
|
|
1844
|
-
// tmp data - do not cleanup files as we need them
|
|
1871
|
+
// tmp data - do not cleanup files as we need them
|
|
1845
1872
|
const spinner = customSpinner(`Parsing ${theme.text.bold(cmd.template)} setup configuration file...`, `clock`);
|
|
1846
1873
|
spinner.start();
|
|
1847
1874
|
const setupCeremonyData = await parseCeremonyFile(cmd.template);
|
|
@@ -2097,17 +2124,29 @@ const expirationCountdownForGithubOAuth = (expirationInSeconds) => {
|
|
|
2097
2124
|
*/
|
|
2098
2125
|
const onVerification = async (verification) => {
|
|
2099
2126
|
// Copy code to clipboard.
|
|
2100
|
-
|
|
2101
|
-
|
|
2127
|
+
let noClipboard = false;
|
|
2128
|
+
try {
|
|
2129
|
+
clipboard.writeSync(verification.user_code);
|
|
2130
|
+
clipboard.readSync();
|
|
2131
|
+
}
|
|
2132
|
+
catch (error) {
|
|
2133
|
+
noClipboard = true;
|
|
2134
|
+
}
|
|
2102
2135
|
// Display data.
|
|
2103
2136
|
console.log(`${theme.symbols.warning} Visit ${theme.text.bold(theme.text.underlined(verification.verification_uri))} on this device to generate a new token and authenticate\n`);
|
|
2104
|
-
console.log(theme.colors.magenta(figlet.textSync("Code is Below", { font: "ANSI Shadow" })),
|
|
2105
|
-
|
|
2137
|
+
console.log(theme.colors.magenta(figlet.textSync("Code is Below", { font: "ANSI Shadow" })), "\n");
|
|
2138
|
+
const message = !noClipboard ? `has been copied to your clipboard (${theme.emojis.clipboard})` : ``;
|
|
2139
|
+
console.log(`${theme.symbols.info} Your auth code: ${theme.text.bold(verification.user_code)} ${message} ${theme.symbols.success})\n`);
|
|
2106
2140
|
const spinner = customSpinner(`Redirecting to Github...`, `clock`);
|
|
2107
2141
|
spinner.start();
|
|
2108
2142
|
await sleep(10000); // ~10s to make users able to read the CLI.
|
|
2109
|
-
|
|
2110
|
-
|
|
2143
|
+
try {
|
|
2144
|
+
// Automatically open the page (# Step 2).
|
|
2145
|
+
await open(verification.verification_uri);
|
|
2146
|
+
}
|
|
2147
|
+
catch (error) {
|
|
2148
|
+
console.log(`${theme.symbols.info} Please authenticate via GitHub at ${verification.verification_uri}`);
|
|
2149
|
+
}
|
|
2111
2150
|
spinner.stop();
|
|
2112
2151
|
// Countdown for time expiration.
|
|
2113
2152
|
expirationCountdownForGithubOAuth(verification.expires_in);
|
|
@@ -2553,8 +2592,8 @@ const listenToParticipantDocumentChanges = async (firestoreDatabase, cloudFuncti
|
|
|
2553
2592
|
// Communicate resume / start of the contribution to participant.
|
|
2554
2593
|
await simpleLoader(`${changedContributionStep === "DOWNLOADING" /* ParticipantContributionStep.DOWNLOADING */ ? `Starting` : `Resuming`} your contribution...`, `clock`, 3000);
|
|
2555
2594
|
// Start / Resume the contribution for the participant.
|
|
2556
|
-
await handleStartOrResumeContribution(cloudFunctions, firestoreDatabase, ceremony, circuit, participant, entropy, providerUserId, false // not finalizing.
|
|
2557
|
-
);
|
|
2595
|
+
await handleStartOrResumeContribution(cloudFunctions, firestoreDatabase, ceremony, circuit, participant, entropy, providerUserId, false, // not finalizing.
|
|
2596
|
+
circuits.length);
|
|
2558
2597
|
}
|
|
2559
2598
|
// Scenario (3.A).
|
|
2560
2599
|
else if (isWaitingForContribution)
|
|
@@ -2603,7 +2642,9 @@ const listenToParticipantDocumentChanges = async (firestoreDatabase, cloudFuncti
|
|
|
2603
2642
|
// Get latest contribution verification result.
|
|
2604
2643
|
await getLatestVerificationResult(firestoreDatabase, ceremony.id, circuit.id, participant.id);
|
|
2605
2644
|
// Get next circuit for contribution.
|
|
2606
|
-
const nextCircuit = timeoutExpired
|
|
2645
|
+
const nextCircuit = timeoutExpired
|
|
2646
|
+
? getCircuitBySequencePosition(circuits, changedContributionProgress)
|
|
2647
|
+
: getCircuitBySequencePosition(circuits, changedContributionProgress + 1);
|
|
2607
2648
|
// Check disk space requirements for participant.
|
|
2608
2649
|
const wannaGenerateAttestation = await handleDiskSpaceRequirementForNextContribution(cloudFunctions, ceremony.id, nextCircuit.data.sequencePosition, nextCircuit.data.zKeySizeInBytes, timeoutExpired, providerUserId);
|
|
2609
2650
|
// Check if the participant would like to generate a new attestation.
|
|
@@ -2682,7 +2723,7 @@ const contribute = async (opt) => {
|
|
|
2682
2723
|
const userDoc = await getDocumentById(firestoreDatabase, commonTerms.collections.users.name, user.uid);
|
|
2683
2724
|
const userData = userDoc.data();
|
|
2684
2725
|
if (!userData) {
|
|
2685
|
-
spinner.fail(`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.`);
|
|
2726
|
+
spinner.fail(`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. 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.`);
|
|
2686
2727
|
process.exit(0);
|
|
2687
2728
|
}
|
|
2688
2729
|
// Check the user's current participant readiness for contribution status (eligible, already contributed, timed out).
|
|
@@ -2902,7 +2943,7 @@ const handleVerifierSmartContract = async (cloudFunctions, bucketName, finalZkey
|
|
|
2902
2943
|
const packagePath = `${dirname(fileURLToPath(import.meta.url))}`;
|
|
2903
2944
|
const verifierPath = packagePath.includes(`src/commands`)
|
|
2904
2945
|
? `${dirname(fileURLToPath(import.meta.url))}/../../../../node_modules/snarkjs/templates/verifier_groth16.sol.ejs`
|
|
2905
|
-
: `${dirname(fileURLToPath(import.meta.url))}
|
|
2946
|
+
: `${dirname(fileURLToPath(import.meta.url))}/../node_modules/snarkjs/templates/verifier_groth16.sol.ejs`;
|
|
2906
2947
|
// Export the Solidity verifier smart contract.
|
|
2907
2948
|
const verifierCode = await exportVerifierContract(finalZkeyLocalFilePath, verifierPath);
|
|
2908
2949
|
spinner.text = `Writing verifier smart contract...`;
|
|
@@ -2929,10 +2970,11 @@ const handleVerifierSmartContract = async (cloudFunctions, bucketName, finalZkey
|
|
|
2929
2970
|
* @param participant <FirebaseDocumentInfo> - the Firestore document of the participant (coordinator).
|
|
2930
2971
|
* @param beacon <string> - the value used to compute the final contribution while finalizing the ceremony.
|
|
2931
2972
|
* @param coordinatorIdentifier <string> - the identifier of the coordinator.
|
|
2973
|
+
* @param circuitsLength <number> - the number of circuits in the ceremony.
|
|
2932
2974
|
*/
|
|
2933
|
-
const handleCircuitFinalization = async (cloudFunctions, firestoreDatabase, ceremony, circuit, participant, beacon, coordinatorIdentifier) => {
|
|
2975
|
+
const handleCircuitFinalization = async (cloudFunctions, firestoreDatabase, ceremony, circuit, participant, beacon, coordinatorIdentifier, circuitsLength) => {
|
|
2934
2976
|
// Step (1).
|
|
2935
|
-
await handleStartOrResumeContribution(cloudFunctions, firestoreDatabase, ceremony, circuit, participant, computeSHA256ToHex(beacon), coordinatorIdentifier, true);
|
|
2977
|
+
await handleStartOrResumeContribution(cloudFunctions, firestoreDatabase, ceremony, circuit, participant, computeSHA256ToHex(beacon), coordinatorIdentifier, true, circuitsLength);
|
|
2936
2978
|
await sleep(2000); // workaound for descriptors.
|
|
2937
2979
|
// Extract data.
|
|
2938
2980
|
const { prefix: circuitPrefix } = circuit.data;
|
|
@@ -2967,10 +3009,11 @@ const handleCircuitFinalization = async (cloudFunctions, firestoreDatabase, cere
|
|
|
2967
3009
|
* @dev For proper execution, the command requires the coordinator to be authenticated with a GitHub account (run auth command first) in order to
|
|
2968
3010
|
* handle sybil-resistance and connect to GitHub APIs to publish the gist containing the final public attestation.
|
|
2969
3011
|
*/
|
|
2970
|
-
const finalize = async () => {
|
|
3012
|
+
const finalize = async (opt) => {
|
|
2971
3013
|
const { firebaseApp, firebaseFunctions, firestoreDatabase } = await bootstrapCommandExecutionAndServices();
|
|
2972
3014
|
// Check for authentication.
|
|
2973
|
-
const
|
|
3015
|
+
const auth = opt.auth;
|
|
3016
|
+
const { user, providerUserId, token: coordinatorAccessToken } = auth ? await authWithToken(firebaseApp, auth) : await checkAuth(firebaseApp);
|
|
2974
3017
|
// Preserve command execution only for coordinators.
|
|
2975
3018
|
if (!(await isCoordinator(user)))
|
|
2976
3019
|
showError(COMMAND_ERRORS.COMMAND_NOT_COORDINATOR, true);
|
|
@@ -3005,7 +3048,7 @@ const finalize = async () => {
|
|
|
3005
3048
|
const circuits = await getCeremonyCircuits(firestoreDatabase, selectedCeremony.id);
|
|
3006
3049
|
// Handle finalization for each ceremony circuit.
|
|
3007
3050
|
for await (const circuit of circuits)
|
|
3008
|
-
await handleCircuitFinalization(firebaseFunctions, firestoreDatabase, selectedCeremony, circuit, participant, beacon, providerUserId);
|
|
3051
|
+
await handleCircuitFinalization(firebaseFunctions, firestoreDatabase, selectedCeremony, circuit, participant, beacon, providerUserId, circuits.length);
|
|
3009
3052
|
process.stdout.write(`\n`);
|
|
3010
3053
|
const spinner = customSpinner(`Wrapping up the finalization of the ceremony...`, "clock");
|
|
3011
3054
|
spinner.start();
|
|
@@ -3020,7 +3063,7 @@ const finalize = async () => {
|
|
|
3020
3063
|
// Generate attestation with final contributions.
|
|
3021
3064
|
const publicAttestation = await generateValidContributionsAttestation(firestoreDatabase, circuits, selectedCeremony.id, participant.id, contributions, providerUserId, ceremonyName, true);
|
|
3022
3065
|
// Write public attestation locally.
|
|
3023
|
-
writeFile(
|
|
3066
|
+
writeFile(getFinalAttestationLocalFilePath(`${prefix}_${finalContributionIndex}_${commonTerms.foldersAndPathsTerms.attestation}.log`), Buffer.from(publicAttestation));
|
|
3024
3067
|
await sleep(3000); // workaround for file descriptor unexpected close.
|
|
3025
3068
|
const gistUrl = await publishGist(coordinatorAccessToken, publicAttestation, ceremonyName, prefix);
|
|
3026
3069
|
console.log(`\n${theme.symbols.info} Your public final attestation has been successfully posted as Github Gist (${theme.text.bold(theme.text.underlined(gistUrl))})`);
|
|
@@ -3162,10 +3205,7 @@ program
|
|
|
3162
3205
|
.command("clean")
|
|
3163
3206
|
.description("clean up output generated by commands from the current working directory")
|
|
3164
3207
|
.action(clean);
|
|
3165
|
-
program
|
|
3166
|
-
.command("list")
|
|
3167
|
-
.description("List all ceremonies prefixes")
|
|
3168
|
-
.action(listCeremonies);
|
|
3208
|
+
program.command("list").description("List all ceremonies prefixes").action(listCeremonies);
|
|
3169
3209
|
program
|
|
3170
3210
|
.command("logout")
|
|
3171
3211
|
.description("sign out from Firebae Auth service and delete Github OAuth 2.0 token from local storage")
|
|
@@ -3181,8 +3221,8 @@ const ceremony = program.command("coordinate").description("commands for coordin
|
|
|
3181
3221
|
ceremony
|
|
3182
3222
|
.command("setup")
|
|
3183
3223
|
.description("setup a Groth16 Phase 2 Trusted Setup ceremony for zk-SNARK circuits")
|
|
3184
|
-
.option(
|
|
3185
|
-
.option(
|
|
3224
|
+
.option("-t, --template <path>", "The path to the ceremony setup template", "")
|
|
3225
|
+
.option("-a, --auth <string>", "The Github OAuth 2.0 token", "")
|
|
3186
3226
|
.action(setup);
|
|
3187
3227
|
ceremony
|
|
3188
3228
|
.command("observe")
|
|
@@ -3191,5 +3231,6 @@ ceremony
|
|
|
3191
3231
|
ceremony
|
|
3192
3232
|
.command("finalize")
|
|
3193
3233
|
.description("finalize a Phase2 Trusted Setup ceremony by applying a beacon, exporting verification key and verifier contract")
|
|
3234
|
+
.option("-a, --auth <string>", "the Github OAuth 2.0 token", "")
|
|
3194
3235
|
.action(finalize);
|
|
3195
3236
|
program.parseAsync(process.argv);
|
|
@@ -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;
|
|
@@ -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>;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devtion/devcli",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.0-
|
|
4
|
+
"version": "0.0.0-3df1645",
|
|
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",
|
|
@@ -97,5 +97,5 @@
|
|
|
97
97
|
"publishConfig": {
|
|
98
98
|
"access": "public"
|
|
99
99
|
},
|
|
100
|
-
"gitHead": "
|
|
100
|
+
"gitHead": "6f9aa8c0097076db64ca825e5714f4e149012f03"
|
|
101
101
|
}
|
package/src/commands/auth.ts
CHANGED
|
@@ -63,8 +63,14 @@ export const expirationCountdownForGithubOAuth = (expirationInSeconds: number) =
|
|
|
63
63
|
*/
|
|
64
64
|
export const onVerification = async (verification: Verification): Promise<void> => {
|
|
65
65
|
// Copy code to clipboard.
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
let noClipboard = false
|
|
67
|
+
try {
|
|
68
|
+
clipboard.writeSync(verification.user_code)
|
|
69
|
+
clipboard.readSync()
|
|
70
|
+
} catch (error) {
|
|
71
|
+
noClipboard = true
|
|
72
|
+
}
|
|
73
|
+
|
|
68
74
|
|
|
69
75
|
// Display data.
|
|
70
76
|
console.log(
|
|
@@ -73,12 +79,13 @@ export const onVerification = async (verification: Verification): Promise<void>
|
|
|
73
79
|
)} on this device to generate a new token and authenticate\n`
|
|
74
80
|
)
|
|
75
81
|
|
|
76
|
-
console.log(theme.colors.magenta(figlet.textSync("Code is Below", { font: "ANSI Shadow" })),
|
|
77
|
-
|
|
82
|
+
console.log(theme.colors.magenta(figlet.textSync("Code is Below", { font: "ANSI Shadow" })), "\n")
|
|
83
|
+
|
|
84
|
+
const message = !noClipboard ? `has been copied to your clipboard (${theme.emojis.clipboard})` : ``
|
|
78
85
|
console.log(
|
|
79
|
-
`${theme.symbols.info} Your auth code: ${theme.text.bold(
|
|
80
|
-
|
|
81
|
-
})\n`
|
|
86
|
+
`${theme.symbols.info} Your auth code: ${theme.text.bold(
|
|
87
|
+
verification.user_code
|
|
88
|
+
)} ${message} ${theme.symbols.success})\n`
|
|
82
89
|
)
|
|
83
90
|
|
|
84
91
|
const spinner = customSpinner(`Redirecting to Github...`, `clock`)
|
|
@@ -86,8 +93,12 @@ export const onVerification = async (verification: Verification): Promise<void>
|
|
|
86
93
|
|
|
87
94
|
await sleep(10000) // ~10s to make users able to read the CLI.
|
|
88
95
|
|
|
89
|
-
|
|
90
|
-
|
|
96
|
+
try {
|
|
97
|
+
// Automatically open the page (# Step 2).
|
|
98
|
+
await open(verification.verification_uri)
|
|
99
|
+
} catch (error: any) {
|
|
100
|
+
console.log(`${theme.symbols.info} Please authenticate via GitHub at ${verification.verification_uri}`)
|
|
101
|
+
}
|
|
91
102
|
|
|
92
103
|
spinner.stop()
|
|
93
104
|
|
|
@@ -724,7 +724,8 @@ export const listenToParticipantDocumentChanges = async (
|
|
|
724
724
|
participant,
|
|
725
725
|
entropy,
|
|
726
726
|
providerUserId,
|
|
727
|
-
false // not finalizing.
|
|
727
|
+
false, // not finalizing.
|
|
728
|
+
circuits.length
|
|
728
729
|
)
|
|
729
730
|
}
|
|
730
731
|
// Scenario (3.A).
|
|
@@ -810,7 +811,9 @@ export const listenToParticipantDocumentChanges = async (
|
|
|
810
811
|
await getLatestVerificationResult(firestoreDatabase, ceremony.id, circuit.id, participant.id)
|
|
811
812
|
|
|
812
813
|
// Get next circuit for contribution.
|
|
813
|
-
const nextCircuit = timeoutExpired
|
|
814
|
+
const nextCircuit = timeoutExpired
|
|
815
|
+
? getCircuitBySequencePosition(circuits, changedContributionProgress)
|
|
816
|
+
: getCircuitBySequencePosition(circuits, changedContributionProgress + 1)
|
|
814
817
|
|
|
815
818
|
// Check disk space requirements for participant.
|
|
816
819
|
const wannaGenerateAttestation = await handleDiskSpaceRequirementForNextContribution(
|
|
@@ -894,7 +897,7 @@ const contribute = async (opt: any) => {
|
|
|
894
897
|
// Get options.
|
|
895
898
|
const ceremonyOpt = opt.ceremony
|
|
896
899
|
const entropyOpt = opt.entropy
|
|
897
|
-
const auth = opt.auth
|
|
900
|
+
const auth = opt.auth
|
|
898
901
|
|
|
899
902
|
// Check for authentication.
|
|
900
903
|
const { user, providerUserId, token } = auth ? await authWithToken(firebaseApp, auth) : await checkAuth(firebaseApp)
|
|
@@ -949,7 +952,7 @@ const contribute = async (opt: any) => {
|
|
|
949
952
|
const userData = userDoc.data()
|
|
950
953
|
if (!userData) {
|
|
951
954
|
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.`
|
|
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 elegible 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
956
|
)
|
|
954
957
|
process.exit(0)
|
|
955
958
|
}
|
package/src/commands/finalize.ts
CHANGED
|
@@ -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
|
-
|
|
41
|
+
getFinalAttestationLocalFilePath,
|
|
42
42
|
getFinalZkeyLocalFilePath,
|
|
43
43
|
getVerificationKeyLocalFilePath,
|
|
44
44
|
getVerifierContractLocalFilePath,
|
|
@@ -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))}
|
|
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)
|
|
@@ -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,7 +173,8 @@ 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
180
|
await sleep(2000) // workaound for descriptors.
|
|
@@ -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
|
|
251
|
+
const auth = opt.auth
|
|
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
|
-
|
|
356
|
+
getFinalAttestationLocalFilePath(
|
|
348
357
|
`${prefix}_${finalContributionIndex}_${commonTerms.foldersAndPathsTerms.attestation}.log`
|
|
349
358
|
),
|
|
350
359
|
Buffer.from(publicAttestation)
|
package/src/commands/index.ts
CHANGED
|
@@ -6,4 +6,4 @@ export { default as finalize } from "./finalize.js"
|
|
|
6
6
|
export { default as clean } from "./clean.js"
|
|
7
7
|
export { default as logout } from "./logout.js"
|
|
8
8
|
export { default as validate } from "./validate.js"
|
|
9
|
-
export { default as listCeremonies} from "./listCeremonies.js"
|
|
9
|
+
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/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).
|
package/src/commands/setup.ts
CHANGED
|
@@ -466,7 +466,7 @@ export const handleCircuitArtifactUploadToStorage = async (
|
|
|
466
466
|
* from Hermez's ceremony Phase 1 Reliable Setup Ceremony.
|
|
467
467
|
* @param cmd? <any> - the path to the ceremony setup file.
|
|
468
468
|
*/
|
|
469
|
-
const setup = async (cmd: { template?: string
|
|
469
|
+
const setup = async (cmd: { template?: string; auth?: string }) => {
|
|
470
470
|
// Setup command state.
|
|
471
471
|
const circuits: Array<CircuitDocument> = [] // Circuits.
|
|
472
472
|
let ceremonyId: string = "" // The unique identifier of the ceremony.
|
|
@@ -474,8 +474,10 @@ const setup = async (cmd: { template?: string, auth?: string}) => {
|
|
|
474
474
|
const { firebaseApp, firebaseFunctions, firestoreDatabase } = await bootstrapCommandExecutionAndServices()
|
|
475
475
|
|
|
476
476
|
// Check for authentication.
|
|
477
|
-
const { user, providerUserId } = cmd.auth
|
|
478
|
-
|
|
477
|
+
const { user, providerUserId } = cmd.auth
|
|
478
|
+
? await authWithToken(firebaseApp, cmd.auth)
|
|
479
|
+
: await checkAuth(firebaseApp)
|
|
480
|
+
|
|
479
481
|
// Preserve command execution only for coordinators.
|
|
480
482
|
if (!(await isCoordinator(user))) showError(COMMAND_ERRORS.COMMAND_NOT_COORDINATOR, true)
|
|
481
483
|
|
|
@@ -501,7 +503,7 @@ const setup = async (cmd: { template?: string, auth?: string}) => {
|
|
|
501
503
|
// if there is the file option, then set up the non interactively
|
|
502
504
|
if (cmd.template) {
|
|
503
505
|
// 1. parse the file
|
|
504
|
-
// tmp data - do not cleanup files as we need them
|
|
506
|
+
// tmp data - do not cleanup files as we need them
|
|
505
507
|
const spinner = customSpinner(`Parsing ${theme.text.bold(cmd.template!)} setup configuration file...`, `clock`)
|
|
506
508
|
spinner.start()
|
|
507
509
|
const setupCeremonyData = await parseCeremonyFile(cmd.template!)
|
|
@@ -524,13 +526,26 @@ const setup = async (cmd: { template?: string, auth?: string}) => {
|
|
|
524
526
|
const zkeyLocalPathAndFileName = getZkeyLocalFilePath(circuit.files.initialZkeyFilename)
|
|
525
527
|
|
|
526
528
|
// 2. download the pot and wasm files
|
|
527
|
-
await checkAndDownloadSmallestPowersOfTau(
|
|
528
|
-
|
|
529
|
+
await checkAndDownloadSmallestPowersOfTau(
|
|
530
|
+
convertToDoubleDigits(circuit.metadata?.pot!),
|
|
531
|
+
circuit.files.potFilename
|
|
532
|
+
)
|
|
533
|
+
|
|
529
534
|
// 3. generate the zKey
|
|
530
|
-
const spinner = customSpinner(
|
|
535
|
+
const spinner = customSpinner(
|
|
536
|
+
`Generating genesis zKey for circuit ${theme.text.bold(circuit.name)}...`,
|
|
537
|
+
`clock`
|
|
538
|
+
)
|
|
531
539
|
spinner.start()
|
|
532
|
-
await zKey.newZKey(
|
|
533
|
-
|
|
540
|
+
await zKey.newZKey(
|
|
541
|
+
r1csLocalPathAndFileName,
|
|
542
|
+
getPotLocalFilePath(circuit.files.potFilename),
|
|
543
|
+
zkeyLocalPathAndFileName,
|
|
544
|
+
undefined
|
|
545
|
+
)
|
|
546
|
+
spinner.succeed(
|
|
547
|
+
`Generation of the genesis zKey for citcui ${theme.text.bold(circuit.name)} completed successfully`
|
|
548
|
+
)
|
|
534
549
|
|
|
535
550
|
// 4. calculate the hashes
|
|
536
551
|
const wasmBlake2bHash = await blake512FromPath(wasmLocalPathAndFileName)
|
|
@@ -547,7 +562,7 @@ const setup = async (cmd: { template?: string, auth?: string}) => {
|
|
|
547
562
|
zkeyLocalPathAndFileName,
|
|
548
563
|
circuit.files.initialZkeyFilename
|
|
549
564
|
)
|
|
550
|
-
|
|
565
|
+
|
|
551
566
|
// Check if PoT file has been already uploaded to storage.
|
|
552
567
|
const alreadyUploadedPot = await checkIfObjectExist(
|
|
553
568
|
firebaseFunctions,
|
|
@@ -595,18 +610,24 @@ const setup = async (cmd: { template?: string, auth?: string}) => {
|
|
|
595
610
|
|
|
596
611
|
ceremonySetupData.circuits[index].zKeySizeInBytes = getFileStats(zkeyLocalPathAndFileName).size
|
|
597
612
|
}
|
|
598
|
-
|
|
599
613
|
|
|
600
614
|
// 7. setup the ceremony
|
|
601
|
-
const ceremonyId = await setupCeremony(
|
|
602
|
-
|
|
603
|
-
ceremonySetupData.ceremonyInputData
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
615
|
+
const ceremonyId = await setupCeremony(
|
|
616
|
+
firebaseFunctions,
|
|
617
|
+
ceremonySetupData.ceremonyInputData,
|
|
618
|
+
ceremonySetupData.ceremonyPrefix,
|
|
619
|
+
ceremonySetupData.circuits
|
|
620
|
+
)
|
|
621
|
+
console.log(
|
|
622
|
+
`Congratulations, the setup of ceremony ${theme.text.bold(
|
|
623
|
+
ceremonySetupData.ceremonyInputData.title
|
|
624
|
+
)} (${`UID: ${theme.text.bold(ceremonyId)}`}) has been successfully completed ${
|
|
625
|
+
theme.emojis.tada
|
|
626
|
+
}. You will be able to find all the files and info respectively in the ceremony bucket and database document.`
|
|
627
|
+
)
|
|
628
|
+
|
|
608
629
|
terminate(providerUserId)
|
|
609
|
-
}
|
|
630
|
+
}
|
|
610
631
|
|
|
611
632
|
// Look for R1CS files.
|
|
612
633
|
const r1csFilePaths = await filterDirectoryFilesByExtension(cwd, `.r1cs`)
|
package/src/commands/validate.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { showError } from "../lib/errors.js"
|
|
|
4
4
|
/**
|
|
5
5
|
* Validate ceremony setup command.
|
|
6
6
|
*/
|
|
7
|
-
const validate = async (cmd: { template: string
|
|
7
|
+
const validate = async (cmd: { template: string; constraints?: number }) => {
|
|
8
8
|
try {
|
|
9
9
|
// parse the file and cleanup after
|
|
10
10
|
const parsedFile = await parseCeremonyFile(cmd.template, true)
|
|
@@ -18,7 +18,6 @@ const validate = async (cmd: { template: string, constraints?: number }) => {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
console.log(true)
|
|
21
|
-
|
|
22
21
|
} catch (err: any) {
|
|
23
22
|
showError(`${err.toString()}`, false)
|
|
24
23
|
// we want to exit with a non-zero exit code
|
package/src/index.ts
CHANGED
|
@@ -4,7 +4,17 @@ import { createCommand } from "commander"
|
|
|
4
4
|
import { readFileSync } from "fs"
|
|
5
5
|
import { dirname } from "path"
|
|
6
6
|
import { fileURLToPath } from "url"
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
setup,
|
|
9
|
+
auth,
|
|
10
|
+
contribute,
|
|
11
|
+
observe,
|
|
12
|
+
finalize,
|
|
13
|
+
clean,
|
|
14
|
+
logout,
|
|
15
|
+
validate,
|
|
16
|
+
listCeremonies
|
|
17
|
+
} from "./commands/index.js"
|
|
8
18
|
|
|
9
19
|
// Get pkg info (e.g., name, version).
|
|
10
20
|
const packagePath = `${dirname(fileURLToPath(import.meta.url))}/..`
|
|
@@ -27,10 +37,7 @@ program
|
|
|
27
37
|
.command("clean")
|
|
28
38
|
.description("clean up output generated by commands from the current working directory")
|
|
29
39
|
.action(clean)
|
|
30
|
-
program
|
|
31
|
-
.command("list")
|
|
32
|
-
.description("List all ceremonies prefixes")
|
|
33
|
-
.action(listCeremonies)
|
|
40
|
+
program.command("list").description("List all ceremonies prefixes").action(listCeremonies)
|
|
34
41
|
program
|
|
35
42
|
.command("logout")
|
|
36
43
|
.description("sign out from Firebae Auth service and delete Github OAuth 2.0 token from local storage")
|
|
@@ -48,10 +55,10 @@ const ceremony = program.command("coordinate").description("commands for coordin
|
|
|
48
55
|
ceremony
|
|
49
56
|
.command("setup")
|
|
50
57
|
.description("setup a Groth16 Phase 2 Trusted Setup ceremony for zk-SNARK circuits")
|
|
51
|
-
.option(
|
|
52
|
-
.option(
|
|
58
|
+
.option("-t, --template <path>", "The path to the ceremony setup template", "")
|
|
59
|
+
.option("-a, --auth <string>", "The Github OAuth 2.0 token", "")
|
|
53
60
|
.action(setup)
|
|
54
|
-
|
|
61
|
+
|
|
55
62
|
ceremony
|
|
56
63
|
.command("observe")
|
|
57
64
|
.description("observe in real-time the waiting queue of each ceremony circuit")
|
|
@@ -62,6 +69,7 @@ ceremony
|
|
|
62
69
|
.description(
|
|
63
70
|
"finalize a Phase2 Trusted Setup ceremony by applying a beacon, exporting verification key and verifier contract"
|
|
64
71
|
)
|
|
72
|
+
.option("-a, --auth <string>", "the Github OAuth 2.0 token", "")
|
|
65
73
|
.action(finalize)
|
|
66
74
|
|
|
67
75
|
program.parseAsync(process.argv)
|
package/src/lib/prompts.ts
CHANGED
|
@@ -343,7 +343,7 @@ export const promptCircuitInputData = async (
|
|
|
343
343
|
let circomVersion: string = ""
|
|
344
344
|
let circomCommitHash: string = ""
|
|
345
345
|
let circuitInputData: CircuitInputData
|
|
346
|
-
let
|
|
346
|
+
let cfOrVm: CircuitContributionVerificationMechanism
|
|
347
347
|
let vmDiskType: DiskTypeForVM
|
|
348
348
|
let vmConfigurationType: string = ""
|
|
349
349
|
|
|
@@ -429,12 +429,17 @@ export const promptCircuitInputData = async (
|
|
|
429
429
|
`CF`, // eq. true.
|
|
430
430
|
`VM` // eq. false.
|
|
431
431
|
)
|
|
432
|
-
|
|
433
|
-
|
|
432
|
+
cfOrVm = confirmation
|
|
433
|
+
? CircuitContributionVerificationMechanism.CF
|
|
434
|
+
: CircuitContributionVerificationMechanism.VM
|
|
434
435
|
|
|
435
|
-
|
|
436
|
+
} else {
|
|
437
|
+
cfOrVm = CircuitContributionVerificationMechanism.VM
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
if (cfOrVm === undefined) showError(COMMAND_ERRORS.COMMAND_ABORT_PROMPT, true)
|
|
436
441
|
|
|
437
|
-
if (
|
|
442
|
+
if (cfOrVm === CircuitContributionVerificationMechanism.VM) {
|
|
438
443
|
// Ask for selecting the specific VM configuration type.
|
|
439
444
|
vmConfigurationType = await promptVMTypeSelector(constraintSize)
|
|
440
445
|
|
|
@@ -478,9 +483,7 @@ export const promptCircuitInputData = async (
|
|
|
478
483
|
paramsConfiguration: circuitConfigurationValues
|
|
479
484
|
},
|
|
480
485
|
verification: {
|
|
481
|
-
cfOrVm
|
|
482
|
-
? CircuitContributionVerificationMechanism.CF
|
|
483
|
-
: CircuitContributionVerificationMechanism.VM,
|
|
486
|
+
cfOrVm,
|
|
484
487
|
vm: {
|
|
485
488
|
vmConfigurationType,
|
|
486
489
|
vmDiskType
|
|
@@ -520,9 +523,7 @@ export const promptCircuitInputData = async (
|
|
|
520
523
|
paramsConfiguration: circuitConfigurationValues
|
|
521
524
|
},
|
|
522
525
|
verification: {
|
|
523
|
-
cfOrVm
|
|
524
|
-
? CircuitContributionVerificationMechanism.CF
|
|
525
|
-
: CircuitContributionVerificationMechanism.VM,
|
|
526
|
+
cfOrVm,
|
|
526
527
|
vm: {
|
|
527
528
|
vmConfigurationType,
|
|
528
529
|
vmDiskType
|
package/src/lib/services.ts
CHANGED
package/src/lib/utils.ts
CHANGED
|
@@ -311,8 +311,22 @@ export const generateCustomUrlToTweetAboutParticipation = (
|
|
|
311
311
|
isFinalizing: boolean
|
|
312
312
|
) =>
|
|
313
313
|
isFinalizing
|
|
314
|
-
? `https://twitter.com/intent/tweet?text=I%20have%20finalized%20the%20${ceremonyName}
|
|
315
|
-
|
|
314
|
+
? `https://twitter.com/intent/tweet?text=I%20have%20finalized%20the%20${ceremonyName}${
|
|
315
|
+
ceremonyName.toLowerCase().includes("trusted") ||
|
|
316
|
+
ceremonyName.toLowerCase().includes("setup") ||
|
|
317
|
+
ceremonyName.toLowerCase().includes("phase2") ||
|
|
318
|
+
ceremonyName.toLowerCase().includes("ceremony")
|
|
319
|
+
? "!"
|
|
320
|
+
: "%20Phase%202%20Trusted%20Setup%20ceremony!"
|
|
321
|
+
}%20You%20can%20view%20my%20final%20attestation%20here:%20${gistUrl}%20#Ethereum%20#ZKP%20#PSE`
|
|
322
|
+
: `https://twitter.com/intent/tweet?text=I%20contributed%20to%20the%20${ceremonyName}${
|
|
323
|
+
ceremonyName.toLowerCase().includes("trusted") ||
|
|
324
|
+
ceremonyName.toLowerCase().includes("setup") ||
|
|
325
|
+
ceremonyName.toLowerCase().includes("phase2") ||
|
|
326
|
+
ceremonyName.toLowerCase().includes("ceremony")
|
|
327
|
+
? "!"
|
|
328
|
+
: "%20Phase%202%20Trusted%20Setup%20ceremony!"
|
|
329
|
+
}%20You%20can%20view%20the%20steps%20to%20contribute%20here:%20https://ceremony.pse.dev%20You%20can%20view%20my%20attestation%20here:%20${gistUrl}%20#Ethereum%20#ZKP`
|
|
316
330
|
|
|
317
331
|
/**
|
|
318
332
|
* Return a custom progress bar.
|
|
@@ -521,6 +535,7 @@ export const getLatestUpdatesFromParticipant = async (
|
|
|
521
535
|
* @param entropyOrBeaconHash <string> - the entropy or beacon hash (only when finalizing) for the contribution.
|
|
522
536
|
* @param contributorOrCoordinatorIdentifier <string> - the identifier of the contributor or coordinator (only when finalizing).
|
|
523
537
|
* @param isFinalizing <boolean> - flag to discriminate between ceremony finalization (true) and contribution (false).
|
|
538
|
+
* @param circuitsLength <number> - the total number of circuits in the ceremony.
|
|
524
539
|
*/
|
|
525
540
|
export const handleStartOrResumeContribution = async (
|
|
526
541
|
cloudFunctions: Functions,
|
|
@@ -530,7 +545,8 @@ export const handleStartOrResumeContribution = async (
|
|
|
530
545
|
participant: FirebaseDocumentInfo,
|
|
531
546
|
entropyOrBeaconHash: any,
|
|
532
547
|
contributorOrCoordinatorIdentifier: string,
|
|
533
|
-
isFinalizing: boolean
|
|
548
|
+
isFinalizing: boolean,
|
|
549
|
+
circuitsLength: number
|
|
534
550
|
): Promise<void> => {
|
|
535
551
|
// Extract data.
|
|
536
552
|
const { prefix: ceremonyPrefix } = ceremony.data
|
|
@@ -538,7 +554,9 @@ export const handleStartOrResumeContribution = async (
|
|
|
538
554
|
const { completedContributions } = waitingQueue // = current progress.
|
|
539
555
|
|
|
540
556
|
console.log(
|
|
541
|
-
`${theme.text.bold(
|
|
557
|
+
`${theme.text.bold(
|
|
558
|
+
`\n- Circuit # ${theme.colors.magenta(`${sequencePosition}/${circuitsLength}`)}`
|
|
559
|
+
)} (Contribution Steps)`
|
|
542
560
|
)
|
|
543
561
|
|
|
544
562
|
// Get most up-to-date data from the participant document.
|
|
@@ -607,6 +625,8 @@ export const handleStartOrResumeContribution = async (
|
|
|
607
625
|
`${theme.symbols.success} Contribution ${theme.text.bold(`#${lastZkeyIndex}`)} correctly downloaded`
|
|
608
626
|
)
|
|
609
627
|
|
|
628
|
+
await sleep(3000)
|
|
629
|
+
|
|
610
630
|
// Advance to next contribution step (COMPUTING) if not finalizing.
|
|
611
631
|
if (!isFinalizing) {
|
|
612
632
|
spinner.text = `Preparing for contribution computation...`
|
|
@@ -650,6 +670,8 @@ export const handleStartOrResumeContribution = async (
|
|
|
650
670
|
// Format contribution hash.
|
|
651
671
|
const contributionHash = matchContributionHash?.at(0)?.replace("\n\t\t", "")!
|
|
652
672
|
|
|
673
|
+
await sleep(500)
|
|
674
|
+
|
|
653
675
|
// Make request to cloud functions to permanently store the information.
|
|
654
676
|
await permanentlyStoreCurrentContributionTimeAndHash(
|
|
655
677
|
cloudFunctions,
|
|
@@ -675,6 +697,9 @@ export const handleStartOrResumeContribution = async (
|
|
|
675
697
|
)}`
|
|
676
698
|
)
|
|
677
699
|
|
|
700
|
+
// ensure the previous step is completed
|
|
701
|
+
await sleep(5000)
|
|
702
|
+
|
|
678
703
|
// Advance to next contribution step (UPLOADING) if not finalizing.
|
|
679
704
|
if (!isFinalizing) {
|
|
680
705
|
spinner.text = `Preparing for uploading the contribution...`
|
|
@@ -699,7 +724,9 @@ export const handleStartOrResumeContribution = async (
|
|
|
699
724
|
} This step may take a while based on circuit size and your internet speed. Everything's fine, just be patient.`
|
|
700
725
|
spinner.start()
|
|
701
726
|
|
|
702
|
-
|
|
727
|
+
const progressBar = customProgressBar(ProgressBarType.UPLOAD, `your contribution`)
|
|
728
|
+
|
|
729
|
+
if (!isFinalizing) {
|
|
703
730
|
await multiPartUpload(
|
|
704
731
|
cloudFunctions,
|
|
705
732
|
bucketName,
|
|
@@ -707,8 +734,12 @@ export const handleStartOrResumeContribution = async (
|
|
|
707
734
|
nextZkeyLocalFilePath,
|
|
708
735
|
Number(process.env.CONFIG_STREAM_CHUNK_SIZE_IN_MB),
|
|
709
736
|
ceremony.id,
|
|
710
|
-
participantData.tempContributionData
|
|
737
|
+
participantData.tempContributionData,
|
|
738
|
+
progressBar
|
|
711
739
|
)
|
|
740
|
+
|
|
741
|
+
progressBar.stop()
|
|
742
|
+
}
|
|
712
743
|
else
|
|
713
744
|
await multiPartUpload(
|
|
714
745
|
cloudFunctions,
|
|
@@ -718,6 +749,9 @@ export const handleStartOrResumeContribution = async (
|
|
|
718
749
|
Number(process.env.CONFIG_STREAM_CHUNK_SIZE_IN_MB)
|
|
719
750
|
)
|
|
720
751
|
|
|
752
|
+
// small sleep to ensure the previous step is completed
|
|
753
|
+
await sleep(5000)
|
|
754
|
+
|
|
721
755
|
spinner.succeed(
|
|
722
756
|
`${
|
|
723
757
|
isFinalizing ? `Contribution` : `Contribution ${theme.text.bold(`#${nextZkeyIndex}`)}`
|