Skip to content

Commit dfbf3a7

Browse files
committed
fix(vms): tests
1 parent 89b621b commit dfbf3a7

File tree

2 files changed

+213
-77
lines changed

2 files changed

+213
-77
lines changed
+213-74
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import chai, { expect } from "chai"
22
import chaiAsPromised from "chai-as-promised"
33
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"
56
import {
67
checkEC2Status,
78
createEC2Client,
@@ -17,9 +18,13 @@ import {
1718
} from "../../src/helpers/ec2"
1819
import { P0tionEC2Instance } from "../../src/types"
1920
import { fakeCeremoniesData, fakeCircuitsData, fakeUsersData } from "../data/samples"
20-
import { getAuth, signInWithEmailAndPassword } from "firebase/auth"
21+
import { getAuth, signInWithEmailAndPassword, signOut } from "firebase/auth"
2122
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"
2328
chai.use(chaiAsPromised)
2429

2530
// @note AWS EC2 on demand VM tests
@@ -34,82 +39,82 @@ describe("VMs", () => {
3439
ec2 = await createEC2Client()
3540
})
3641

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+
})
4449

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+
})
4954

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+
})
5560

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+
})
6065

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+
})
6469

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+
})
6974

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+
})
7479

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+
})
113118

114119
describe("Setup a ceremony that uses two VMs", () => {
115120
// Sample data for running the test.
@@ -130,6 +135,27 @@ describe("VMs", () => {
130135
let ceremonyId: string
131136
const instancesToTerminate: string[] = []
132137

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+
133159
beforeAll(async () => {
134160
// create 2 users the second is the coordinator
135161
for (let i = 0; i < 2; i++) {
@@ -145,6 +171,13 @@ describe("VMs", () => {
145171
// 1 create a bucket for the ceremony
146172
await signInWithEmailAndPassword(userAuth, users[1].data.email, passwords[1])
147173
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)
148181
})
149182

150183
afterAll(async () => {
@@ -153,7 +186,7 @@ describe("VMs", () => {
153186
}
154187
})
155188

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 () => {
157190
// 1. setup ceremony
158191
ceremonyId = await setupCeremony(userFunctions, ceremony.data, ceremony.data.prefix!, [circuit.data])
159192

@@ -175,6 +208,112 @@ describe("VMs", () => {
175208
}
176209
})
177210

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(/Contribution.+Hash.+\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+
})
179318
})
180319
})

packages/backend/src/functions/ceremony.ts

-3
Original file line numberDiff line numberDiff line change
@@ -148,9 +148,6 @@ export const setupCeremony = functions
148148
roleArn
149149
)
150150

151-
// @todo calculate how long it usually takes for the VM to be ready
152-
await sleep(500000)
153-
154151
// html encode circuit data.
155152
const encodedCircuit = htmlEncodeCircuitData(circuit)
156153
// Prepare tx to write circuit data.

0 commit comments

Comments
 (0)