@devtion/backend 0.0.0-5d170d3 → 0.0.0-671e653

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.
Files changed (37) hide show
  1. package/README.md +28 -2
  2. package/dist/src/functions/index.js +407 -168
  3. package/dist/src/functions/index.mjs +407 -170
  4. package/dist/types/functions/bandada.d.ts +4 -0
  5. package/dist/types/functions/bandada.d.ts.map +1 -0
  6. package/dist/types/functions/ceremony.d.ts.map +1 -1
  7. package/dist/types/functions/circuit.d.ts.map +1 -1
  8. package/dist/types/functions/index.d.ts +2 -0
  9. package/dist/types/functions/index.d.ts.map +1 -1
  10. package/dist/types/functions/siwe.d.ts +4 -0
  11. package/dist/types/functions/siwe.d.ts.map +1 -0
  12. package/dist/types/functions/storage.d.ts.map +1 -1
  13. package/dist/types/functions/timeout.d.ts.map +1 -1
  14. package/dist/types/functions/user.d.ts.map +1 -1
  15. package/dist/types/lib/errors.d.ts +2 -1
  16. package/dist/types/lib/errors.d.ts.map +1 -1
  17. package/dist/types/lib/services.d.ts +7 -0
  18. package/dist/types/lib/services.d.ts.map +1 -1
  19. package/dist/types/lib/utils.d.ts +1 -1
  20. package/dist/types/lib/utils.d.ts.map +1 -1
  21. package/dist/types/types/index.d.ts +57 -1
  22. package/dist/types/types/index.d.ts.map +1 -1
  23. package/package.json +5 -4
  24. package/src/functions/bandada.ts +155 -0
  25. package/src/functions/ceremony.ts +9 -4
  26. package/src/functions/circuit.ts +137 -185
  27. package/src/functions/index.ts +2 -0
  28. package/src/functions/participant.ts +9 -9
  29. package/src/functions/siwe.ts +77 -0
  30. package/src/functions/storage.ts +7 -4
  31. package/src/functions/timeout.ts +5 -4
  32. package/src/functions/user.ts +35 -10
  33. package/src/lib/errors.ts +6 -1
  34. package/src/lib/services.ts +36 -0
  35. package/src/lib/utils.ts +11 -9
  36. package/src/types/declarations.d.ts +1 -0
  37. package/src/types/index.ts +61 -1
@@ -37,12 +37,14 @@ import {
37
37
  createCustomLoggerForFile,
38
38
  retrieveCommandStatus,
39
39
  stopEC2Instance
40
- } from "@p0tion/actions"
40
+ } from "@devtion/actions"
41
41
  import { zKey } from "snarkjs"
42
42
  import { CommandInvocationStatus, SSMClient } from "@aws-sdk/client-ssm"
43
+ import { EC2Client } from "@aws-sdk/client-ec2"
44
+ import { HttpsError } from "firebase-functions/v2/https"
43
45
  import { FinalizeCircuitData, VerifyContributionData } from "../types/index"
44
46
  import { LogLevel } from "../types/enums"
45
- import { COMMON_ERRORS, logAndThrowError, makeError, printLog, SPECIFIC_ERRORS } from "../lib/errors"
47
+ import { COMMON_ERRORS, logAndThrowError, printLog, SPECIFIC_ERRORS } from "../lib/errors"
46
48
  import {
47
49
  createEC2Client,
48
50
  createSSMClient,
@@ -57,7 +59,6 @@ import {
57
59
  sleep,
58
60
  uploadFileToBucket
59
61
  } from "../lib/utils"
60
- import { EC2Client } from "@aws-sdk/client-ec2"
61
62
 
62
63
  dotenv.config()
63
64
 
@@ -131,6 +132,7 @@ const coordinate = async (
131
132
 
132
133
  newParticipantStatus = ParticipantStatus.CONTRIBUTING
133
134
  newContributionStep = ParticipantContributionStep.DOWNLOADING
135
+ newCurrentContributorId = participant.id
134
136
  }
135
137
  // Scenario (B).
136
138
  else if (participantIsNotCurrentContributor) {
@@ -213,113 +215,80 @@ const coordinate = async (
213
215
  * Wait until the command has completed its execution inside the VM.
214
216
  * @dev this method implements a custom interval to check 5 times after 1 minute if the command execution
215
217
  * has been completed or not by calling the `retrieveCommandStatus` method.
216
- * @param {any} resolve the promise.
217
- * @param {any} reject the promise.
218
218
  * @param {SSMClient} ssm the SSM client.
219
219
  * @param {string} vmInstanceId the unique identifier of the VM instance.
220
220
  * @param {string} commandId the unique identifier of the VM command.
221
221
  * @returns <Promise<void>> true when the command execution succeed; otherwise false.
222
222
  */
223
- const waitForVMCommandExecution = (
224
- resolve: any,
225
- reject: any,
226
- ssm: SSMClient,
227
- vmInstanceId: string,
228
- commandId: string
229
- ) => {
230
- const interval = setInterval(async () => {
231
- try {
232
- // Get command status.
233
- const cmdStatus = await retrieveCommandStatus(ssm, vmInstanceId, commandId)
234
- printLog(`Checking command ${commandId} status => ${cmdStatus}`, LogLevel.DEBUG)
235
-
236
- if (cmdStatus === CommandInvocationStatus.SUCCESS) {
237
- printLog(`Command ${commandId} successfully completed`, LogLevel.DEBUG)
238
-
239
- // Resolve the promise.
240
- resolve()
241
- } else if (cmdStatus === CommandInvocationStatus.FAILED) {
242
- logAndThrowError(SPECIFIC_ERRORS.SE_VM_FAILED_COMMAND_EXECUTION)
243
- reject()
244
- } else if (cmdStatus === CommandInvocationStatus.TIMED_OUT) {
245
- logAndThrowError(SPECIFIC_ERRORS.SE_VM_TIMEDOUT_COMMAND_EXECUTION)
246
- reject()
247
- } else if (cmdStatus === CommandInvocationStatus.CANCELLED) {
248
- logAndThrowError(SPECIFIC_ERRORS.SE_VM_CANCELLED_COMMAND_EXECUTION)
249
- reject()
250
- } else if (cmdStatus === CommandInvocationStatus.DELAYED) {
251
- logAndThrowError(SPECIFIC_ERRORS.SE_VM_DELAYED_COMMAND_EXECUTION)
252
- reject()
253
- }
254
- } catch (error: any) {
255
- printLog(`Invalid command ${commandId} execution`, LogLevel.DEBUG)
223
+ const waitForVMCommandExecution = (ssm: SSMClient, vmInstanceId: string, commandId: string): Promise<void> =>
224
+ new Promise((resolve, reject) => {
225
+ const poll = async () => {
226
+ try {
227
+ // Get command status.
228
+ const cmdStatus = await retrieveCommandStatus(ssm, vmInstanceId, commandId)
229
+ printLog(`Checking command ${commandId} status => ${cmdStatus}`, LogLevel.DEBUG)
230
+
231
+ let error: HttpsError | undefined
232
+ switch (cmdStatus) {
233
+ case CommandInvocationStatus.CANCELLING:
234
+ case CommandInvocationStatus.CANCELLED: {
235
+ error = SPECIFIC_ERRORS.SE_VM_CANCELLED_COMMAND_EXECUTION
236
+ break
237
+ }
238
+ case CommandInvocationStatus.DELAYED: {
239
+ error = SPECIFIC_ERRORS.SE_VM_DELAYED_COMMAND_EXECUTION
240
+ break
241
+ }
242
+ case CommandInvocationStatus.FAILED: {
243
+ error = SPECIFIC_ERRORS.SE_VM_FAILED_COMMAND_EXECUTION
244
+ break
245
+ }
246
+ case CommandInvocationStatus.TIMED_OUT: {
247
+ error = SPECIFIC_ERRORS.SE_VM_TIMEDOUT_COMMAND_EXECUTION
248
+ break
249
+ }
250
+ case CommandInvocationStatus.IN_PROGRESS:
251
+ case CommandInvocationStatus.PENDING: {
252
+ // wait a minute and poll again
253
+ setTimeout(poll, 60000)
254
+ return
255
+ }
256
+ case CommandInvocationStatus.SUCCESS: {
257
+ printLog(`Command ${commandId} successfully completed`, LogLevel.DEBUG)
258
+
259
+ // Resolve the promise.
260
+ resolve()
261
+ return
262
+ }
263
+ default: {
264
+ logAndThrowError(SPECIFIC_ERRORS.SE_VM_UNKNOWN_COMMAND_STATUS)
265
+ }
266
+ }
256
267
 
257
- if (!error.toString().includes(commandId)) logAndThrowError(COMMON_ERRORS.CM_INVALID_COMMAND_EXECUTION)
268
+ if (error) {
269
+ logAndThrowError(error)
270
+ }
271
+ } catch (error: any) {
272
+ printLog(`Invalid command ${commandId} execution`, LogLevel.DEBUG)
258
273
 
259
- // Reject the promise.
260
- reject()
261
- } finally {
262
- // Clear the interval.
263
- clearInterval(interval)
264
- }
265
- }, 60000) // 1 minute.
266
- }
274
+ const ec2 = await createEC2Client()
267
275
 
268
- /**
269
- * Wait until the artifacts have been downloaded.
270
- * @param {any} resolve the promise.
271
- * @param {any} reject the promise.
272
- * @param {string} potTempFilePath the tmp path to the locally downloaded pot file.
273
- * @param {string} firstZkeyTempFilePath the tmp path to the locally downloaded first zkey file.
274
- * @param {string} lastZkeyTempFilePath the tmp path to the locally downloaded last zkey file.
275
- */
276
- const waitForFileDownload = (
277
- resolve: any,
278
- reject: any,
279
- potTempFilePath: string,
280
- firstZkeyTempFilePath: string,
281
- lastZkeyTempFilePath: string,
282
- circuitId: string,
283
- participantId: string
284
- ) => {
285
- const maxWaitTime = 5 * 60 * 1000 // 5 minutes
286
- // every second check if the file download was completed
287
- const interval = setInterval(async () => {
288
- printLog(`Verifying that the artifacts were downloaded for circuit ${circuitId} and participant ${participantId}`, LogLevel.DEBUG)
289
- try {
290
- // check if files have been downloaded
291
- if (!fs.existsSync(potTempFilePath)) {
292
- printLog(`Pot file not found at ${potTempFilePath}`, LogLevel.DEBUG)
293
- }
294
- if (!fs.existsSync(firstZkeyTempFilePath)) {
295
- printLog(`First zkey file not found at ${firstZkeyTempFilePath}`, LogLevel.DEBUG)
296
- }
297
- if (!fs.existsSync(lastZkeyTempFilePath)) {
298
- printLog(`Last zkey file not found at ${lastZkeyTempFilePath}`, LogLevel.DEBUG)
299
- }
276
+ // if it errors out, let's just log it as a warning so the coordinator is aware
277
+ try {
278
+ await stopEC2Instance(ec2, vmInstanceId)
279
+ } catch (error: any) {
280
+ printLog(`Error while stopping VM instance ${vmInstanceId} - Error ${error}`, LogLevel.WARN)
281
+ }
282
+
283
+ if (!error.toString().includes(commandId)) logAndThrowError(COMMON_ERRORS.CM_INVALID_COMMAND_EXECUTION)
300
284
 
301
- // if all files were downloaded
302
- if (fs.existsSync(potTempFilePath) && fs.existsSync(firstZkeyTempFilePath) && fs.existsSync(lastZkeyTempFilePath)) {
303
- printLog(`All required files are present on disk.`, LogLevel.INFO)
304
- // resolve the promise
305
- resolve()
285
+ // Reject the promise.
286
+ reject()
306
287
  }
307
- } catch (error: any) {
308
- // if we have an error then we print it as a warning and reject
309
- printLog(`Error while downloading files: ${error}`, LogLevel.WARN)
310
- reject()
311
- } finally {
312
- printLog(`Clearing the interval for file download. Circuit ${circuitId} and participant ${participantId}`, LogLevel.DEBUG)
313
- clearInterval(interval)
314
288
  }
315
- }, 5000)
316
289
 
317
- // we want to clean in 5 minutes in case
318
- setTimeout(() => {
319
- clearInterval(interval)
320
- reject(new Error('Timeout exceeded while waiting for files to be downloaded.'))
321
- }, maxWaitTime)
322
- }
290
+ setTimeout(poll, 60000)
291
+ })
323
292
 
324
293
  /**
325
294
  * This method is used to coordinate the waiting queues of ceremony circuits.
@@ -341,7 +310,7 @@ const waitForFileDownload = (
341
310
  * - Just completed a contribution or all contributions for each circuit. If yes, coordinate (multi-participant scenario).
342
311
  */
343
312
  export const coordinateCeremonyParticipant = functionsV1
344
- .region('europe-west1')
313
+ .region("europe-west1")
345
314
  .runWith({
346
315
  memory: "512MB"
347
316
  })
@@ -442,7 +411,6 @@ export const coordinateCeremonyParticipant = functionsV1
442
411
  }
443
412
  })
444
413
 
445
-
446
414
  /**
447
415
  * Recursive function to check whether an EC2 is in a running state
448
416
  * @notice required step to run commands
@@ -451,23 +419,18 @@ export const coordinateCeremonyParticipant = functionsV1
451
419
  * @param attempts <number> - how many times to retry before failing
452
420
  * @returns <Promise<boolean>> - whether the VM was started
453
421
  */
454
- const checkIfVMRunning = async (
455
- ec2: EC2Client,
456
- vmInstanceId: string,
457
- attempts = 5
458
- ): Promise<boolean> => {
422
+ const checkIfVMRunning = async (ec2: EC2Client, vmInstanceId: string, attempts = 5): Promise<boolean> => {
459
423
  // if we tried 5 times, then throw an error
460
424
  if (attempts <= 0) logAndThrowError(SPECIFIC_ERRORS.SE_VM_NOT_RUNNING)
461
425
 
462
- await sleep(60000); // Wait for 1 min
463
- const isVMRunning = await checkIfRunning(ec2, vmInstanceId)
426
+ await sleep(60000) // Wait for 1 min
427
+ const isVMRunning = await checkIfRunning(ec2, vmInstanceId)
464
428
 
465
429
  if (!isVMRunning) {
466
430
  printLog(`VM not running, ${attempts - 1} attempts remaining. Retrying in 1 minute...`, LogLevel.DEBUG)
467
- return await checkIfVMRunning(ec2, vmInstanceId, attempts - 1)
468
- } else {
469
- return true
431
+ return checkIfVMRunning(ec2, vmInstanceId, attempts - 1)
470
432
  }
433
+ return true
471
434
  }
472
435
 
473
436
  /**
@@ -497,7 +460,7 @@ const checkIfVMRunning = async (
497
460
  * 2) Send all updates atomically to the Firestore database.
498
461
  */
499
462
  export const verifycontribution = functionsV2.https.onCall(
500
- { memory: "16GiB", timeoutSeconds: 3600, region: 'europe-west1' },
463
+ { memory: "16GiB", timeoutSeconds: 3600, region: "europe-west1" },
501
464
  async (request: functionsV2.https.CallableRequest<VerifyContributionData>): Promise<any> => {
502
465
  if (!request.auth || (!request.auth.token.participant && !request.auth.token.coordinator))
503
466
  logAndThrowError(SPECIFIC_ERRORS.SE_AUTH_NO_CURRENT_AUTH_USER)
@@ -665,9 +628,6 @@ export const verifycontribution = functionsV2.https.onCall(
665
628
  verificationTranscriptTemporaryLocalPath,
666
629
  true
667
630
  )
668
-
669
- // Stop VM instance.
670
- await stopEC2Instance(ec2, vmInstanceId)
671
631
  } else {
672
632
  // Upload verification transcript.
673
633
  /// nb. do not use multi-part upload here due to small file size.
@@ -744,6 +704,17 @@ export const verifycontribution = functionsV2.https.onCall(
744
704
  })
745
705
  }
746
706
 
707
+ // Stop VM instance
708
+ if (isUsingVM) {
709
+ // using try and catch as the VM stopping function can throw
710
+ // however we want to continue without stopping as the
711
+ // verification was valid, and inform the coordinator
712
+ try {
713
+ await stopEC2Instance(ec2, vmInstanceId)
714
+ } catch (error: any) {
715
+ printLog(`Error while stopping VM instance ${vmInstanceId} - Error ${error}`, LogLevel.WARN)
716
+ }
717
+ }
747
718
  // Step (1.A.4.C)
748
719
  if (!isFinalizing) {
749
720
  // Step (1.A.4.C.1)
@@ -764,6 +735,8 @@ export const verifycontribution = functionsV2.https.onCall(
764
735
  : verifyCloudFunctionTime
765
736
 
766
737
  // Prepare tx to update circuit average contribution/verification time.
738
+ const updatedCircuitDoc = await getDocumentById(getCircuitsCollectionPath(ceremonyId), circuitId)
739
+ const { waitingQueue: updatedWaitingQueue } = updatedCircuitDoc.data()!
767
740
  /// @dev this must happen only for valid contributions.
768
741
  batch.update(circuitDoc.ref, {
769
742
  avgTimings: {
@@ -776,7 +749,7 @@ export const verifycontribution = functionsV2.https.onCall(
776
749
  : avgVerifyCloudFunctionTime
777
750
  },
778
751
  waitingQueue: {
779
- ...waitingQueue,
752
+ ...updatedWaitingQueue,
780
753
  completedContributions: isContributionValid
781
754
  ? completedContributions + 1
782
755
  : completedContributions,
@@ -835,9 +808,7 @@ export const verifycontribution = functionsV2.https.onCall(
835
808
  printLog(`Starting the execution of command ${commandId}`, LogLevel.DEBUG)
836
809
 
837
810
  // Step (1.A.3.3).
838
- return new Promise<void>((resolve, reject) =>
839
- waitForVMCommandExecution(resolve, reject, ssm, vmInstanceId, commandId)
840
- )
811
+ return waitForVMCommandExecution(ssm, vmInstanceId, commandId)
841
812
  .then(async () => {
842
813
  // Command execution successfully completed.
843
814
  printLog(`Command ${commandId} execution has been successfully completed`, LogLevel.DEBUG)
@@ -849,76 +820,57 @@ export const verifycontribution = functionsV2.https.onCall(
849
820
 
850
821
  logAndThrowError(COMMON_ERRORS.CM_INVALID_COMMAND_EXECUTION)
851
822
  })
852
- } else {
853
- // CF approach.
854
- printLog(`CF mechanism`, LogLevel.DEBUG)
855
-
856
- const potStoragePath = getPotStorageFilePath(files.potFilename)
857
- const firstZkeyStoragePath = getZkeyStorageFilePath(prefix, `${prefix}_${genesisZkeyIndex}.zkey`)
858
- // Prepare temporary file paths.
859
- // (nb. these are needed to download the necessary artifacts for verification from AWS S3).
860
- verificationTranscriptTemporaryLocalPath = createTemporaryLocalPath(
861
- verificationTranscriptCompleteFilename
862
- )
863
- const potTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}.pot`)
864
- const firstZkeyTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}_genesis.zkey`)
865
- const lastZkeyTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}_last.zkey`)
866
-
867
- // Create and populate transcript.
868
- const transcriptLogger = createCustomLoggerForFile(verificationTranscriptTemporaryLocalPath)
869
- transcriptLogger.info(
870
- `${
871
- isFinalizing ? `Final verification` : `Verification`
872
- } transcript for ${prefix} circuit Phase 2 contribution.\n${
873
- isFinalizing ? `Coordinator ` : `Contributor # ${Number(lastZkeyIndex)}`
874
- } (${contributorOrCoordinatorIdentifier})\n`
875
- )
876
-
877
- // Step (1.A.2).
878
- await downloadArtifactFromS3Bucket(bucketName, potStoragePath, potTempFilePath)
879
- await downloadArtifactFromS3Bucket(bucketName, firstZkeyStoragePath, firstZkeyTempFilePath)
880
- await downloadArtifactFromS3Bucket(bucketName, lastZkeyStoragePath, lastZkeyTempFilePath)
823
+ }
881
824
 
882
- await sleep(6000)
825
+ // CF approach.
826
+ printLog(`CF mechanism`, LogLevel.DEBUG)
827
+
828
+ const potStoragePath = getPotStorageFilePath(files.potFilename)
829
+ const firstZkeyStoragePath = getZkeyStorageFilePath(prefix, `${prefix}_${genesisZkeyIndex}.zkey`)
830
+ // Prepare temporary file paths.
831
+ // (nb. these are needed to download the necessary artifacts for verification from AWS S3).
832
+ verificationTranscriptTemporaryLocalPath = createTemporaryLocalPath(verificationTranscriptCompleteFilename)
833
+ const potTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}.pot`)
834
+ const firstZkeyTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}_genesis.zkey`)
835
+ const lastZkeyTempFilePath = createTemporaryLocalPath(`${circuitId}_${participantDoc.id}_last.zkey`)
836
+
837
+ // Create and populate transcript.
838
+ const transcriptLogger = createCustomLoggerForFile(verificationTranscriptTemporaryLocalPath)
839
+ transcriptLogger.info(
840
+ `${
841
+ isFinalizing ? `Final verification` : `Verification`
842
+ } transcript for ${prefix} circuit Phase 2 contribution.\n${
843
+ isFinalizing ? `Coordinator ` : `Contributor # ${Number(lastZkeyIndex)}`
844
+ } (${contributorOrCoordinatorIdentifier})\n`
845
+ )
883
846
 
884
- // wait until the files are actually downloaded
885
- return new Promise<void>((resolve, reject) =>
886
- waitForFileDownload(resolve, reject, potTempFilePath, firstZkeyTempFilePath, lastZkeyTempFilePath, circuitId, participantDoc.id)
887
- )
888
- .then(async () => {
889
- printLog(`Downloads from AWS S3 bucket completed - ceremony ${ceremonyId} circuit ${circuitId}`, LogLevel.DEBUG)
890
-
891
- // Step (1.A.4).
892
- isContributionValid = await zKey.verifyFromInit(
893
- firstZkeyTempFilePath,
894
- potTempFilePath,
895
- lastZkeyTempFilePath,
896
- transcriptLogger
897
- )
898
-
899
- // Compute contribution hash.
900
- lastZkeyBlake2bHash = await blake512FromPath(lastZkeyTempFilePath)
901
-
902
- // Free resources by unlinking temporary folders.
903
- // Do not free-up verification transcript path here.
904
- try {
905
- fs.unlinkSync(potTempFilePath)
906
- fs.unlinkSync(firstZkeyTempFilePath)
907
- fs.unlinkSync(lastZkeyTempFilePath)
908
- } catch (error: any) {
909
- printLog(`Error while unlinking temporary files - Error ${error}`, LogLevel.WARN)
910
- }
847
+ // Step (1.A.2).
848
+ await downloadArtifactFromS3Bucket(bucketName, potStoragePath, potTempFilePath)
849
+ await downloadArtifactFromS3Bucket(bucketName, firstZkeyStoragePath, firstZkeyTempFilePath)
850
+ await downloadArtifactFromS3Bucket(bucketName, lastZkeyStoragePath, lastZkeyTempFilePath)
911
851
 
912
- await completeVerification()
913
- })
914
- .catch((error: any) => {
915
- // Throw the new error
916
- const commonError = COMMON_ERRORS.CM_INVALID_REQUEST
917
- const additionalDetails = error.toString()
852
+ // Step (1.A.4).
853
+ isContributionValid = await zKey.verifyFromInit(
854
+ firstZkeyTempFilePath,
855
+ potTempFilePath,
856
+ lastZkeyTempFilePath,
857
+ transcriptLogger
858
+ )
918
859
 
919
- logAndThrowError(makeError(commonError.code, commonError.message, additionalDetails))
920
- })
860
+ // Compute contribution hash.
861
+ lastZkeyBlake2bHash = await blake512FromPath(lastZkeyTempFilePath)
862
+
863
+ // Free resources by unlinking temporary folders.
864
+ // Do not free-up verification transcript path here.
865
+ try {
866
+ fs.unlinkSync(potTempFilePath)
867
+ fs.unlinkSync(firstZkeyTempFilePath)
868
+ fs.unlinkSync(lastZkeyTempFilePath)
869
+ } catch (error: any) {
870
+ printLog(`Error while unlinking temporary files - Error ${error}`, LogLevel.WARN)
921
871
  }
872
+
873
+ await completeVerification()
922
874
  }
923
875
  }
924
876
  )
@@ -929,7 +881,7 @@ export const verifycontribution = functionsV2.https.onCall(
929
881
  * this does not happen if the participant is actually the coordinator who is finalizing the ceremony.
930
882
  */
931
883
  export const refreshParticipantAfterContributionVerification = functionsV1
932
- .region('europe-west1')
884
+ .region("europe-west1")
933
885
  .runWith({
934
886
  memory: "512MB"
935
887
  })
@@ -1012,7 +964,7 @@ export const refreshParticipantAfterContributionVerification = functionsV1
1012
964
  * and verification key extracted from the circuit final contribution (as part of the ceremony finalization process).
1013
965
  */
1014
966
  export const finalizeCircuit = functionsV1
1015
- .region('europe-west1')
967
+ .region("europe-west1")
1016
968
  .runWith({
1017
969
  memory: "512MB"
1018
970
  })
@@ -31,6 +31,8 @@ export {
31
31
  generatePreSignedUrlsParts,
32
32
  completeMultiPartUpload
33
33
  } from "./storage"
34
+ export { bandadaValidateProof } from "./bandada"
35
+ export { checkNonceOfSIWEAddress } from "./siwe"
34
36
  export { checkAndRemoveBlockingContributor, resumeContributionAfterTimeoutExpiration } from "./timeout"
35
37
 
36
38
  admin.initializeApp()
@@ -8,7 +8,7 @@ import {
8
8
  ParticipantContributionStep,
9
9
  getParticipantsCollectionPath,
10
10
  commonTerms
11
- } from "@p0tion/actions"
11
+ } from "@devtion/actions"
12
12
  import { FieldValue } from "firebase-admin/firestore"
13
13
  import {
14
14
  PermanentlyStoreCurrentContributionTimeAndHash,
@@ -44,7 +44,7 @@ dotenv.config()
44
44
  * @dev true when the participant can participate (1.A, 3.B, 1.D); otherwise false.
45
45
  */
46
46
  export const checkParticipantForCeremony = functions
47
- .region('europe-west1')
47
+ .region("europe-west1")
48
48
  .runWith({
49
49
  memory: "512MB"
50
50
  })
@@ -134,7 +134,7 @@ export const checkParticipantForCeremony = functions
134
134
  participantDoc.ref.update({
135
135
  status: ParticipantStatus.EXHUMED,
136
136
  contributions,
137
- tempContributionData: tempContributionData ? tempContributionData : FieldValue.delete(),
137
+ tempContributionData: tempContributionData || FieldValue.delete(),
138
138
  contributionStep: ParticipantContributionStep.DOWNLOADING,
139
139
  contributionStartedAt: 0,
140
140
  verificationStartedAt: FieldValue.delete(),
@@ -175,7 +175,7 @@ export const checkParticipantForCeremony = functions
175
175
  * 2) the participant has just finished the contribution for a circuit (contributionProgress != 0 && status = CONTRIBUTED && contributionStep = COMPLETED).
176
176
  */
177
177
  export const progressToNextCircuitForContribution = functions
178
- .region('europe-west1')
178
+ .region("europe-west1")
179
179
  .runWith({
180
180
  memory: "512MB"
181
181
  })
@@ -233,7 +233,7 @@ export const progressToNextCircuitForContribution = functions
233
233
  * 5) Completed contribution computation and verification.
234
234
  */
235
235
  export const progressToNextContributionStep = functions
236
- .region('europe-west1')
236
+ .region("europe-west1")
237
237
  .runWith({
238
238
  memory: "512MB"
239
239
  })
@@ -296,7 +296,7 @@ export const progressToNextContributionStep = functions
296
296
  * @dev enable the current contributor to resume a contribution from where it had left off.
297
297
  */
298
298
  export const permanentlyStoreCurrentContributionTimeAndHash = functions
299
- .region('europe-west1')
299
+ .region("europe-west1")
300
300
  .runWith({
301
301
  memory: "512MB"
302
302
  })
@@ -355,7 +355,7 @@ export const permanentlyStoreCurrentContributionTimeAndHash = functions
355
355
  * @dev enable the current contributor to resume a multi-part upload from where it had left off.
356
356
  */
357
357
  export const temporaryStoreCurrentContributionMultiPartUploadId = functions
358
- .region('europe-west1')
358
+ .region("europe-west1")
359
359
  .runWith({
360
360
  memory: "512MB"
361
361
  })
@@ -409,7 +409,7 @@ export const temporaryStoreCurrentContributionMultiPartUploadId = functions
409
409
  * @dev enable the current contributor to resume a multi-part upload from where it had left off.
410
410
  */
411
411
  export const temporaryStoreCurrentContributionUploadedChunkData = functions
412
- .region('europe-west1')
412
+ .region("europe-west1")
413
413
  .runWith({
414
414
  memory: "512MB"
415
415
  })
@@ -469,7 +469,7 @@ export const temporaryStoreCurrentContributionUploadedChunkData = functions
469
469
  * contributed to every selected ceremony circuits (= DONE).
470
470
  */
471
471
  export const checkAndPrepareCoordinatorForFinalization = functions
472
- .region('europe-west1')
472
+ .region("europe-west1")
473
473
  .runWith({
474
474
  memory: "512MB"
475
475
  })
@@ -0,0 +1,77 @@
1
+ import dotenv from "dotenv"
2
+ import fetch from "@adobe/node-fetch-retry"
3
+ import * as functions from "firebase-functions"
4
+ import { getAuth } from "firebase-admin/auth"
5
+ import admin from "firebase-admin"
6
+ import { Auth0UserInfo, CheckNonceOfSIWEAddressRequest, CheckNonceOfSIWEAddressResponse } from "../types"
7
+ import { setEthProvider } from "../lib/services"
8
+
9
+ dotenv.config()
10
+
11
+ export const checkNonceOfSIWEAddress = functions
12
+ .region("europe-west1")
13
+ .runWith({ memory: "1GB" })
14
+ .https.onCall(async (data: CheckNonceOfSIWEAddressRequest): Promise<CheckNonceOfSIWEAddressResponse> => {
15
+ try {
16
+ const { auth0Token } = data
17
+ const result = (await fetch(`${process.env.AUTH0_APPLICATION_URL}/userinfo`, {
18
+ method: "GET",
19
+ headers: {
20
+ "content-type": "application/json",
21
+ authorization: `Bearer ${auth0Token}`
22
+ }
23
+ }).then((_res) => _res.json())) as Auth0UserInfo
24
+ if (!result.sub) {
25
+ return {
26
+ valid: false,
27
+ message: "No user detected. Please check device flow token"
28
+ }
29
+ }
30
+ const auth = getAuth()
31
+ // check nonce
32
+ const parts = result.sub.split("|")
33
+ const address = decodeURIComponent(parts[2]).split("eip155:534352:")[1]
34
+
35
+ const minimumNonce = Number(process.env.ETH_MINIMUM_NONCE)
36
+ const nonceBlockHeight = "latest" // process.env.ETH_NONCE_BLOCK_HEIGHT
37
+ // look up nonce for address @block
38
+ let nonceOk = true
39
+ if (minimumNonce > 0) {
40
+ const provider = setEthProvider()
41
+ console.log(`got provider - block # ${await provider.getBlockNumber()}`)
42
+ const nonce = await provider.getTransactionCount(address, nonceBlockHeight)
43
+ console.log(`nonce ${nonce}`)
44
+ nonceOk = nonce >= minimumNonce
45
+ }
46
+ console.log(`checking nonce ${nonceOk}`)
47
+ if (!nonceOk) {
48
+ return {
49
+ valid: false,
50
+ message: "Eth address does not meet the nonce requirements"
51
+ }
52
+ }
53
+ try {
54
+ await admin.auth().createUser({
55
+ displayName: address,
56
+ uid: address
57
+ })
58
+ } catch (error: any) {
59
+ // if user already exist then just pass
60
+ if (error.code !== "auth/uid-already-exists") {
61
+ throw new Error(error)
62
+ }
63
+ }
64
+ const token = await auth.createCustomToken(address)
65
+ return {
66
+ valid: true,
67
+ token
68
+ }
69
+ } catch (error) {
70
+ return {
71
+ valid: false,
72
+ message: `Something went wrong ${error}`
73
+ }
74
+ }
75
+ })
76
+
77
+ export default checkNonceOfSIWEAddress
@@ -20,7 +20,7 @@ import {
20
20
  ParticipantContributionStep,
21
21
  formatZkeyIndex,
22
22
  getZkeyStorageFilePath
23
- } from "@p0tion/actions"
23
+ } from "@devtion/actions"
24
24
  import { getCeremonyCircuits, getDocumentById } from "../lib/utils"
25
25
  import { COMMON_ERRORS, logAndThrowError, makeError, printLog, SPECIFIC_ERRORS } from "../lib/errors"
26
26
  import { LogLevel } from "../types/enums"
@@ -193,8 +193,10 @@ export const createBucket = functions
193
193
  CORSConfiguration: {
194
194
  CORSRules: [
195
195
  {
196
- AllowedMethods: ["GET"],
197
- AllowedOrigins: ["*"]
196
+ AllowedMethods: ["GET", "PUT"],
197
+ AllowedOrigins: ["*"],
198
+ ExposeHeaders: ["ETag", "Content-Length"],
199
+ AllowedHeaders: ["*"]
198
200
  }
199
201
  ]
200
202
  }
@@ -407,7 +409,8 @@ export const startMultiPartUpload = functions
407
409
  export const generatePreSignedUrlsParts = functions
408
410
  .region("europe-west1")
409
411
  .runWith({
410
- memory: "512MB"
412
+ memory: "512MB",
413
+ timeoutSeconds: 300
411
414
  })
412
415
  .https.onCall(
413
416
  async (