@devtion/devcli 0.0.0-a6dcd68 → 0.0.0-c749be4
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/LICENSE +21 -0
- package/README.md +3 -1
- package/dist/.env +40 -39
- package/dist/index.js +108 -78
- package/dist/types/commands/contribute.d.ts +1 -1
- package/dist/types/commands/finalize.d.ts +4 -3
- package/dist/types/commands/observe.d.ts +1 -1
- package/dist/types/commands/setup.d.ts +2 -2
- package/dist/types/lib/prompts.d.ts +6 -6
- package/dist/types/lib/utils.d.ts +3 -2
- package/package.json +5 -4
- package/src/commands/auth.ts +17 -7
- package/src/commands/contribute.ts +12 -8
- package/src/commands/finalize.ts +19 -10
- package/src/commands/index.ts +1 -1
- package/src/commands/listCeremonies.ts +2 -3
- package/src/commands/observe.ts +3 -3
- package/src/commands/setup.ts +44 -46
- package/src/commands/validate.ts +2 -3
- package/src/index.ts +17 -8
- package/src/lib/localConfigs.ts +1 -1
- package/src/lib/prompts.ts +20 -20
- package/src/lib/services.ts +1 -3
- package/src/lib/utils.ts +42 -9
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 Ethereum Foundation
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -61,11 +61,13 @@ or run specific commands with `npx`:
|
|
|
61
61
|
npx @p0tion/phase2cli contribute
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
+
> Please note that phase2cli only runs on Linux or Mac systems. If a Windows user, please install [WSL2](https://learn.microsoft.com/en-us/windows/wsl/install) first.
|
|
65
|
+
|
|
64
66
|
## 📜 Usage
|
|
65
67
|
|
|
66
68
|
### Local Development
|
|
67
69
|
|
|
68
|
-
**
|
|
70
|
+
**Prerequisites**
|
|
69
71
|
|
|
70
72
|
- Node.js version 16.0 or higher.
|
|
71
73
|
- Yarn version 3.5.0 or higher.
|
package/dist/.env
CHANGED
|
@@ -1,40 +1,41 @@
|
|
|
1
|
-
### FIREBASE ###
|
|
2
|
-
### These configs are related to the configuration of the Firebase services.
|
|
1
|
+
### FIREBASE ###
|
|
2
|
+
### These configs are related to the configuration of the Firebase services.
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
# The Firebase Application API key for making request against the services.
|
|
6
|
+
# nb. this is going to be auto-generated when creating a new application.
|
|
7
|
+
FIREBASE_API_KEY=AIzaSyDVAu8U4zBpM3FFLPyktSjZnCmg1IR73Cg
|
|
8
|
+
# The URL to Firebase Authentication service (should point to default).
|
|
9
|
+
# nb. this is going to be auto-generated when creating a new application.
|
|
10
|
+
FIREBASE_AUTH_DOMAIN=p0tion-ci-environment.firebaseapp.com
|
|
11
|
+
# The Firebase Application project id (should match with application name).
|
|
12
|
+
FIREBASE_PROJECT_ID=p0tion-ci-environment
|
|
13
|
+
# The Firebase unique message sender identifier (to recognize the application user).
|
|
14
|
+
# nb. this is going to be auto-generated when creating a new application.
|
|
15
|
+
FIREBASE_MESSAGING_SENDER_ID=22680510841
|
|
16
|
+
# The Firebase unique identifier for your application.
|
|
17
|
+
# nb. this is going to be auto-generated when creating a new application.
|
|
18
|
+
FIREBASE_APP_ID=1:22680510841:web:529a664e73dbabd1c7cfa8
|
|
19
|
+
FIREBASE_CF_URL_VERIFY_CONTRIBUTION=https://verifycontribution-mq4aqokliq-ew.a.run.app
|
|
20
|
+
|
|
21
|
+
### AUTHENTICATION ###
|
|
22
|
+
### These configs are related to the authentication of users.
|
|
23
|
+
|
|
24
|
+
# The unique identifier for the Github client associated to the OAuth Application.
|
|
25
|
+
AUTH_GITHUB_CLIENT_ID=e9f8a5fabdfe0d95618c
|
|
26
|
+
|
|
27
|
+
### AWS S3 STORAGE ###
|
|
28
|
+
### These configs are related to the configuration of the interaction with the
|
|
29
|
+
### AWS S3 bucket used as storage for ceremony artifacts.
|
|
30
|
+
|
|
31
|
+
# The chunk size to be used when executing multi-part upload or downloads.
|
|
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
|
+
# The postfix string for each ceremony bucket.
|
|
36
|
+
# default -ph2-ceremony
|
|
37
|
+
CONFIG_CEREMONY_BUCKET_POSTFIX=-p0tion-development-environment
|
|
38
|
+
# The amount of time in seconds which indicates the duration about the validity of a pre-signed URL.
|
|
39
|
+
# default: 7200 seconds = 2 hours.
|
|
40
|
+
CONFIG_PRESIGNED_URL_EXPIRATION_IN_SECONDS=7200
|
|
3
41
|
|
|
4
|
-
|
|
5
|
-
# The Firebase Application API key for making request against the services.
|
|
6
|
-
# nb. this is going to be auto-generated when creating a new application.
|
|
7
|
-
FIREBASE_API_KEY="AIzaSyC_m2yETFfuJ_dO5mtQfyU6qlc8k3CTFUY"
|
|
8
|
-
# The URL to Firebase Authentication service (should point to default).
|
|
9
|
-
# nb. this is going to be auto-generated when creating a new application.
|
|
10
|
-
FIREBASE_AUTH_DOMAIN="pse-p0tion-production.firebaseapp.com"
|
|
11
|
-
# The Firebase Application project id (should match with application name).
|
|
12
|
-
FIREBASE_PROJECT_ID="pse-p0tion-production"
|
|
13
|
-
# The Firebase unique message sender identifier (to recognize the application user).
|
|
14
|
-
# nb. this is going to be auto-generated when creating a new application.
|
|
15
|
-
FIREBASE_MESSAGING_SENDER_ID="791425100143"
|
|
16
|
-
# The Firebase unique identifier for your application.
|
|
17
|
-
# nb. this is going to be auto-generated when creating a new application.
|
|
18
|
-
FIREBASE_APP_ID="1:791425100143:web:86313c4d311c8f6014a09a"
|
|
19
|
-
FIREBASE_CF_URL_VERIFY_CONTRIBUTION="https://verifycontribution-h3mhz7ox4a-ew.a.run.app"
|
|
20
|
-
|
|
21
|
-
### AUTHENTICATION ###
|
|
22
|
-
### These configs are related to the authentication of users.
|
|
23
|
-
|
|
24
|
-
# The unique identifier for the Github client associated to the OAuth Application.
|
|
25
|
-
AUTH_GITHUB_CLIENT_ID="057a55ac6fe383cfcc3a"
|
|
26
|
-
|
|
27
|
-
### AWS S3 STORAGE ###
|
|
28
|
-
### These configs are related to the configuration of the interaction with the
|
|
29
|
-
### AWS S3 bucket used as storage for ceremony artifacts.
|
|
30
|
-
|
|
31
|
-
# The chunk size to be used when executing multi-part upload or downloads.
|
|
32
|
-
# default 50 MBs.
|
|
33
|
-
# (e.g. a 200 MB file setting a stream chunk size of 50 MB is going to be splitted and uploaded/downloaded in 4 chunks).
|
|
34
|
-
CONFIG_STREAM_CHUNK_SIZE_IN_MB=50
|
|
35
|
-
# The postfix string for each ceremony bucket.
|
|
36
|
-
# default "-ph2-ceremony"
|
|
37
|
-
CONFIG_CEREMONY_BUCKET_POSTFIX="-pse-p0tion-production"
|
|
38
|
-
# The amount of time in seconds which indicates the duration about the validity of a pre-signed URL.
|
|
39
|
-
# default: 7200 seconds = 2 hours.
|
|
40
|
-
CONFIG_PRESIGNED_URL_EXPIRATION_IN_SECONDS=7200
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @module @p0tion/phase2cli
|
|
5
|
-
* @version 1.0
|
|
5
|
+
* @version 1.1.0
|
|
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
|
|
@@ -17,8 +17,7 @@ import boxen from 'boxen';
|
|
|
17
17
|
import { pipeline } from 'node:stream';
|
|
18
18
|
import { promisify } from 'node:util';
|
|
19
19
|
import fetch$1 from 'node-fetch';
|
|
20
|
-
import {
|
|
21
|
-
import { commonTerms, formatZkeyIndex, getZkeyStorageFilePath, finalContributionIndex, createCustomLoggerForFile, getBucketName, progressToNextContributionStep, permanentlyStoreCurrentContributionTimeAndHash, convertToDoubleDigits, multiPartUpload, verifyContribution, generateGetObjectPreSignedUrl, convertBytesOrKbToGb, numExpIterations, getDocumentById, getParticipantsCollectionPath, fromQueryToFirebaseDocumentInfo, getAllCollectionDocs, extractPrefix, autoGenerateEntropy, vmConfigurationTypes, initializeFirebaseCoreServices, signInToFirebaseWithCredentials, getCurrentFirebaseAuthUser, isCoordinator, parseCeremonyFile, blake512FromPath, checkIfObjectExist, setupCeremony, genesisZkeyIndex, getR1csStorageFilePath, getWasmStorageFilePath, getPotStorageFilePath, extractPoTFromFilename, potFileDownloadMainUrl, createS3Bucket, potFilenameTemplate, getR1CSInfo, getOpenedCeremonies, getCeremonyCircuits, checkParticipantForCeremony, getCurrentActiveParticipantTimeout, getCircuitBySequencePosition, getCircuitContributionsFromContributor, progressToNextCircuitForContribution, resumeContributionAfterTimeoutExpiration, generateValidContributionsAttestation, getContributionsValidityForContributor, getClosedCeremonies, checkAndPrepareCoordinatorForFinalization, computeSHA256ToHex, finalizeCeremony, getVerificationKeyStorageFilePath, verificationKeyAcronym, getVerifierContractStorageFilePath, verifierSmartContractAcronym, finalizeCircuit, exportVkey, exportVerifierContract } from '@p0tion/actions';
|
|
20
|
+
import { commonTerms, formatZkeyIndex, getZkeyStorageFilePath, finalContributionIndex, createCustomLoggerForFile, getBucketName, progressToNextContributionStep, permanentlyStoreCurrentContributionTimeAndHash, convertToDoubleDigits, multiPartUpload, verifyContribution, generateGetObjectPreSignedUrl, convertBytesOrKbToGb, numExpIterations, getDocumentById, getParticipantsCollectionPath, fromQueryToFirebaseDocumentInfo, getAllCollectionDocs, extractPrefix, autoGenerateEntropy, vmConfigurationTypes, initializeFirebaseCoreServices, signInToFirebaseWithCredentials, getCurrentFirebaseAuthUser, isCoordinator, parseCeremonyFile, blake512FromPath, checkIfObjectExist, setupCeremony, genesisZkeyIndex, getR1csStorageFilePath, getWasmStorageFilePath, getPotStorageFilePath, extractPoTFromFilename, potFileDownloadMainUrl, createS3Bucket, potFilenameTemplate, getR1CSInfo, getOpenedCeremonies, getCeremonyCircuits, checkParticipantForCeremony, getCurrentActiveParticipantTimeout, getCircuitBySequencePosition, getCircuitContributionsFromContributor, progressToNextCircuitForContribution, resumeContributionAfterTimeoutExpiration, generateValidContributionsAttestation, getContributionsValidityForContributor, getClosedCeremonies, checkAndPrepareCoordinatorForFinalization, computeSHA256ToHex, finalizeCeremony, getVerificationKeyStorageFilePath, verificationKeyAcronym, getVerifierContractStorageFilePath, verifierSmartContractAcronym, finalizeCircuit, exportVkey, exportVerifierContract } from '@devtion/actions';
|
|
22
21
|
import fetch from '@adobe/node-fetch-retry';
|
|
23
22
|
import { request } from '@octokit/request';
|
|
24
23
|
import { SingleBar, Presets } from 'cli-progress';
|
|
@@ -34,7 +33,6 @@ import Conf from 'conf';
|
|
|
34
33
|
import prompts from 'prompts';
|
|
35
34
|
import clear from 'clear';
|
|
36
35
|
import figlet from 'figlet';
|
|
37
|
-
import { Readable } from 'stream';
|
|
38
36
|
import { createOAuthDeviceAuth } from '@octokit/auth-oauth-device';
|
|
39
37
|
import clipboard from 'clipboardy';
|
|
40
38
|
import open from 'open';
|
|
@@ -367,6 +365,12 @@ const getVerificationKeyLocalFilePath = (completeFilename) => `${verificationKey
|
|
|
367
365
|
* @returns <string> - the complete final verifier contract path to the file.
|
|
368
366
|
*/
|
|
369
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}`;
|
|
370
374
|
/**
|
|
371
375
|
* Get the final transcript file path.
|
|
372
376
|
* @param completeFilename <string> - the complete filename of the file (name.ext).
|
|
@@ -581,8 +585,18 @@ const publishGist = async (token, content, ceremonyTitle, ceremonyPrefix) => {
|
|
|
581
585
|
* @returns <string> - the ready to share tweet url.
|
|
582
586
|
*/
|
|
583
587
|
const generateCustomUrlToTweetAboutParticipation = (ceremonyName, gistUrl, isFinalizing) => isFinalizing
|
|
584
|
-
? `https://twitter.com/intent/tweet?text=I%20have%20finalized%20the%20${ceremonyName}
|
|
585
|
-
|
|
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`;
|
|
586
600
|
/**
|
|
587
601
|
* Return a custom progress bar.
|
|
588
602
|
* @param type <ProgressBarType> - the type of the progress bar.
|
|
@@ -710,13 +724,14 @@ const getLatestUpdatesFromParticipant = async (firestoreDatabase, ceremonyId, pa
|
|
|
710
724
|
* @param entropyOrBeaconHash <string> - the entropy or beacon hash (only when finalizing) for the contribution.
|
|
711
725
|
* @param contributorOrCoordinatorIdentifier <string> - the identifier of the contributor or coordinator (only when finalizing).
|
|
712
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.
|
|
713
728
|
*/
|
|
714
|
-
const handleStartOrResumeContribution = async (cloudFunctions, firestoreDatabase, ceremony, circuit, participant, entropyOrBeaconHash, contributorOrCoordinatorIdentifier, isFinalizing) => {
|
|
729
|
+
const handleStartOrResumeContribution = async (cloudFunctions, firestoreDatabase, ceremony, circuit, participant, entropyOrBeaconHash, contributorOrCoordinatorIdentifier, isFinalizing, circuitsLength) => {
|
|
715
730
|
// Extract data.
|
|
716
731
|
const { prefix: ceremonyPrefix } = ceremony.data;
|
|
717
732
|
const { waitingQueue, avgTimings, prefix: circuitPrefix, sequencePosition } = circuit.data;
|
|
718
733
|
const { completedContributions } = waitingQueue; // = current progress.
|
|
719
|
-
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)`);
|
|
720
735
|
// Get most up-to-date data from the participant document.
|
|
721
736
|
let participantData = await getLatestUpdatesFromParticipant(firestoreDatabase, ceremony.id, participant.id);
|
|
722
737
|
const spinner = customSpinner(`${participantData.contributionStep === "DOWNLOADING" /* ParticipantContributionStep.DOWNLOADING */
|
|
@@ -762,6 +777,7 @@ const handleStartOrResumeContribution = async (cloudFunctions, firestoreDatabase
|
|
|
762
777
|
// Download the latest contribution from bucket.
|
|
763
778
|
await downloadCeremonyArtifact(cloudFunctions, bucketName, lastZkeyStorageFilePath, lastZkeyLocalFilePath);
|
|
764
779
|
console.log(`${theme.symbols.success} Contribution ${theme.text.bold(`#${lastZkeyIndex}`)} correctly downloaded`);
|
|
780
|
+
await sleep(3000);
|
|
765
781
|
// Advance to next contribution step (COMPUTING) if not finalizing.
|
|
766
782
|
if (!isFinalizing) {
|
|
767
783
|
spinner.text = `Preparing for contribution computation...`;
|
|
@@ -789,11 +805,14 @@ const handleStartOrResumeContribution = async (cloudFunctions, firestoreDatabase
|
|
|
789
805
|
showError(COMMAND_ERRORS.COMMAND_CONTRIBUTE_FINALIZE_NO_TRANSCRIPT_CONTRIBUTION_HASH_MATCH, true);
|
|
790
806
|
// Format contribution hash.
|
|
791
807
|
const contributionHash = matchContributionHash?.at(0)?.replace("\n\t\t", "");
|
|
808
|
+
await sleep(500);
|
|
792
809
|
// Make request to cloud functions to permanently store the information.
|
|
793
810
|
await permanentlyStoreCurrentContributionTimeAndHash(cloudFunctions, ceremony.id, computingTime, contributionHash);
|
|
794
811
|
// Format computing time.
|
|
795
812
|
const { seconds: computationSeconds, minutes: computationMinutes, hours: computationHours } = getSecondsMinutesHoursFromMillis(computingTime);
|
|
796
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);
|
|
797
816
|
// Advance to next contribution step (UPLOADING) if not finalizing.
|
|
798
817
|
if (!isFinalizing) {
|
|
799
818
|
spinner.text = `Preparing for uploading the contribution...`;
|
|
@@ -809,12 +828,17 @@ const handleStartOrResumeContribution = async (cloudFunctions, firestoreDatabase
|
|
|
809
828
|
console.log(`${theme.symbols.success} Contribution ${theme.text.bold(`#${nextZkeyIndex}`)} already computed`);
|
|
810
829
|
// Contribution step = UPLOADING.
|
|
811
830
|
if (isFinalizing || participantData.contributionStep === "UPLOADING" /* ParticipantContributionStep.UPLOADING */) {
|
|
812
|
-
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
|
|
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.`;
|
|
813
832
|
spinner.start();
|
|
814
|
-
|
|
815
|
-
|
|
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
|
+
}
|
|
816
838
|
else
|
|
817
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);
|
|
818
842
|
spinner.succeed(`${isFinalizing ? `Contribution` : `Contribution ${theme.text.bold(`#${nextZkeyIndex}`)}`} correctly saved to storage`);
|
|
819
843
|
// Advance to next contribution step (VERIFYING) if not finalizing.
|
|
820
844
|
if (!isFinalizing) {
|
|
@@ -992,7 +1016,7 @@ const promptCircomCompiler = async () => {
|
|
|
992
1016
|
* Shows a list of circuits for a single option selection.
|
|
993
1017
|
* @dev the circuit names are derived from local R1CS files.
|
|
994
1018
|
* @param options <Array<string>> - an array of circuits names.
|
|
995
|
-
* @returns Promise<string> - the name of the
|
|
1019
|
+
* @returns Promise<string> - the name of the chosen circuit.
|
|
996
1020
|
*/
|
|
997
1021
|
const promptCircuitSelector = async (options) => {
|
|
998
1022
|
const { circuitFilename } = await prompts({
|
|
@@ -1010,7 +1034,7 @@ const promptCircuitSelector = async (options) => {
|
|
|
1010
1034
|
* Shows a list of standard EC2 VM instance types for a single option selection.
|
|
1011
1035
|
* @notice the suggested VM configuration type is calculated based on circuit constraint size.
|
|
1012
1036
|
* @param constraintSize <number> - the amount of circuit constraints
|
|
1013
|
-
* @returns Promise<string> - the name of the
|
|
1037
|
+
* @returns Promise<string> - the name of the chosen VM type.
|
|
1014
1038
|
*/
|
|
1015
1039
|
const promptVMTypeSelector = async (constraintSize) => {
|
|
1016
1040
|
let suggestedConfiguration = 0;
|
|
@@ -1107,7 +1131,7 @@ const promptVMDiskTypeSelector = async () => {
|
|
|
1107
1131
|
/**
|
|
1108
1132
|
* Show a series of questions about the circuits.
|
|
1109
1133
|
* @param constraintSize <number> - the amount of circuit constraints.
|
|
1110
|
-
* @param timeoutMechanismType <CeremonyTimeoutType> - the
|
|
1134
|
+
* @param timeoutMechanismType <CeremonyTimeoutType> - the chosen timeout mechanism type for the ceremony.
|
|
1111
1135
|
* @param needPromptCircomCompiler <boolean> - a boolean value indicating if the questions related to the Circom compiler version and commit hash must be asked.
|
|
1112
1136
|
* @param enforceVM <boolean> - a boolean value indicating if the contribution verification could be supported by VM-only approach or not.
|
|
1113
1137
|
* @returns Promise<Array<Circuit>> - circuit info prompted by the coordinator.
|
|
@@ -1120,7 +1144,7 @@ const promptCircuitInputData = async (constraintSize, timeoutMechanismType, same
|
|
|
1120
1144
|
let circomVersion = "";
|
|
1121
1145
|
let circomCommitHash = "";
|
|
1122
1146
|
let circuitInputData;
|
|
1123
|
-
let
|
|
1147
|
+
let cfOrVm;
|
|
1124
1148
|
let vmDiskType;
|
|
1125
1149
|
let vmConfigurationType = "";
|
|
1126
1150
|
const questions = [
|
|
@@ -1175,18 +1199,21 @@ const promptCircuitInputData = async (constraintSize, timeoutMechanismType, same
|
|
|
1175
1199
|
circomVersion = version;
|
|
1176
1200
|
circomCommitHash = commitHash;
|
|
1177
1201
|
}
|
|
1178
|
-
// Ask for
|
|
1202
|
+
// Ask for preferred contribution verification method (CF vs VM).
|
|
1179
1203
|
if (!enforceVM) {
|
|
1180
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.
|
|
1181
1205
|
`VM` // eq. false.
|
|
1182
1206
|
);
|
|
1183
|
-
|
|
1207
|
+
cfOrVm = confirmation
|
|
1208
|
+
? "CF" /* CircuitContributionVerificationMechanism.CF */
|
|
1209
|
+
: "VM" /* CircuitContributionVerificationMechanism.VM */;
|
|
1184
1210
|
}
|
|
1185
|
-
else
|
|
1186
|
-
|
|
1187
|
-
|
|
1211
|
+
else {
|
|
1212
|
+
cfOrVm = "VM" /* CircuitContributionVerificationMechanism.VM */;
|
|
1213
|
+
}
|
|
1214
|
+
if (cfOrVm === undefined)
|
|
1188
1215
|
showError(COMMAND_ERRORS.COMMAND_ABORT_PROMPT, true);
|
|
1189
|
-
if (
|
|
1216
|
+
if (cfOrVm === "VM" /* CircuitContributionVerificationMechanism.VM */) {
|
|
1190
1217
|
// Ask for selecting the specific VM configuration type.
|
|
1191
1218
|
vmConfigurationType = await promptVMTypeSelector(constraintSize);
|
|
1192
1219
|
// Ask for selecting the specific VM disk (volume) type.
|
|
@@ -1220,9 +1247,7 @@ const promptCircuitInputData = async (constraintSize, timeoutMechanismType, same
|
|
|
1220
1247
|
paramsConfiguration: circuitConfigurationValues
|
|
1221
1248
|
},
|
|
1222
1249
|
verification: {
|
|
1223
|
-
cfOrVm
|
|
1224
|
-
? "CF" /* CircuitContributionVerificationMechanism.CF */
|
|
1225
|
-
: "VM" /* CircuitContributionVerificationMechanism.VM */,
|
|
1250
|
+
cfOrVm,
|
|
1226
1251
|
vm: {
|
|
1227
1252
|
vmConfigurationType,
|
|
1228
1253
|
vmDiskType
|
|
@@ -1258,9 +1283,7 @@ const promptCircuitInputData = async (constraintSize, timeoutMechanismType, same
|
|
|
1258
1283
|
paramsConfiguration: circuitConfigurationValues
|
|
1259
1284
|
},
|
|
1260
1285
|
verification: {
|
|
1261
|
-
cfOrVm
|
|
1262
|
-
? "CF" /* CircuitContributionVerificationMechanism.CF */
|
|
1263
|
-
: "VM" /* CircuitContributionVerificationMechanism.VM */,
|
|
1286
|
+
cfOrVm,
|
|
1264
1287
|
vm: {
|
|
1265
1288
|
vmConfigurationType,
|
|
1266
1289
|
vmDiskType
|
|
@@ -1304,7 +1327,7 @@ const promptCircuitAddition = async () => {
|
|
|
1304
1327
|
* Shows a list of pre-computed zKeys for a single option selection.
|
|
1305
1328
|
* @dev the names are derived from local zKeys files.
|
|
1306
1329
|
* @param options <Array<string>> - an array of pre-computed zKeys names.
|
|
1307
|
-
* @returns Promise<string> - the name of the
|
|
1330
|
+
* @returns Promise<string> - the name of the chosen pre-computed zKey.
|
|
1308
1331
|
*/
|
|
1309
1332
|
const promptPreComputedZkeySelector = async (options) => {
|
|
1310
1333
|
const { preComputedZkeyFilename } = await prompts({
|
|
@@ -1342,13 +1365,13 @@ const promptNeededPowersForCircuit = async (suggestedSmallestNeededPowers) => {
|
|
|
1342
1365
|
* Shows a list of PoT files for a single option selection.
|
|
1343
1366
|
* @dev the names are derived from local PoT files.
|
|
1344
1367
|
* @param options <Array<string>> - an array of PoT file names.
|
|
1345
|
-
* @returns Promise<string> - the name of the
|
|
1368
|
+
* @returns Promise<string> - the name of the chosen PoT.
|
|
1346
1369
|
*/
|
|
1347
1370
|
const promptPotSelector = async (options) => {
|
|
1348
1371
|
const { potFilename } = await prompts({
|
|
1349
1372
|
type: "select",
|
|
1350
1373
|
name: "potFilename",
|
|
1351
|
-
message: theme.text.bold("Select the Powers of Tau file
|
|
1374
|
+
message: theme.text.bold("Select the Powers of Tau file chosen for the circuit"),
|
|
1352
1375
|
choices: options.map((option) => {
|
|
1353
1376
|
console.log(option);
|
|
1354
1377
|
return { title: option, value: option };
|
|
@@ -1418,7 +1441,7 @@ const promptToTypeEntropyOrBeacon = async (isEntropy = true) => {
|
|
|
1418
1441
|
* @return <Promise<string>> - the entropy.
|
|
1419
1442
|
*/
|
|
1420
1443
|
const promptForEntropy = async () => {
|
|
1421
|
-
// Prompt for entropy generation
|
|
1444
|
+
// Prompt for entropy generation preferred method.
|
|
1422
1445
|
const { confirmation } = await askForConfirmation(`Do you prefer to type your entropy or generate it randomly?`, "Manually", "Randomly");
|
|
1423
1446
|
if (confirmation === undefined)
|
|
1424
1447
|
showError(COMMAND_ERRORS.COMMAND_ABORT_PROMPT, true);
|
|
@@ -1635,7 +1658,7 @@ const handleAdditionOfCircuitsToCeremony = async (r1csOptions, wasmOptions, cere
|
|
|
1635
1658
|
wasmFilename.split(`.${commonTerms.foldersAndPathsTerms.wasm}`)[0]);
|
|
1636
1659
|
if (matchingWasms.length !== 1)
|
|
1637
1660
|
showError(COMMAND_ERRORS.COMMAND_SETUP_MISMATCH_R1CS_WASM, true);
|
|
1638
|
-
// Get input data for
|
|
1661
|
+
// Get input data for chosen circuit.
|
|
1639
1662
|
const circuitInputData = await getInputDataToAddCircuitToCeremony(choosenCircuitFilename, matchingWasms[0], ceremonyTimeoutMechanismType, sameCircomCompiler, circuitSequencePosition, sharedCircomCompilerData);
|
|
1640
1663
|
// Store circuit data.
|
|
1641
1664
|
inputDataForCircuits.push(circuitInputData);
|
|
@@ -1725,7 +1748,7 @@ const checkAndDownloadSmallestPowersOfTau = async (powers, ptauCompleteFilename)
|
|
|
1725
1748
|
* number of powers greater than or equal to the powers needed by the zKey), the coordinator will be asked
|
|
1726
1749
|
* to provide a number of powers manually, ranging from the smallest possible to the largest.
|
|
1727
1750
|
* @param neededPowers <number> - the smallest amount of powers needed by the zKey.
|
|
1728
|
-
* @returns Promise<string, string> - the information about the
|
|
1751
|
+
* @returns Promise<string, string> - the information about the chosen Powers of Tau file for the pre-computed zKey
|
|
1729
1752
|
* along with related powers.
|
|
1730
1753
|
*/
|
|
1731
1754
|
const handlePreComputedZkeyPowersOfTauSelection = async (neededPowers) => {
|
|
@@ -1826,7 +1849,9 @@ const setup = async (cmd) => {
|
|
|
1826
1849
|
let ceremonyId = ""; // The unique identifier of the ceremony.
|
|
1827
1850
|
const { firebaseApp, firebaseFunctions, firestoreDatabase } = await bootstrapCommandExecutionAndServices();
|
|
1828
1851
|
// Check for authentication.
|
|
1829
|
-
const { user, providerUserId } = cmd.auth
|
|
1852
|
+
const { user, providerUserId } = cmd.auth
|
|
1853
|
+
? await authWithToken(firebaseApp, cmd.auth)
|
|
1854
|
+
: await checkAuth(firebaseApp);
|
|
1830
1855
|
// Preserve command execution only for coordinators.
|
|
1831
1856
|
if (!(await isCoordinator(user)))
|
|
1832
1857
|
showError(COMMAND_ERRORS.COMMAND_NOT_COORDINATOR, true);
|
|
@@ -1843,7 +1868,7 @@ const setup = async (cmd) => {
|
|
|
1843
1868
|
// if there is the file option, then set up the non interactively
|
|
1844
1869
|
if (cmd.template) {
|
|
1845
1870
|
// 1. parse the file
|
|
1846
|
-
// tmp data - do not cleanup files as we need them
|
|
1871
|
+
// tmp data - do not cleanup files as we need them
|
|
1847
1872
|
const spinner = customSpinner(`Parsing ${theme.text.bold(cmd.template)} setup configuration file...`, `clock`);
|
|
1848
1873
|
spinner.start();
|
|
1849
1874
|
const setupCeremonyData = await parseCeremonyFile(cmd.template);
|
|
@@ -1853,8 +1878,6 @@ const setup = async (cmd) => {
|
|
|
1853
1878
|
// create a new bucket
|
|
1854
1879
|
const bucketName = await handleCeremonyBucketCreation(firebaseFunctions, ceremonySetupData.ceremonyPrefix);
|
|
1855
1880
|
console.log(`\n${theme.symbols.success} Ceremony bucket name: ${theme.text.bold(bucketName)}`);
|
|
1856
|
-
// create S3 clienbt
|
|
1857
|
-
const s3 = new S3Client({ region: 'us-east-1' });
|
|
1858
1881
|
// loop through each circuit
|
|
1859
1882
|
for await (const circuit of setupCeremonyData.circuits) {
|
|
1860
1883
|
// Local paths.
|
|
@@ -1864,21 +1887,12 @@ const setup = async (cmd) => {
|
|
|
1864
1887
|
const potLocalPathAndFileName = getPotLocalFilePath(circuit.files.potFilename);
|
|
1865
1888
|
const zkeyLocalPathAndFileName = getZkeyLocalFilePath(circuit.files.initialZkeyFilename);
|
|
1866
1889
|
// 2. download the pot and wasm files
|
|
1867
|
-
const streamPipeline = promisify(pipeline);
|
|
1868
1890
|
await checkAndDownloadSmallestPowersOfTau(convertToDoubleDigits(circuit.metadata?.pot), circuit.files.potFilename);
|
|
1869
|
-
// download the wasm to calculate the hash
|
|
1870
|
-
const spinner = customSpinner(`Downloading the ${theme.text.bold(`#${circuit.name}`)} WASM file from the project's bucket...`, `clock`);
|
|
1871
|
-
spinner.start();
|
|
1872
|
-
const command = new GetObjectCommand({ Bucket: ceremonySetupData.circuitArtifacts[index].artifacts.bucket, Key: ceremonySetupData.circuitArtifacts[index].artifacts.wasmStoragePath });
|
|
1873
|
-
const response = await s3.send(command);
|
|
1874
|
-
if (response.$metadata.httpStatusCode !== 200) {
|
|
1875
|
-
throw new Error("There was an error while trying to download the wasm file. Please check that the file has the correct permissions (public) set.");
|
|
1876
|
-
}
|
|
1877
|
-
if (response.Body instanceof Readable)
|
|
1878
|
-
await streamPipeline(response.Body, createWriteStream(wasmLocalPathAndFileName));
|
|
1879
|
-
spinner.stop();
|
|
1880
1891
|
// 3. generate the zKey
|
|
1892
|
+
const spinner = customSpinner(`Generating genesis zKey for circuit ${theme.text.bold(circuit.name)}...`, `clock`);
|
|
1893
|
+
spinner.start();
|
|
1881
1894
|
await zKey.newZKey(r1csLocalPathAndFileName, getPotLocalFilePath(circuit.files.potFilename), zkeyLocalPathAndFileName, undefined);
|
|
1895
|
+
spinner.succeed(`Generation of the genesis zKey for citcui ${theme.text.bold(circuit.name)} completed successfully`);
|
|
1882
1896
|
// 4. calculate the hashes
|
|
1883
1897
|
const wasmBlake2bHash = await blake512FromPath(wasmLocalPathAndFileName);
|
|
1884
1898
|
const potBlake2bHash = await blake512FromPath(getPotLocalFilePath(circuit.files.potFilename));
|
|
@@ -1900,9 +1914,9 @@ const setup = async (cmd) => {
|
|
|
1900
1914
|
// 6 update the setup data object
|
|
1901
1915
|
ceremonySetupData.circuits[index].files = {
|
|
1902
1916
|
...circuit.files,
|
|
1903
|
-
potBlake2bHash
|
|
1904
|
-
wasmBlake2bHash
|
|
1905
|
-
initialZkeyBlake2bHash
|
|
1917
|
+
potBlake2bHash,
|
|
1918
|
+
wasmBlake2bHash,
|
|
1919
|
+
initialZkeyBlake2bHash
|
|
1906
1920
|
};
|
|
1907
1921
|
ceremonySetupData.circuits[index].zKeySizeInBytes = getFileStats(zkeyLocalPathAndFileName).size;
|
|
1908
1922
|
}
|
|
@@ -2110,17 +2124,29 @@ const expirationCountdownForGithubOAuth = (expirationInSeconds) => {
|
|
|
2110
2124
|
*/
|
|
2111
2125
|
const onVerification = async (verification) => {
|
|
2112
2126
|
// Copy code to clipboard.
|
|
2113
|
-
|
|
2114
|
-
|
|
2127
|
+
let noClipboard = false;
|
|
2128
|
+
try {
|
|
2129
|
+
clipboard.writeSync(verification.user_code);
|
|
2130
|
+
clipboard.readSync();
|
|
2131
|
+
}
|
|
2132
|
+
catch (error) {
|
|
2133
|
+
noClipboard = true;
|
|
2134
|
+
}
|
|
2115
2135
|
// Display data.
|
|
2116
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`);
|
|
2117
|
-
console.log(theme.colors.magenta(figlet.textSync(
|
|
2118
|
-
|
|
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`);
|
|
2119
2140
|
const spinner = customSpinner(`Redirecting to Github...`, `clock`);
|
|
2120
2141
|
spinner.start();
|
|
2121
2142
|
await sleep(10000); // ~10s to make users able to read the CLI.
|
|
2122
|
-
|
|
2123
|
-
|
|
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
|
+
}
|
|
2124
2150
|
spinner.stop();
|
|
2125
2151
|
// Countdown for time expiration.
|
|
2126
2152
|
expirationCountdownForGithubOAuth(verification.expires_in);
|
|
@@ -2566,8 +2592,8 @@ const listenToParticipantDocumentChanges = async (firestoreDatabase, cloudFuncti
|
|
|
2566
2592
|
// Communicate resume / start of the contribution to participant.
|
|
2567
2593
|
await simpleLoader(`${changedContributionStep === "DOWNLOADING" /* ParticipantContributionStep.DOWNLOADING */ ? `Starting` : `Resuming`} your contribution...`, `clock`, 3000);
|
|
2568
2594
|
// Start / Resume the contribution for the participant.
|
|
2569
|
-
await handleStartOrResumeContribution(cloudFunctions, firestoreDatabase, ceremony, circuit, participant, entropy, providerUserId, false // not finalizing.
|
|
2570
|
-
);
|
|
2595
|
+
await handleStartOrResumeContribution(cloudFunctions, firestoreDatabase, ceremony, circuit, participant, entropy, providerUserId, false, // not finalizing.
|
|
2596
|
+
circuits.length);
|
|
2571
2597
|
}
|
|
2572
2598
|
// Scenario (3.A).
|
|
2573
2599
|
else if (isWaitingForContribution)
|
|
@@ -2616,7 +2642,9 @@ const listenToParticipantDocumentChanges = async (firestoreDatabase, cloudFuncti
|
|
|
2616
2642
|
// Get latest contribution verification result.
|
|
2617
2643
|
await getLatestVerificationResult(firestoreDatabase, ceremony.id, circuit.id, participant.id);
|
|
2618
2644
|
// Get next circuit for contribution.
|
|
2619
|
-
const nextCircuit = timeoutExpired
|
|
2645
|
+
const nextCircuit = timeoutExpired
|
|
2646
|
+
? getCircuitBySequencePosition(circuits, changedContributionProgress)
|
|
2647
|
+
: getCircuitBySequencePosition(circuits, changedContributionProgress + 1);
|
|
2620
2648
|
// Check disk space requirements for participant.
|
|
2621
2649
|
const wannaGenerateAttestation = await handleDiskSpaceRequirementForNextContribution(cloudFunctions, ceremony.id, nextCircuit.data.sequencePosition, nextCircuit.data.zKeySizeInBytes, timeoutExpired, providerUserId);
|
|
2622
2650
|
// Check if the participant would like to generate a new attestation.
|
|
@@ -2654,11 +2682,12 @@ const listenToParticipantDocumentChanges = async (firestoreDatabase, cloudFuncti
|
|
|
2654
2682
|
*/
|
|
2655
2683
|
const contribute = async (opt) => {
|
|
2656
2684
|
const { firebaseApp, firebaseFunctions, firestoreDatabase } = await bootstrapCommandExecutionAndServices();
|
|
2657
|
-
// Check for authentication.
|
|
2658
|
-
const { user, providerUserId, token } = await checkAuth(firebaseApp);
|
|
2659
2685
|
// Get options.
|
|
2660
2686
|
const ceremonyOpt = opt.ceremony;
|
|
2661
2687
|
const entropyOpt = opt.entropy;
|
|
2688
|
+
const { auth } = opt;
|
|
2689
|
+
// Check for authentication.
|
|
2690
|
+
const { user, providerUserId, token } = auth ? await authWithToken(firebaseApp, auth) : await checkAuth(firebaseApp);
|
|
2662
2691
|
// Prepare data.
|
|
2663
2692
|
let selectedCeremony;
|
|
2664
2693
|
// Retrieve the opened ceremonies.
|
|
@@ -2694,7 +2723,7 @@ const contribute = async (opt) => {
|
|
|
2694
2723
|
const userDoc = await getDocumentById(firestoreDatabase, commonTerms.collections.users.name, user.uid);
|
|
2695
2724
|
const userData = userDoc.data();
|
|
2696
2725
|
if (!userData) {
|
|
2697
|
-
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.`);
|
|
2698
2727
|
process.exit(0);
|
|
2699
2728
|
}
|
|
2700
2729
|
// Check the user's current participant readiness for contribution status (eligible, already contributed, timed out).
|
|
@@ -2845,7 +2874,7 @@ const observe = async () => {
|
|
|
2845
2874
|
// Preserve command execution only for coordinators].
|
|
2846
2875
|
if (!(await isCoordinator(user)))
|
|
2847
2876
|
showError(COMMAND_ERRORS.COMMAND_NOT_COORDINATOR, true);
|
|
2848
|
-
// Get running
|
|
2877
|
+
// Get running ceremonies info (if any).
|
|
2849
2878
|
const runningCeremoniesDocs = await getOpenedCeremonies(firestoreDatabase);
|
|
2850
2879
|
// Ask to select a ceremony.
|
|
2851
2880
|
const ceremony = await promptForCeremonySelection(runningCeremoniesDocs, false);
|
|
@@ -2914,7 +2943,7 @@ const handleVerifierSmartContract = async (cloudFunctions, bucketName, finalZkey
|
|
|
2914
2943
|
const packagePath = `${dirname(fileURLToPath(import.meta.url))}`;
|
|
2915
2944
|
const verifierPath = packagePath.includes(`src/commands`)
|
|
2916
2945
|
? `${dirname(fileURLToPath(import.meta.url))}/../../../../node_modules/snarkjs/templates/verifier_groth16.sol.ejs`
|
|
2917
|
-
: `${dirname(fileURLToPath(import.meta.url))}
|
|
2946
|
+
: `${dirname(fileURLToPath(import.meta.url))}/../node_modules/snarkjs/templates/verifier_groth16.sol.ejs`;
|
|
2918
2947
|
// Export the Solidity verifier smart contract.
|
|
2919
2948
|
const verifierCode = await exportVerifierContract(finalZkeyLocalFilePath, verifierPath);
|
|
2920
2949
|
spinner.text = `Writing verifier smart contract...`;
|
|
@@ -2941,10 +2970,11 @@ const handleVerifierSmartContract = async (cloudFunctions, bucketName, finalZkey
|
|
|
2941
2970
|
* @param participant <FirebaseDocumentInfo> - the Firestore document of the participant (coordinator).
|
|
2942
2971
|
* @param beacon <string> - the value used to compute the final contribution while finalizing the ceremony.
|
|
2943
2972
|
* @param coordinatorIdentifier <string> - the identifier of the coordinator.
|
|
2973
|
+
* @param circuitsLength <number> - the number of circuits in the ceremony.
|
|
2944
2974
|
*/
|
|
2945
|
-
const handleCircuitFinalization = async (cloudFunctions, firestoreDatabase, ceremony, circuit, participant, beacon, coordinatorIdentifier) => {
|
|
2975
|
+
const handleCircuitFinalization = async (cloudFunctions, firestoreDatabase, ceremony, circuit, participant, beacon, coordinatorIdentifier, circuitsLength) => {
|
|
2946
2976
|
// Step (1).
|
|
2947
|
-
await handleStartOrResumeContribution(cloudFunctions, firestoreDatabase, ceremony, circuit, participant, computeSHA256ToHex(beacon), coordinatorIdentifier, true);
|
|
2977
|
+
await handleStartOrResumeContribution(cloudFunctions, firestoreDatabase, ceremony, circuit, participant, computeSHA256ToHex(beacon), coordinatorIdentifier, true, circuitsLength);
|
|
2948
2978
|
await sleep(2000); // workaound for descriptors.
|
|
2949
2979
|
// Extract data.
|
|
2950
2980
|
const { prefix: circuitPrefix } = circuit.data;
|
|
@@ -2979,10 +3009,11 @@ const handleCircuitFinalization = async (cloudFunctions, firestoreDatabase, cere
|
|
|
2979
3009
|
* @dev For proper execution, the command requires the coordinator to be authenticated with a GitHub account (run auth command first) in order to
|
|
2980
3010
|
* handle sybil-resistance and connect to GitHub APIs to publish the gist containing the final public attestation.
|
|
2981
3011
|
*/
|
|
2982
|
-
const finalize = async () => {
|
|
3012
|
+
const finalize = async (opt) => {
|
|
2983
3013
|
const { firebaseApp, firebaseFunctions, firestoreDatabase } = await bootstrapCommandExecutionAndServices();
|
|
2984
3014
|
// Check for authentication.
|
|
2985
|
-
const {
|
|
3015
|
+
const { auth } = opt;
|
|
3016
|
+
const { user, providerUserId, token: coordinatorAccessToken } = auth ? await authWithToken(firebaseApp, auth) : await checkAuth(firebaseApp);
|
|
2986
3017
|
// Preserve command execution only for coordinators.
|
|
2987
3018
|
if (!(await isCoordinator(user)))
|
|
2988
3019
|
showError(COMMAND_ERRORS.COMMAND_NOT_COORDINATOR, true);
|
|
@@ -3017,7 +3048,7 @@ const finalize = async () => {
|
|
|
3017
3048
|
const circuits = await getCeremonyCircuits(firestoreDatabase, selectedCeremony.id);
|
|
3018
3049
|
// Handle finalization for each ceremony circuit.
|
|
3019
3050
|
for await (const circuit of circuits)
|
|
3020
|
-
await handleCircuitFinalization(firebaseFunctions, firestoreDatabase, selectedCeremony, circuit, participant, beacon, providerUserId);
|
|
3051
|
+
await handleCircuitFinalization(firebaseFunctions, firestoreDatabase, selectedCeremony, circuit, participant, beacon, providerUserId, circuits.length);
|
|
3021
3052
|
process.stdout.write(`\n`);
|
|
3022
3053
|
const spinner = customSpinner(`Wrapping up the finalization of the ceremony...`, "clock");
|
|
3023
3054
|
spinner.start();
|
|
@@ -3032,7 +3063,7 @@ const finalize = async () => {
|
|
|
3032
3063
|
// Generate attestation with final contributions.
|
|
3033
3064
|
const publicAttestation = await generateValidContributionsAttestation(firestoreDatabase, circuits, selectedCeremony.id, participant.id, contributions, providerUserId, ceremonyName, true);
|
|
3034
3065
|
// Write public attestation locally.
|
|
3035
|
-
writeFile(
|
|
3066
|
+
writeFile(getFinalAttestationLocalFilePath(`${prefix}_${finalContributionIndex}_${commonTerms.foldersAndPathsTerms.attestation}.log`), Buffer.from(publicAttestation));
|
|
3036
3067
|
await sleep(3000); // workaround for file descriptor unexpected close.
|
|
3037
3068
|
const gistUrl = await publishGist(coordinatorAccessToken, publicAttestation, ceremonyName, prefix);
|
|
3038
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))})`);
|
|
@@ -3168,15 +3199,13 @@ program
|
|
|
3168
3199
|
.description("compute contributions for a Phase2 Trusted Setup ceremony circuits")
|
|
3169
3200
|
.option("-c, --ceremony <string>", "the prefix of the ceremony you want to contribute for", "")
|
|
3170
3201
|
.option("-e, --entropy <string>", "the entropy (aka toxic waste) of your contribution", "")
|
|
3202
|
+
.option("-a, --auth <string>", "the Github OAuth 2.0 token", "")
|
|
3171
3203
|
.action(contribute);
|
|
3172
3204
|
program
|
|
3173
3205
|
.command("clean")
|
|
3174
3206
|
.description("clean up output generated by commands from the current working directory")
|
|
3175
3207
|
.action(clean);
|
|
3176
|
-
program
|
|
3177
|
-
.command("list")
|
|
3178
|
-
.description("List all ceremonies prefixes")
|
|
3179
|
-
.action(listCeremonies);
|
|
3208
|
+
program.command("list").description("List all ceremonies prefixes").action(listCeremonies);
|
|
3180
3209
|
program
|
|
3181
3210
|
.command("logout")
|
|
3182
3211
|
.description("sign out from Firebae Auth service and delete Github OAuth 2.0 token from local storage")
|
|
@@ -3192,8 +3221,8 @@ const ceremony = program.command("coordinate").description("commands for coordin
|
|
|
3192
3221
|
ceremony
|
|
3193
3222
|
.command("setup")
|
|
3194
3223
|
.description("setup a Groth16 Phase 2 Trusted Setup ceremony for zk-SNARK circuits")
|
|
3195
|
-
.option(
|
|
3196
|
-
.option(
|
|
3224
|
+
.option("-t, --template <path>", "The path to the ceremony setup template", "")
|
|
3225
|
+
.option("-a, --auth <string>", "The Github OAuth 2.0 token", "")
|
|
3197
3226
|
.action(setup);
|
|
3198
3227
|
ceremony
|
|
3199
3228
|
.command("observe")
|
|
@@ -3202,5 +3231,6 @@ ceremony
|
|
|
3202
3231
|
ceremony
|
|
3203
3232
|
.command("finalize")
|
|
3204
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", "")
|
|
3205
3235
|
.action(finalize);
|
|
3206
3236
|
program.parseAsync(process.argv);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { Contribution, ContributionValidity, FirebaseDocumentInfo } from "@
|
|
2
|
+
import { Contribution, ContributionValidity, FirebaseDocumentInfo } from "@devtion/actions";
|
|
3
3
|
import { DocumentSnapshot, DocumentData, Firestore } from "firebase/firestore";
|
|
4
4
|
import { Functions } from "firebase/functions";
|
|
5
5
|
/**
|