@devtion/actions 0.0.0-270e9e0 → 0.0.0-2ed8e18

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/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
- * @module @p0tion/actions
3
- * @version 1.0.4
2
+ * @module @devtion/actions
3
+ * @version 1.0.9
4
4
  * @file A set of actions and helpers for CLI commands
5
5
  * @copyright Ethereum Foundation 2022
6
6
  * @license MIT
@@ -17,8 +17,7 @@ import crypto from 'crypto';
17
17
  import blake from 'blakejs';
18
18
  import { utils } from 'ffjavascript';
19
19
  import winston from 'winston';
20
- import { S3Client, HeadObjectCommand, GetObjectCommand } from '@aws-sdk/client-s3';
21
- import { pipeline, Readable } from 'stream';
20
+ import { pipeline } from 'stream';
22
21
  import { promisify } from 'util';
23
22
  import { initializeApp } from 'firebase/app';
24
23
  import { signInWithCredential, initializeAuth, getAuth } from 'firebase/auth';
@@ -244,6 +243,12 @@ const commonTerms = {
244
243
  verificationStartedAt: "verificationStartedAt"
245
244
  }
246
245
  },
246
+ avatars: {
247
+ name: "avatars",
248
+ fields: {
249
+ avatarUrl: "avatarUrl"
250
+ }
251
+ },
247
252
  ceremonies: {
248
253
  name: "ceremonies",
249
254
  fields: {
@@ -334,7 +339,7 @@ const commonTerms = {
334
339
  finalizeCircuit: "finalizeCircuit",
335
340
  finalizeCeremony: "finalizeCeremony",
336
341
  downloadCircuitArtifacts: "downloadCircuitArtifacts",
337
- transferObject: "transferObject",
342
+ transferObject: "transferObject"
338
343
  }
339
344
  };
340
345
 
@@ -1052,7 +1057,8 @@ const parseCeremonyFile = async (path, cleanup = false) => {
1052
1057
  // read the data
1053
1058
  const data = JSON.parse(fs.readFileSync(path).toString());
1054
1059
  // verify that the data is correct
1055
- if (data['timeoutMechanismType'] !== "DYNAMIC" /* CeremonyTimeoutType.DYNAMIC */ && data['timeoutMechanismType'] !== "FIXED" /* CeremonyTimeoutType.FIXED */)
1060
+ if (data["timeoutMechanismType"] !== "DYNAMIC" /* CeremonyTimeoutType.DYNAMIC */ &&
1061
+ data["timeoutMechanismType"] !== "FIXED" /* CeremonyTimeoutType.FIXED */)
1056
1062
  throw new Error("Invalid timeout type. Please choose between DYNAMIC and FIXED.");
1057
1063
  // validate that we have at least 1 circuit input data
1058
1064
  if (!data.circuits || data.circuits.length === 0)
@@ -1085,42 +1091,26 @@ const parseCeremonyFile = async (path, cleanup = false) => {
1085
1091
  circuitArtifacts.push({
1086
1092
  artifacts: artifacts
1087
1093
  });
1088
- const r1csPath = artifacts.r1csStoragePath;
1089
- const wasmPath = artifacts.wasmStoragePath;
1090
1094
  // where we storing the r1cs downloaded
1091
1095
  const localR1csPath = `./${circuitData.name}.r1cs`;
1092
- // check that the artifacts exist in S3
1093
- // we don't need any privileges to download this
1094
- // just the correct region
1095
- const s3 = new S3Client({ region: artifacts.region });
1096
- try {
1097
- await s3.send(new HeadObjectCommand({
1098
- Bucket: artifacts.bucket,
1099
- Key: r1csPath
1100
- }));
1101
- }
1102
- catch (error) {
1103
- throw new Error(`The r1cs file (${r1csPath}) seems to not exist. Please ensure this is correct and that the object is publicly available.`);
1104
- }
1105
- try {
1106
- await s3.send(new HeadObjectCommand({
1107
- Bucket: artifacts.bucket,
1108
- Key: wasmPath
1109
- }));
1110
- }
1111
- catch (error) {
1112
- throw new Error(`The wasm file (${wasmPath}) seems to not exist. Please ensure this is correct and that the object is publicly available.`);
1113
- }
1096
+ // where we storing the wasm downloaded
1097
+ const localWasmPath = `./${circuitData.name}.wasm`;
1114
1098
  // download the r1cs to extract the metadata
1115
- const command = new GetObjectCommand({ Bucket: artifacts.bucket, Key: artifacts.r1csStoragePath });
1116
- const response = await s3.send(command);
1117
1099
  const streamPipeline = promisify(pipeline);
1118
- if (response.$metadata.httpStatusCode !== 200)
1119
- throw new Error("There was an error while trying to download the r1cs file. Please check that the file has the correct permissions (public) set.");
1120
- if (response.Body instanceof Readable)
1121
- await streamPipeline(response.Body, fs.createWriteStream(localR1csPath));
1100
+ // Make the call.
1101
+ const responseR1CS = await fetch(artifacts.r1csStoragePath);
1102
+ // Handle errors.
1103
+ if (!responseR1CS.ok && responseR1CS.status !== 200)
1104
+ throw new Error(`There was an error while trying to download the r1cs file for circuit ${circuitData.name}. Please check that the file has the correct permissions (public) set.`);
1105
+ await streamPipeline(responseR1CS.body, createWriteStream(localR1csPath));
1106
+ // Write the file locally
1122
1107
  // extract the metadata from the r1cs
1123
1108
  const metadata = getR1CSInfo(localR1csPath);
1109
+ // download wasm too to ensure it's available
1110
+ const responseWASM = await fetch(artifacts.wasmStoragePath);
1111
+ if (!responseWASM.ok && responseWASM.status !== 200)
1112
+ throw new Error(`There was an error while trying to download the WASM file for circuit ${circuitData.name}. Please check that the file has the correct permissions (public) set.`);
1113
+ await streamPipeline(responseWASM.body, createWriteStream(localWasmPath));
1124
1114
  // validate that the circuit hash and template links are valid
1125
1115
  const template = circuitData.template;
1126
1116
  const URLMatch = template.source.match(urlPattern);
@@ -1138,7 +1128,7 @@ const parseCeremonyFile = async (path, cleanup = false) => {
1138
1128
  const wasmCompleteFilename = `${circuitData.name}.wasm`;
1139
1129
  const smallestPowersOfTauCompleteFilenameForCircuit = `${potFilenameTemplate}${doubleDigitsPowers}.ptau`;
1140
1130
  const firstZkeyCompleteFilename = `${circuitPrefix}_${genesisZkeyIndex}.zkey`;
1141
- // storage paths
1131
+ // storage paths
1142
1132
  const r1csStorageFilePath = getR1csStorageFilePath(circuitPrefix, r1csCompleteFilename);
1143
1133
  const wasmStorageFilePath = getWasmStorageFilePath(circuitPrefix, wasmCompleteFilename);
1144
1134
  const potStorageFilePath = getPotStorageFilePath(smallestPowersOfTauCompleteFilenameForCircuit);
@@ -1154,7 +1144,7 @@ const parseCeremonyFile = async (path, cleanup = false) => {
1154
1144
  initialZkeyStoragePath: zkeyStorageFilePath,
1155
1145
  r1csBlake2bHash: r1csBlake2bHash
1156
1146
  };
1157
- // validate that the compiler hash is a valid hash
1147
+ // validate that the compiler hash is a valid hash
1158
1148
  const compiler = circuitData.compiler;
1159
1149
  const compilerHashMatch = compiler.commitHash.match(commitHashPattern);
1160
1150
  if (!compilerHashMatch || compilerHashMatch.length === 0 || compilerHashMatch.length > 1)
@@ -1168,39 +1158,58 @@ const parseCeremonyFile = async (path, cleanup = false) => {
1168
1158
  // check that the timeout is provided for the correct configuration
1169
1159
  let dynamicThreshold;
1170
1160
  let fixedTimeWindow;
1161
+ let circuit = {};
1171
1162
  if (data.timeoutMechanismType === "DYNAMIC" /* CeremonyTimeoutType.DYNAMIC */) {
1172
1163
  if (circuitData.dynamicThreshold <= 0)
1173
1164
  throw new Error("The dynamic threshold should be > 0.");
1174
1165
  dynamicThreshold = circuitData.dynamicThreshold;
1166
+ // the Circuit data for the ceremony setup
1167
+ circuit = {
1168
+ name: circuitData.name,
1169
+ description: circuitData.description,
1170
+ prefix: circuitPrefix,
1171
+ sequencePosition: i + 1,
1172
+ metadata: metadata,
1173
+ files: files,
1174
+ template: template,
1175
+ compiler: compiler,
1176
+ verification: verification,
1177
+ dynamicThreshold: dynamicThreshold,
1178
+ avgTimings: {
1179
+ contributionComputation: 0,
1180
+ fullContribution: 0,
1181
+ verifyCloudFunction: 0
1182
+ }
1183
+ };
1175
1184
  }
1176
1185
  if (data.timeoutMechanismType === "FIXED" /* CeremonyTimeoutType.FIXED */) {
1177
1186
  if (circuitData.fixedTimeWindow <= 0)
1178
1187
  throw new Error("The fixed time window threshold should be > 0.");
1179
1188
  fixedTimeWindow = circuitData.fixedTimeWindow;
1189
+ // the Circuit data for the ceremony setup
1190
+ circuit = {
1191
+ name: circuitData.name,
1192
+ description: circuitData.description,
1193
+ prefix: circuitPrefix,
1194
+ sequencePosition: i + 1,
1195
+ metadata: metadata,
1196
+ files: files,
1197
+ template: template,
1198
+ compiler: compiler,
1199
+ verification: verification,
1200
+ fixedTimeWindow: fixedTimeWindow,
1201
+ avgTimings: {
1202
+ contributionComputation: 0,
1203
+ fullContribution: 0,
1204
+ verifyCloudFunction: 0
1205
+ }
1206
+ };
1180
1207
  }
1181
- // the Circuit data for the ceremony setup
1182
- const circuit = {
1183
- name: circuitData.name,
1184
- description: circuitData.description,
1185
- prefix: circuitPrefix,
1186
- sequencePosition: i + 1,
1187
- metadata: metadata,
1188
- files: files,
1189
- template: template,
1190
- compiler: compiler,
1191
- verification: verification,
1192
- fixedTimeWindow: fixedTimeWindow,
1193
- // dynamicThreshold: dynamicThreshold,
1194
- avgTimings: {
1195
- contributionComputation: 0,
1196
- fullContribution: 0,
1197
- verifyCloudFunction: 0
1198
- },
1199
- };
1200
1208
  circuits.push(circuit);
1201
- // remove the local r1cs download (if used for verifying the config only vs setup)
1209
+ // remove the local r1cs and wasm downloads (if used for verifying the config only vs setup)
1202
1210
  if (cleanup)
1203
1211
  fs.unlinkSync(localR1csPath);
1212
+ fs.unlinkSync(localWasmPath);
1204
1213
  }
1205
1214
  const setupData = {
1206
1215
  ceremonyInputData: {
@@ -1354,7 +1363,9 @@ const getContributionsValidityForContributor = async (firestoreDatabase, circuit
1354
1363
  * @param isFinalizing <boolean> - true when the coordinator is finalizing the ceremony, otherwise false.
1355
1364
  * @returns <string> - the public attestation preamble.
1356
1365
  */
1357
- const getPublicAttestationPreambleForContributor = (contributorIdentifier, ceremonyName, isFinalizing) => `Hey, I'm ${contributorIdentifier} and I have ${isFinalizing ? "finalized" : "contributed to"} the ${ceremonyName} MPC Phase2 Trusted Setup ceremony.\nThe following are my contribution signatures:`;
1366
+ const getPublicAttestationPreambleForContributor = (contributorIdentifier, ceremonyName, isFinalizing) => `Hey, I'm ${contributorIdentifier} and I have ${isFinalizing ? "finalized" : "contributed to"} the ${ceremonyName}${ceremonyName.toLowerCase().includes("trusted setup") || ceremonyName.toLowerCase().includes("ceremony")
1367
+ ? "."
1368
+ : " MPC Phase2 Trusted Setup ceremony."}\nThe following are my contribution signatures:`;
1358
1369
  /**
1359
1370
  * Check and prepare public attestation for the contributor made only of its valid contributions.
1360
1371
  * @param firestoreDatabase <Firestore> - the Firestore service instance associated to the current Firebase application.
@@ -1810,7 +1821,7 @@ const getFirestoreDatabase = (app) => getFirestore(app);
1810
1821
  * @param app <FirebaseApp> - the Firebase application.
1811
1822
  * @returns <Functions> - the Cloud Functions associated to the application.
1812
1823
  */
1813
- const getFirebaseFunctions = (app) => getFunctions(app, 'europe-west1');
1824
+ const getFirebaseFunctions = (app) => getFunctions(app, "europe-west1");
1814
1825
  /**
1815
1826
  * Retrieve the configuration variables for the AWS services (S3, EC2).
1816
1827
  * @returns <AWSVariables> - the values of the AWS services configuration variables.
@@ -2061,55 +2072,27 @@ const verifyCeremony = async (functions, firestore, ceremonyPrefix, outputDirect
2061
2072
  };
2062
2073
 
2063
2074
  /**
2064
- * This function will return the number of public repos of a user
2065
- * @param user <string> The username of the user
2066
- * @returns <number> The number of public repos
2067
- */
2068
- const getNumberOfPublicReposGitHub = async (user) => {
2069
- const response = await fetch(`https://api.github.com/user/${user}/repos`, {
2070
- method: "GET",
2071
- headers: {
2072
- Authorization: `token ${process.env.GITHUB_ACCESS_TOKEN}`
2073
- }
2074
- });
2075
- if (response.status !== 200)
2076
- throw new Error("It was not possible to retrieve the number of public repositories. Please try again.");
2077
- const jsonData = await response.json();
2078
- return jsonData.length;
2079
- };
2080
- /**
2081
- * This function will return the number of followers of a user
2082
- * @param user <string> The username of the user
2083
- * @returns <number> The number of followers
2084
- */
2085
- const getNumberOfFollowersGitHub = async (user) => {
2086
- const response = await fetch(`https://api.github.com/user/${user}/followers`, {
2087
- method: "GET",
2088
- headers: {
2089
- Authorization: `token ${process.env.GITHUB_ACCESS_TOKEN}`
2090
- }
2091
- });
2092
- if (response.status !== 200)
2093
- throw new Error("It was not possible to retrieve the number of followers. Please try again.");
2094
- const jsonData = await response.json();
2095
- return jsonData.length;
2096
- };
2097
- /**
2098
- * This function will return the number of following of a user
2099
- * @param user <string> The username of the user
2100
- * @returns <number> The number of following users
2075
+ * This function queries the GitHub API to fetch users statistics
2076
+ * @param user {string} the user uid
2077
+ * @returns {any} the stats from the GitHub API
2101
2078
  */
2102
- const getNumberOfFollowingGitHub = async (user) => {
2103
- const response = await fetch(`https://api.github.com/user/${user}/following`, {
2079
+ const getGitHubStats = async (user) => {
2080
+ const response = await fetch(`https://api.github.com/user/${user}`, {
2104
2081
  method: "GET",
2105
2082
  headers: {
2106
2083
  Authorization: `token ${process.env.GITHUB_ACCESS_TOKEN}`
2107
2084
  }
2108
2085
  });
2109
2086
  if (response.status !== 200)
2110
- throw new Error("It was not possible to retrieve the number of following. Please try again.");
2087
+ throw new Error("It was not possible to retrieve the user's statistic. Please try again.");
2111
2088
  const jsonData = await response.json();
2112
- return jsonData.length;
2089
+ const data = {
2090
+ following: jsonData.following,
2091
+ followers: jsonData.followers,
2092
+ publicRepos: jsonData.public_repos,
2093
+ avatarUrl: jsonData.avatar_url
2094
+ };
2095
+ return data;
2113
2096
  };
2114
2097
  /**
2115
2098
  * This function will check if the user is reputable enough to be able to use the app
@@ -2117,19 +2100,23 @@ const getNumberOfFollowingGitHub = async (user) => {
2117
2100
  * @param minimumAmountOfFollowing <number> The minimum amount of following the user should have
2118
2101
  * @param minimumAmountOfFollowers <number> The minimum amount of followers the user should have
2119
2102
  * @param minimumAmountOfPublicRepos <number> The minimum amount of public repos the user should have
2120
- * @returns <boolean> True if the user is reputable enough, false otherwise
2103
+ * @returns <any> Return the avatar URL of the user if the user is reputable, false otherwise
2121
2104
  */
2122
2105
  const githubReputation = async (userLogin, minimumAmountOfFollowing, minimumAmountOfFollowers, minimumAmountOfPublicRepos) => {
2123
2106
  if (!process.env.GITHUB_ACCESS_TOKEN)
2124
2107
  throw new Error("The GitHub access token is missing. Please insert a valid token to be used for anti-sybil checks on user registation, and then try again.");
2125
- const following = await getNumberOfFollowingGitHub(userLogin);
2126
- const repos = await getNumberOfPublicReposGitHub(userLogin);
2127
- const followers = await getNumberOfFollowersGitHub(userLogin);
2108
+ const { following, followers, publicRepos, avatarUrl } = await getGitHubStats(userLogin);
2128
2109
  if (following < minimumAmountOfFollowing ||
2129
- repos < minimumAmountOfPublicRepos ||
2110
+ publicRepos < minimumAmountOfPublicRepos ||
2130
2111
  followers < minimumAmountOfFollowers)
2131
- return false;
2132
- return true;
2112
+ return {
2113
+ reputable: false,
2114
+ avatarUrl: ""
2115
+ };
2116
+ return {
2117
+ reputable: true,
2118
+ avatarUrl: avatarUrl
2119
+ };
2133
2120
  };
2134
2121
 
2135
2122
  /**
@@ -2337,8 +2324,13 @@ const vmDependenciesAndCacheArtifactsCommand = (zKeyPath, potPath, snsTopic, reg
2337
2324
  // eslint-disable-next-line no-template-curly-in-string
2338
2325
  "touch ${MARKER_FILE}",
2339
2326
  "sudo yum update -y",
2340
- "curl -sL https://rpm.nodesource.com/setup_16.x | sudo bash - ",
2341
- "sudo yum install -y nodejs",
2327
+ "curl -O https://nodejs.org/dist/v16.13.0/node-v16.13.0-linux-x64.tar.xz",
2328
+ "tar -xf node-v16.13.0-linux-x64.tar.xz",
2329
+ "mv node-v16.13.0-linux-x64 nodejs",
2330
+ "sudo mv nodejs /opt/",
2331
+ "echo 'export NODEJS_HOME=/opt/nodejs' >> /etc/profile",
2332
+ "echo 'export PATH=$NODEJS_HOME/bin:$PATH' >> /etc/profile",
2333
+ "source /etc/profile",
2342
2334
  "npm install -g snarkjs",
2343
2335
  `aws s3 cp s3://${zKeyPath} /var/tmp/genesisZkey.zkey`,
2344
2336
  `aws s3 cp s3://${potPath} /var/tmp/pot.ptau`,
@@ -2357,6 +2349,7 @@ const vmDependenciesAndCacheArtifactsCommand = (zKeyPath, potPath, snsTopic, reg
2357
2349
  * @returns Array<string> - the list of commands for contribution verification.
2358
2350
  */
2359
2351
  const vmContributionVerificationCommand = (bucketName, lastZkeyStoragePath, verificationTranscriptStoragePathAndFilename) => [
2352
+ `source /etc/profile`,
2360
2353
  `aws s3 cp s3://${bucketName}/${lastZkeyStoragePath} /var/tmp/lastZKey.zkey > /var/tmp/log.txt`,
2361
2354
  `snarkjs zkvi /var/tmp/genesisZkey.zkey /var/tmp/pot.ptau /var/tmp/lastZKey.zkey > /var/tmp/verification_transcript.log`,
2362
2355
  `aws s3 cp /var/tmp/verification_transcript.log s3://${bucketName}/${verificationTranscriptStoragePathAndFilename} &>/dev/null`,