mirror of
https://github.com/privacy-scaling-explorations/p0tion.git
synced 2026-04-21 03:00:07 -04:00
feat: create a circuit module
This commit is contained in:
@@ -7,6 +7,7 @@ import { JwtModule } from "@nestjs/jwt"
|
||||
import { CeremoniesModule } from "./ceremonies/ceremonies.module"
|
||||
import { StorageModule } from "./storage/storage.module"
|
||||
import { ScheduleModule } from "@nestjs/schedule"
|
||||
import { CircuitsModule } from "./circuits/circuits.module"
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -28,7 +29,8 @@ import { ScheduleModule } from "@nestjs/schedule"
|
||||
AuthModule,
|
||||
UsersModule,
|
||||
CeremoniesModule,
|
||||
StorageModule
|
||||
StorageModule,
|
||||
CircuitsModule
|
||||
]
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
@@ -6,13 +6,14 @@ import { UserEntity } from "src/users/entities/user.entity"
|
||||
import { UsersService } from "src/users/service/users.service"
|
||||
import { CeremoniesService } from "src/ceremonies/service/ceremonies.service"
|
||||
import { CeremonyEntity } from "src/ceremonies/entities/ceremony.entity"
|
||||
import { CircuitEntity } from "src/ceremonies/entities/circuit.entity"
|
||||
import { ParticipantEntity } from "src/ceremonies/entities/participant.entity"
|
||||
import { CircuitsService } from "src/circuits/service/circuits.service"
|
||||
import { CircuitEntity } from "src/circuits/entities/circuit.entity"
|
||||
|
||||
@Module({
|
||||
imports: [SequelizeModule.forFeature([UserEntity, CeremonyEntity, CircuitEntity, ParticipantEntity])],
|
||||
exports: [SequelizeModule],
|
||||
controllers: [AuthController],
|
||||
providers: [AuthService, UsersService, CeremoniesService]
|
||||
providers: [AuthService, UsersService, CeremoniesService, CircuitsService]
|
||||
})
|
||||
export class AuthModule {}
|
||||
|
||||
@@ -3,14 +3,15 @@ import { CeremoniesController } from "./controller/ceremonies.controller"
|
||||
import { CeremoniesService } from "./service/ceremonies.service"
|
||||
import { SequelizeModule } from "@nestjs/sequelize"
|
||||
import { CeremonyEntity } from "./entities/ceremony.entity"
|
||||
import { CircuitEntity } from "./entities/circuit.entity"
|
||||
import { UsersService } from "src/users/service/users.service"
|
||||
import { UserEntity } from "src/users/entities/user.entity"
|
||||
import { ParticipantEntity } from "./entities/participant.entity"
|
||||
import { CircuitsService } from "src/circuits/service/circuits.service"
|
||||
import { CircuitEntity } from "src/circuits/entities/circuit.entity"
|
||||
|
||||
@Module({
|
||||
controllers: [CeremoniesController],
|
||||
providers: [CeremoniesService, UsersService],
|
||||
providers: [CeremoniesService, CircuitsService, UsersService],
|
||||
imports: [SequelizeModule.forFeature([CeremonyEntity, CircuitEntity, ParticipantEntity, UserEntity])],
|
||||
exports: [SequelizeModule]
|
||||
})
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { CeremonyState, CeremonyTimeoutType, CeremonyType } from "@p0tion/actions"
|
||||
import { ArrayMinSize, IsArray, IsEnum, IsIn, IsNumber, IsOptional, IsString, ValidateNested } from "class-validator"
|
||||
import { CircuitDto } from "./circuit-dto"
|
||||
import { Type } from "class-transformer"
|
||||
import { AuthProvider } from "src/types/enums"
|
||||
import { GithubDto } from "./github-dto"
|
||||
import { SiweDto } from "./siwe-dto"
|
||||
import { BandadaDto } from "./bandada-dto"
|
||||
import { CircuitDto } from "src/circuits/dto/circuits-dto"
|
||||
|
||||
export class CeremonyDto {
|
||||
@IsString()
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { AutoIncrement, Column, DataType, ForeignKey, HasMany, Model, Table } from "sequelize-typescript"
|
||||
import { CeremonyState, CeremonyTimeoutType, CeremonyType } from "@p0tion/actions"
|
||||
import { CircuitEntity } from "./circuit.entity"
|
||||
import { AuthProvider } from "src/types/enums"
|
||||
import { GithubDto } from "../dto/github-dto"
|
||||
import { SiweDto } from "../dto/siwe-dto"
|
||||
import { BandadaDto } from "../dto/bandada-dto"
|
||||
import { UserEntity } from "src/users/entities/user.entity"
|
||||
import { ParticipantEntity } from "./participant.entity"
|
||||
import { CircuitEntity } from "src/circuits/entities/circuit.entity"
|
||||
|
||||
@Table
|
||||
export class CeremonyEntity extends Model {
|
||||
|
||||
@@ -2,32 +2,26 @@ import { Injectable } from "@nestjs/common"
|
||||
import { CeremonyDto } from "../dto/ceremony-dto"
|
||||
import { InjectModel } from "@nestjs/sequelize"
|
||||
import { CeremonyEntity } from "../entities/ceremony.entity"
|
||||
import { CircuitEntity } from "../entities/circuit.entity"
|
||||
import { CircuitDto } from "../dto/circuit-dto"
|
||||
import {
|
||||
CeremonyState,
|
||||
CircuitContributionVerificationMechanism,
|
||||
computeDiskSizeForVM,
|
||||
createEC2Instance,
|
||||
getBucketName,
|
||||
terminateEC2Instance,
|
||||
vmBootstrapCommand,
|
||||
vmBootstrapScriptFilename,
|
||||
vmDependenciesAndCacheArtifactsCommand
|
||||
terminateEC2Instance
|
||||
} from "@p0tion/actions"
|
||||
import { createEC2Client, getAWSVariables, getFinalContribution, uploadFileToBucketNoFile } from "src/lib/utils"
|
||||
import { createEC2Client, getFinalContribution } from "src/lib/utils"
|
||||
import { SPECIFIC_ERRORS, logAndThrowError, printLog } from "src/lib/errors"
|
||||
import { LogLevel } from "src/types/enums"
|
||||
import { Cron, CronExpression } from "@nestjs/schedule"
|
||||
import { Op } from "sequelize"
|
||||
import { CircuitsService } from "src/circuits/service/circuits.service"
|
||||
import { CircuitEntity } from "src/circuits/entities/circuit.entity"
|
||||
|
||||
@Injectable()
|
||||
export class CeremoniesService {
|
||||
constructor(
|
||||
@InjectModel(CeremonyEntity)
|
||||
private ceremonyModel: typeof CeremonyEntity,
|
||||
@InjectModel(CircuitEntity)
|
||||
private circuitModel: typeof CircuitEntity
|
||||
private circuitsService: CircuitsService
|
||||
) {}
|
||||
|
||||
async create(ceremonyDto: CeremonyDto) {
|
||||
@@ -35,95 +29,13 @@ export class CeremoniesService {
|
||||
|
||||
const ceremony = await this.ceremonyModel.create(ceremonyData as any)
|
||||
|
||||
const circuitEntities = await this.createCircuits(circuits, ceremony)
|
||||
const circuitEntities = await this.circuitsService.createCircuits(circuits, ceremony)
|
||||
await ceremony.$set("circuits", circuitEntities)
|
||||
|
||||
printLog(`Setup completed for ceremony ${ceremony.id}`, LogLevel.DEBUG)
|
||||
return ceremony
|
||||
}
|
||||
|
||||
async createCircuits(circuits: CircuitDto[], ceremony: CeremonyEntity) {
|
||||
const bucketName = getBucketName(ceremony.prefix, String(process.env.AWS_CEREMONY_BUCKET_POSTFIX))
|
||||
const ceremonyId = ceremony.id
|
||||
|
||||
const circuitEntities = []
|
||||
for (let i = 0, ni = circuits.length; i < ni; i++) {
|
||||
let circuit = circuits[i]
|
||||
// create the waiting queue object
|
||||
circuit = {
|
||||
...circuit,
|
||||
waitingQueue: {
|
||||
contributors: [],
|
||||
currentContributor: "",
|
||||
completedContributions: 0,
|
||||
failedContributions: 0
|
||||
}
|
||||
}
|
||||
// create VMs outside this server if the option was selected
|
||||
if (circuit.verification.cfOrVm === CircuitContributionVerificationMechanism.VM) {
|
||||
const { instance, vmDiskSize } = await this.setupAWSEnvironment(circuit, bucketName)
|
||||
// Update the circuit document info accordingly.
|
||||
circuit = {
|
||||
...circuit,
|
||||
verification: {
|
||||
cfOrVm: circuit.verification.cfOrVm,
|
||||
vm: {
|
||||
vmConfigurationType: circuit.verification.vm.vmConfigurationType,
|
||||
vmInstanceId: instance.instanceId,
|
||||
vmDiskSize
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const circuitEntity = await this.circuitModel.create({ ...circuit, ceremonyId })
|
||||
circuitEntities.push(circuitEntity)
|
||||
}
|
||||
return circuitEntities
|
||||
}
|
||||
|
||||
async setupAWSEnvironment(circuit: CircuitDto, bucketName: string) {
|
||||
// VM command to be run at the startup.
|
||||
const startupCommand = vmBootstrapCommand(`${bucketName}/circuits/${circuit.name!}`)
|
||||
|
||||
// Get EC2 client.
|
||||
const ec2Client = await createEC2Client()
|
||||
|
||||
// Get AWS variables.
|
||||
const { snsTopic, region } = getAWSVariables()
|
||||
|
||||
// Prepare dependencies and cache artifacts command.
|
||||
const vmCommands = vmDependenciesAndCacheArtifactsCommand(
|
||||
`${bucketName}/${circuit.files.initialZkeyStoragePath}`,
|
||||
`${bucketName}/${circuit.files.potStoragePath}`,
|
||||
snsTopic,
|
||||
region
|
||||
)
|
||||
|
||||
printLog(`Check VM dependencies and cache artifacts commands ${vmCommands.join("\n")}`, LogLevel.DEBUG)
|
||||
// Upload the post-startup commands script file.
|
||||
printLog(`Uploading VM post-startup commands script file ${vmBootstrapScriptFilename}`, LogLevel.DEBUG)
|
||||
await uploadFileToBucketNoFile(
|
||||
bucketName,
|
||||
`circuits/${circuit.name!}/${vmBootstrapScriptFilename}`,
|
||||
vmCommands.join("\n")
|
||||
)
|
||||
// TODO: should we create a AWS instance or run it in a docker file?
|
||||
// Compute the VM disk space requirement (in GB).
|
||||
const vmDiskSize = computeDiskSizeForVM(circuit.zKeySizeInBytes, circuit.metadata.pot)
|
||||
|
||||
printLog(`Check VM startup commands ${startupCommand.join("\n")}`, LogLevel.DEBUG)
|
||||
|
||||
// Configure and instantiate a new VM based on the coordinator input.
|
||||
const instance = await createEC2Instance(
|
||||
ec2Client,
|
||||
startupCommand,
|
||||
circuit.verification.vm.vmConfigurationType,
|
||||
vmDiskSize,
|
||||
circuit.verification.vm.vmDiskType
|
||||
)
|
||||
return { instance, vmDiskSize }
|
||||
}
|
||||
|
||||
findById(id: number) {
|
||||
return this.ceremonyModel.findByPk(id, { include: [CircuitEntity] })
|
||||
}
|
||||
|
||||
12
packages/api/src/circuits/circuits.module.ts
Normal file
12
packages/api/src/circuits/circuits.module.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Module } from "@nestjs/common"
|
||||
import { CircuitsController } from "./controller/circuits.controller"
|
||||
import { CircuitsService } from "./service/circuits.service"
|
||||
import { SequelizeModule } from "@nestjs/sequelize"
|
||||
import { CircuitEntity } from "./entities/circuit.entity"
|
||||
|
||||
@Module({
|
||||
controllers: [CircuitsController],
|
||||
providers: [CircuitsService],
|
||||
imports: [SequelizeModule.forFeature([CircuitEntity])]
|
||||
})
|
||||
export class CircuitsModule {}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { Test, TestingModule } from "@nestjs/testing"
|
||||
import { CircuitsController } from "./circuits.controller"
|
||||
|
||||
describe("CircuitsController", () => {
|
||||
let controller: CircuitsController
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [CircuitsController]
|
||||
}).compile()
|
||||
|
||||
controller = module.get<CircuitsController>(CircuitsController)
|
||||
})
|
||||
|
||||
it("should be defined", () => {
|
||||
expect(controller).toBeDefined()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,4 @@
|
||||
import { Controller } from "@nestjs/common"
|
||||
|
||||
@Controller("circuits")
|
||||
export class CircuitsController {}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { CircuitDto } from "./circuit-dto"
|
||||
import { CircuitDto } from "./circuits-dto"
|
||||
|
||||
describe("CircuitDto", () => {
|
||||
describe("CircuitsDto", () => {
|
||||
it("should be defined", () => {
|
||||
expect(new CircuitDto()).toBeDefined()
|
||||
})
|
||||
@@ -1,5 +1,4 @@
|
||||
import { AutoIncrement, Column, DataType, ForeignKey, Model, Table } from "sequelize-typescript"
|
||||
import { CeremonyEntity } from "./ceremony.entity"
|
||||
import {
|
||||
AvgTimingsDto,
|
||||
CompilationArtifactsDto,
|
||||
@@ -9,7 +8,8 @@ import {
|
||||
TemplateDto,
|
||||
VerificationDto,
|
||||
WaitingQueueDto
|
||||
} from "../dto/circuit-dto"
|
||||
} from "../dto/circuits-dto"
|
||||
import { CeremonyEntity } from "src/ceremonies/entities/ceremony.entity"
|
||||
|
||||
@Table
|
||||
export class CircuitEntity extends Model {
|
||||
18
packages/api/src/circuits/service/circuits.service.spec.ts
Normal file
18
packages/api/src/circuits/service/circuits.service.spec.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Test, TestingModule } from "@nestjs/testing"
|
||||
import { CircuitsService } from "./circuits.service"
|
||||
|
||||
describe("CircuitsService", () => {
|
||||
let service: CircuitsService
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [CircuitsService]
|
||||
}).compile()
|
||||
|
||||
service = module.get<CircuitsService>(CircuitsService)
|
||||
})
|
||||
|
||||
it("should be defined", () => {
|
||||
expect(service).toBeDefined()
|
||||
})
|
||||
})
|
||||
108
packages/api/src/circuits/service/circuits.service.ts
Normal file
108
packages/api/src/circuits/service/circuits.service.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
import { Injectable } from "@nestjs/common"
|
||||
import { InjectModel } from "@nestjs/sequelize"
|
||||
import { CircuitEntity } from "../entities/circuit.entity"
|
||||
import {
|
||||
CircuitContributionVerificationMechanism,
|
||||
computeDiskSizeForVM,
|
||||
createEC2Client,
|
||||
createEC2Instance,
|
||||
getBucketName,
|
||||
vmBootstrapCommand,
|
||||
vmBootstrapScriptFilename,
|
||||
vmDependenciesAndCacheArtifactsCommand
|
||||
} from "@p0tion/actions"
|
||||
import { CircuitDto } from "../dto/circuits-dto"
|
||||
import { CeremonyEntity } from "src/ceremonies/entities/ceremony.entity"
|
||||
import { getAWSVariables, uploadFileToBucketNoFile } from "src/lib/utils"
|
||||
import { printLog } from "src/lib/errors"
|
||||
import { LogLevel } from "src/types/enums"
|
||||
|
||||
@Injectable()
|
||||
export class CircuitsService {
|
||||
constructor(
|
||||
@InjectModel(CircuitEntity)
|
||||
private circuitModel: typeof CircuitEntity
|
||||
) {}
|
||||
|
||||
async createCircuits(circuits: CircuitDto[], ceremony: CeremonyEntity) {
|
||||
const bucketName = getBucketName(ceremony.prefix, String(process.env.AWS_CEREMONY_BUCKET_POSTFIX))
|
||||
const ceremonyId = ceremony.id
|
||||
|
||||
const circuitEntities = []
|
||||
for (let i = 0, ni = circuits.length; i < ni; i++) {
|
||||
let circuit = circuits[i]
|
||||
// create the waiting queue object
|
||||
circuit = {
|
||||
...circuit,
|
||||
waitingQueue: {
|
||||
contributors: [],
|
||||
currentContributor: "",
|
||||
completedContributions: 0,
|
||||
failedContributions: 0
|
||||
}
|
||||
}
|
||||
// create VMs outside this server if the option was selected
|
||||
if (circuit.verification.cfOrVm === CircuitContributionVerificationMechanism.VM) {
|
||||
const { instance, vmDiskSize } = await this.setupAWSEnvironment(circuit, bucketName)
|
||||
// Update the circuit document info accordingly.
|
||||
circuit = {
|
||||
...circuit,
|
||||
verification: {
|
||||
cfOrVm: circuit.verification.cfOrVm,
|
||||
vm: {
|
||||
vmConfigurationType: circuit.verification.vm.vmConfigurationType,
|
||||
vmInstanceId: instance.instanceId,
|
||||
vmDiskSize
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const circuitEntity = await this.circuitModel.create({ ...circuit, ceremonyId })
|
||||
circuitEntities.push(circuitEntity)
|
||||
}
|
||||
return circuitEntities
|
||||
}
|
||||
|
||||
async setupAWSEnvironment(circuit: CircuitDto, bucketName: string) {
|
||||
// VM command to be run at the startup.
|
||||
const startupCommand = vmBootstrapCommand(`${bucketName}/circuits/${circuit.name!}`)
|
||||
|
||||
// Get EC2 client.
|
||||
const ec2Client = await createEC2Client()
|
||||
|
||||
// Get AWS variables.
|
||||
const { snsTopic, region } = getAWSVariables()
|
||||
|
||||
// Prepare dependencies and cache artifacts command.
|
||||
const vmCommands = vmDependenciesAndCacheArtifactsCommand(
|
||||
`${bucketName}/${circuit.files.initialZkeyStoragePath}`,
|
||||
`${bucketName}/${circuit.files.potStoragePath}`,
|
||||
snsTopic,
|
||||
region
|
||||
)
|
||||
|
||||
printLog(`Check VM dependencies and cache artifacts commands ${vmCommands.join("\n")}`, LogLevel.DEBUG)
|
||||
// Upload the post-startup commands script file.
|
||||
printLog(`Uploading VM post-startup commands script file ${vmBootstrapScriptFilename}`, LogLevel.DEBUG)
|
||||
await uploadFileToBucketNoFile(
|
||||
bucketName,
|
||||
`circuits/${circuit.name!}/${vmBootstrapScriptFilename}`,
|
||||
vmCommands.join("\n")
|
||||
)
|
||||
// TODO: should we create a AWS instance or run it in a docker file?
|
||||
// Compute the VM disk space requirement (in GB).
|
||||
const vmDiskSize = computeDiskSizeForVM(circuit.zKeySizeInBytes, circuit.metadata.pot)
|
||||
|
||||
printLog(`Check VM startup commands ${startupCommand.join("\n")}`, LogLevel.DEBUG)
|
||||
|
||||
// Configure and instantiate a new VM based on the coordinator input.
|
||||
const instance = await createEC2Instance(
|
||||
ec2Client,
|
||||
startupCommand,
|
||||
circuit.verification.vm.vmConfigurationType,
|
||||
vmDiskSize,
|
||||
circuit.verification.vm.vmDiskType
|
||||
)
|
||||
return { instance, vmDiskSize }
|
||||
}
|
||||
}
|
||||
@@ -18,10 +18,10 @@ import {
|
||||
getBucketName,
|
||||
getZkeyStorageFilePath
|
||||
} from "@p0tion/actions"
|
||||
import { CircuitEntity } from "src/ceremonies/entities/circuit.entity"
|
||||
import { ParticipantEntity } from "src/ceremonies/entities/participant.entity"
|
||||
import { CeremoniesService } from "src/ceremonies/service/ceremonies.service"
|
||||
import { ParticipantsService } from "src/ceremonies/service/participants.service"
|
||||
import { CircuitEntity } from "src/circuits/entities/circuit.entity"
|
||||
import { COMMON_ERRORS, SPECIFIC_ERRORS, logAndThrowError, makeError, printLog } from "src/lib/errors"
|
||||
import { getS3Client } from "src/lib/services"
|
||||
import { getCurrentServerTimestampInMillis } from "src/lib/utils"
|
||||
|
||||
@@ -6,13 +6,14 @@ import { UserEntity } from "src/users/entities/user.entity"
|
||||
import { SequelizeModule } from "@nestjs/sequelize"
|
||||
import { CeremoniesService } from "src/ceremonies/service/ceremonies.service"
|
||||
import { CeremonyEntity } from "src/ceremonies/entities/ceremony.entity"
|
||||
import { CircuitEntity } from "src/ceremonies/entities/circuit.entity"
|
||||
import { ParticipantEntity } from "src/ceremonies/entities/participant.entity"
|
||||
import { ParticipantsService } from "src/ceremonies/service/participants.service"
|
||||
import { CircuitsService } from "src/circuits/service/circuits.service"
|
||||
import { CircuitEntity } from "src/circuits/entities/circuit.entity"
|
||||
|
||||
@Module({
|
||||
controllers: [StorageController],
|
||||
imports: [SequelizeModule.forFeature([UserEntity, CeremonyEntity, CircuitEntity, ParticipantEntity])],
|
||||
providers: [StorageService, UsersService, CeremoniesService, ParticipantsService]
|
||||
providers: [StorageService, UsersService, CeremoniesService, CircuitsService, ParticipantsService]
|
||||
})
|
||||
export class StorageModule {}
|
||||
|
||||
Reference in New Issue
Block a user