1
1
import chai , { expect } from "chai"
2
2
import chaiAsPromised from "chai-as-promised"
3
3
import { EC2Client } from "@aws-sdk/client-ec2"
4
- import { createMockUser , generateUserPasswords , getStorageConfiguration , initializeAdminServices , initializeUserServices , sleep } from "../utils"
4
+ import fetch from "@adobe/node-fetch-retry"
5
+ import { createMockUser , envType , generateUserPasswords , getStorageConfiguration , getTranscriptLocalFilePath , initializeAdminServices , initializeUserServices , sleep } from "../utils"
5
6
import {
6
7
checkEC2Status ,
7
8
createEC2Client ,
@@ -17,9 +18,13 @@ import {
17
18
} from "../../src/helpers/ec2"
18
19
import { P0tionEC2Instance } from "../../src/types"
19
20
import { fakeCeremoniesData , fakeCircuitsData , fakeUsersData } from "../data/samples"
20
- import { getAuth , signInWithEmailAndPassword } from "firebase/auth"
21
+ import { getAuth , signInWithEmailAndPassword , signOut } from "firebase/auth"
21
22
import { SSMClient } from "@aws-sdk/client-ssm"
22
- import { commonTerms , createS3Bucket , getBucketName , getCeremonyCircuits , getDocumentById , setupCeremony } from "../../src"
23
+ import { TestingEnvironment , checkParticipantForCeremony , commonTerms , createCustomLoggerForFile , createS3Bucket , formatZkeyIndex , generateGetObjectPreSignedUrl , genesisZkeyIndex , getBucketName , getCeremonyCircuits , getCircuitBySequencePosition , getCircuitsCollectionPath , getDocumentById , getParticipantsCollectionPath , getPotStorageFilePath , getZkeyStorageFilePath , multiPartUpload , permanentlyStoreCurrentContributionTimeAndHash , progressToNextCircuitForContribution , progressToNextContributionStep , setupCeremony , verifyContribution } from "../../src"
24
+ import { cwd } from "process"
25
+ import fs from "fs"
26
+ import { zKey } from "snarkjs"
27
+ import { randomBytes } from "crypto"
23
28
chai . use ( chaiAsPromised )
24
29
25
30
// @note AWS EC2 on demand VM tests
@@ -34,82 +39,82 @@ describe("VMs", () => {
34
39
ec2 = await createEC2Client ( )
35
40
} )
36
41
37
- // describe("EC2", () => {
38
- // it("should create an instance", async () => {
39
- // instance = await createEC2Instance(ec2, [], "t2.micro", amiId, keyName, roleArn)
40
- // expect(instance).to.not.be.undefined
41
- // // give it time to actually spin up
42
- // await sleep(250000)
43
- // })
42
+ describe ( "EC2" , ( ) => {
43
+ it ( "should create an instance" , async ( ) => {
44
+ instance = await createEC2Instance ( ec2 , [ ] , "t2.micro" , amiId , keyName , roleArn )
45
+ expect ( instance ) . to . not . be . undefined
46
+ // give it time to actually spin up
47
+ await sleep ( 250000 )
48
+ } )
44
49
45
- // it("checkEC2Status should return true for an instance that is running", async () => {
46
- // const response = await checkEC2Status(ec2, instance.InstanceId!)
47
- // expect(response).to.be.true
48
- // })
50
+ it ( "checkEC2Status should return true for an instance that is running" , async ( ) => {
51
+ const response = await checkEC2Status ( ec2 , instance . InstanceId ! )
52
+ expect ( response ) . to . be . true
53
+ } )
49
54
50
- // it("getEC2Ip should return an ip", async () => {
51
- // const ip = await getEC2Ip(ec2, instance.InstanceId!)
52
- // expect(ip).to.not.be.undefined
53
- // previousIp = ip!
54
- // })
55
+ it ( "getEC2Ip should return an ip" , async ( ) => {
56
+ const ip = await getEC2Ip ( ec2 , instance . InstanceId ! )
57
+ expect ( ip ) . to . not . be . undefined
58
+ previousIp = ip !
59
+ } )
55
60
56
- // it("stopEC2Instance should stop an instance", async () => {
57
- // await expect(stopEC2Instance(ec2, instance.InstanceId!)).to.be.fulfilled
58
- // await sleep(200000)
59
- // })
61
+ it ( "stopEC2Instance should stop an instance" , async ( ) => {
62
+ await expect ( stopEC2Instance ( ec2 , instance . InstanceId ! ) ) . to . be . fulfilled
63
+ await sleep ( 200000 )
64
+ } )
60
65
61
- // it("checkEC2Status should throw for an instance that is stopped", async () => {
62
- // await expect(checkEC2Status(ec2, instance.InstanceId!)).to.be.rejected
63
- // })
66
+ it ( "checkEC2Status should throw for an instance that is stopped" , async ( ) => {
67
+ await expect ( checkEC2Status ( ec2 , instance . InstanceId ! ) ) . to . be . rejected
68
+ } )
64
69
65
- // it("startEC2Instance should start an instance", async () => {
66
- // await expect(startEC2Instance(ec2, instance.InstanceId!)).to.be.fulfilled
67
- // await sleep(200000)
68
- // })
70
+ it ( "startEC2Instance should start an instance" , async ( ) => {
71
+ await expect ( startEC2Instance ( ec2 , instance . InstanceId ! ) ) . to . be . fulfilled
72
+ await sleep ( 200000 )
73
+ } )
69
74
70
- // it("should get a different ip address after a restart", async () => {
71
- // const ip = getEC2Ip(ec2, instance.InstanceId!)
72
- // expect(previousIp).to.not.equal(ip)
73
- // })
75
+ it ( "should get a different ip address after a restart" , async ( ) => {
76
+ const ip = getEC2Ip ( ec2 , instance . InstanceId ! )
77
+ expect ( previousIp ) . to . not . equal ( ip )
78
+ } )
74
79
75
- // it("terminateEC2Instance should terminate an instance", async () => {
76
- // await expect(terminateEC2Instance(ec2, instance.InstanceId!)).to.be.fulfilled
77
- // })
78
- // })
79
-
80
- // describe("SSM", () => {
81
- // let ssmClient: SSMClient
82
- // let commandId: string
83
- // let ssmTestInstance: P0tionEC2Instance
84
- // beforeAll(async () => {
85
- // ssmClient = await createSSMClient()
86
- // ssmTestInstance = await createEC2Instance(ec2, [], "t2.micro", amiId, keyName, roleArn)
87
- // await sleep(250000)
88
- // })
89
- // it("run a command on a VM that is active", async () => {
90
- // commandId = await runCommandOnEC2(ssmClient, instance.InstanceId!, ["ls -la"])
91
- // expect(commandId).to.not.be.null
92
- // await sleep(500)
93
- // })
94
- // it("should throw when trying to call a command on a VM that is not active", async () => {
95
- // await expect(runCommandOnEC2(ssmClient, "nonExistentOrOff", ["echo hello world"])).to.be.rejected
96
- // })
97
- // it("shuold retrieve the output of a command", async () => {
98
- // await sleep(20000)
99
- // const output = await retrieveCommandOutput(ssmClient, commandId, instance.InstanceId!)
100
- // expect(output.length).to.be.gt(0)
101
- // })
102
- // it("should throw when trying to retrieve the output of a non existent command", async () => {
103
- // await expect(retrieveCommandOutput(ssmClient, "nonExistentCommand", instance.InstanceId!)).to.be.rejected
104
- // })
105
- // afterAll(async () => {
106
- // await terminateEC2Instance(ec2, ssmTestInstance.InstanceId!)
107
- // })
108
- // })
109
-
110
- // afterAll(async () => {
111
- // await terminateEC2Instance(ec2, instance.InstanceId!)
112
- // })
80
+ it ( "terminateEC2Instance should terminate an instance" , async ( ) => {
81
+ await expect ( terminateEC2Instance ( ec2 , instance . InstanceId ! ) ) . to . be . fulfilled
82
+ } )
83
+ } )
84
+
85
+ describe ( "SSM" , ( ) => {
86
+ let ssmClient : SSMClient
87
+ let commandId : string
88
+ let ssmTestInstance : P0tionEC2Instance
89
+ beforeAll ( async ( ) => {
90
+ ssmClient = await createSSMClient ( )
91
+ ssmTestInstance = await createEC2Instance ( ec2 , [ ] , "t2.micro" , amiId , keyName , roleArn )
92
+ await sleep ( 250000 )
93
+ } )
94
+ it ( "run a command on a VM that is active" , async ( ) => {
95
+ commandId = await runCommandOnEC2 ( ssmClient , instance . InstanceId ! , [ "ls -la" ] )
96
+ expect ( commandId ) . to . not . be . null
97
+ await sleep ( 500 )
98
+ } )
99
+ it ( "should throw when trying to call a command on a VM that is not active" , async ( ) => {
100
+ await expect ( runCommandOnEC2 ( ssmClient , "nonExistentOrOff" , [ "echo hello world" ] ) ) . to . be . rejected
101
+ } )
102
+ it ( "shuold retrieve the output of a command" , async ( ) => {
103
+ await sleep ( 20000 )
104
+ const output = await retrieveCommandOutput ( ssmClient , commandId , instance . InstanceId ! )
105
+ expect ( output . length ) . to . be . gt ( 0 )
106
+ } )
107
+ it ( "should throw when trying to retrieve the output of a non existent command" , async ( ) => {
108
+ await expect ( retrieveCommandOutput ( ssmClient , "nonExistentCommand" , instance . InstanceId ! ) ) . to . be . rejected
109
+ } )
110
+ afterAll ( async ( ) => {
111
+ await terminateEC2Instance ( ec2 , ssmTestInstance . InstanceId ! )
112
+ } )
113
+ } )
114
+
115
+ afterAll ( async ( ) => {
116
+ await terminateEC2Instance ( ec2 , instance . InstanceId ! )
117
+ } )
113
118
114
119
describe ( "Setup a ceremony that uses two VMs" , ( ) => {
115
120
// Sample data for running the test.
@@ -130,6 +135,27 @@ describe("VMs", () => {
130
135
let ceremonyId : string
131
136
const instancesToTerminate : string [ ] = [ ]
132
137
138
+ const zkeyPath = `${ cwd ( ) } /packages/actions/test/data/artifacts/circuit_0000.zkey`
139
+ const potPath = `${ cwd ( ) } /packages/actions/test/data/artifacts/powersOfTau28_hez_final_02.ptau`
140
+ let storagePath = getZkeyStorageFilePath (
141
+ circuit . data . prefix ! ,
142
+ `${ circuit . data . prefix } _${ genesisZkeyIndex } .zkey`
143
+ )
144
+
145
+ const potStoragePath = getPotStorageFilePath ( circuit . data . files ?. potFilename ! )
146
+ const outputDirectory = `${ cwd ( ) } /packages/actions/test/data/artifacts/output`
147
+
148
+ if ( envType === TestingEnvironment . PRODUCTION ) {
149
+ // create dir structure
150
+ fs . mkdirSync ( `${ outputDirectory } /contribute/attestation` , { recursive : true } )
151
+ fs . mkdirSync ( `${ outputDirectory } /contribute/transcripts` , { recursive : true } )
152
+ fs . mkdirSync ( `${ outputDirectory } /contribute/zkeys` , { recursive : true } )
153
+
154
+ }
155
+
156
+ // s3 objects we have to delete
157
+ const objectsToDelete = [ potStoragePath , storagePath ]
158
+
133
159
beforeAll ( async ( ) => {
134
160
// create 2 users the second is the coordinator
135
161
for ( let i = 0 ; i < 2 ; i ++ ) {
@@ -145,6 +171,13 @@ describe("VMs", () => {
145
171
// 1 create a bucket for the ceremony
146
172
await signInWithEmailAndPassword ( userAuth , users [ 1 ] . data . email , passwords [ 1 ] )
147
173
await createS3Bucket ( userFunctions , ceremonyBucket )
174
+
175
+
176
+ // zkey upload
177
+ await multiPartUpload ( userFunctions , ceremonyBucket , storagePath , zkeyPath , streamChunkSizeInMb )
178
+ // pot upload
179
+ await multiPartUpload ( userFunctions , ceremonyBucket , potStoragePath , potPath , streamChunkSizeInMb )
180
+ await signOut ( userAuth )
148
181
} )
149
182
150
183
afterAll ( async ( ) => {
@@ -153,7 +186,7 @@ describe("VMs", () => {
153
186
}
154
187
} )
155
188
156
- it ( "should create a ceremony and two VMs should spin up" , async ( ) => {
189
+ it . skip ( "should create a ceremony and two VMs should spin up" , async ( ) => {
157
190
// 1. setup ceremony
158
191
ceremonyId = await setupCeremony ( userFunctions , ceremony . data , ceremony . data . prefix ! , [ circuit . data ] )
159
192
@@ -175,6 +208,112 @@ describe("VMs", () => {
175
208
}
176
209
} )
177
210
178
- it ( "should verify a contribution" , async ( ) => { } )
211
+ it . skip ( "should verify a contribution" , async ( ) => {
212
+ // 1. login as user 2
213
+ await signInWithEmailAndPassword ( userAuth , users [ 2 ] . data . email , passwords [ 2 ] )
214
+ await sleep ( 500 )
215
+ // 2. get circuits for ceremony
216
+ const circuits = await getCeremonyCircuits ( userFirestore , ceremonyId )
217
+ expect ( circuits . length ) . to . be . gt ( 0 )
218
+
219
+ // 3. register for cermeony
220
+ const canParticipate = await checkParticipantForCeremony ( userFunctions , ceremonyId )
221
+ expect ( canParticipate ) . to . be . true
222
+
223
+ // 4. entropy
224
+ const entropy = randomBytes ( 32 ) . toString ( "hex" )
225
+
226
+ // 5. get circuit to contribute to
227
+ const circuit = getCircuitBySequencePosition ( circuits , 1 )
228
+ expect ( circuit ) . not . be . null
229
+
230
+ // 6. get circuit data
231
+ const currentProgress = circuit . data . waitingQueue . completedContributions
232
+ const currentZkeyIndex = formatZkeyIndex ( currentProgress )
233
+ const nextZkeyIndex = formatZkeyIndex ( currentProgress + 1 )
234
+
235
+ // 7. download previous contribution
236
+ storagePath = getZkeyStorageFilePath ( circuit . data . prefix , `${ circuit . data . prefix } _${ currentZkeyIndex } .zkey` )
237
+
238
+ const lastZkeyLocalFilePath = `${ outputDirectory } /contribute/zkeys/${ circuit . data . prefix } _${ currentZkeyIndex } .zkey`
239
+ const nextZkeyLocalFilePath = `${ outputDirectory } /contribute/zkeys/${ circuit . data . prefix } _${ nextZkeyIndex } .zkey`
240
+
241
+ const preSignedUrl = await generateGetObjectPreSignedUrl ( userFunctions , ceremonyBucket , storagePath )
242
+ // @ts -ignore
243
+ const getResponse = await fetch ( preSignedUrl )
244
+ await sleep ( 500 )
245
+ // Write the file to disk.
246
+ fs . writeFileSync ( lastZkeyLocalFilePath , await getResponse . buffer ( ) )
247
+ await sleep ( 500 )
248
+ // 9. progress to next step
249
+ await progressToNextCircuitForContribution ( userFunctions , ceremonyId )
250
+ await sleep ( 1000 )
251
+
252
+ const transcriptLocalFilePath = `${ outputDirectory } /${ getTranscriptLocalFilePath (
253
+ `${ circuit . data . prefix } _${ nextZkeyIndex } .log`
254
+ ) } `
255
+ const transcriptLogger = createCustomLoggerForFile ( transcriptLocalFilePath )
256
+ // 10. do contribution
257
+ await zKey . contribute ( lastZkeyLocalFilePath , nextZkeyLocalFilePath , users [ 2 ] . uid , entropy , transcriptLogger )
258
+ await sleep ( 1000 )
259
+
260
+ // read the contribution hash
261
+ const transcriptContents = fs . readFileSync ( transcriptLocalFilePath , "utf-8" ) . toString ( )
262
+ const matchContributionHash = transcriptContents . match ( / C o n t r i b u t i o n .+ H a s h .+ \n \t \t .+ \n \t \t .+ \n .+ \n \t \t .+ \n / )
263
+ const contributionHash = matchContributionHash ?. at ( 0 ) ?. replace ( "\n\t\t" , "" ) !
264
+
265
+ await progressToNextContributionStep ( userFunctions , ceremonyId )
266
+ await sleep ( 2000 )
267
+ await permanentlyStoreCurrentContributionTimeAndHash (
268
+ userFunctions ,
269
+ ceremonyId ,
270
+ new Date ( ) . valueOf ( ) ,
271
+ contributionHash
272
+ )
273
+ await sleep ( 2000 )
274
+
275
+ await progressToNextContributionStep ( userFunctions , ceremonyId )
276
+ await sleep ( 1000 )
277
+
278
+ const participant = await getDocumentById (
279
+ userFirestore ,
280
+ getParticipantsCollectionPath ( ceremonyId ) ,
281
+ users [ 2 ] . uid
282
+ )
283
+
284
+ // Upload
285
+ const nextZkeyStoragePath = getZkeyStorageFilePath (
286
+ circuit . data . prefix ,
287
+ `${ circuit . data . prefix } _${ nextZkeyIndex } .zkey`
288
+ )
289
+ await multiPartUpload (
290
+ userFunctions ,
291
+ ceremonyBucket ,
292
+ nextZkeyStoragePath ,
293
+ nextZkeyLocalFilePath ,
294
+ streamChunkSizeInMb ,
295
+ ceremony . uid ,
296
+ participant . data ( ) ! . tempContributionData
297
+ )
298
+ await sleep ( 1000 )
299
+
300
+ objectsToDelete . push ( nextZkeyStoragePath )
301
+
302
+ // Execute contribution verification.
303
+ const tempCircuit = await getDocumentById (
304
+ userFirestore ,
305
+ getCircuitsCollectionPath ( ceremonyId ) ,
306
+ circuit . id
307
+ )
308
+
309
+ await verifyContribution (
310
+ userFunctions ,
311
+ ceremonyId ,
312
+ tempCircuit ,
313
+ ceremonyBucket ,
314
+ users [ 2 ] . uid ,
315
+ String ( process . env . FIREBASE_CF_URL_VERIFY_CONTRIBUTION )
316
+ )
317
+ } )
179
318
} )
180
319
} )
0 commit comments