diff --git a/spartan/aztec-node/templates/secret.yaml b/spartan/aztec-node/templates/secret.yaml index 1271a93a6ce2..0e057fb646fa 100644 --- a/spartan/aztec-node/templates/secret.yaml +++ b/spartan/aztec-node/templates/secret.yaml @@ -1,7 +1,8 @@ +{{- if or (has "--sequencer" .Values.node.startCmd) (has "--prover-node" .Values.node.startCmd) }} apiVersion: v1 kind: Secret metadata: - name: {{ include "chart.name" . }}-l1-publisher + name: {{ include "chart.fullname" . }}-l1-publisher labels: {{- include "chart.labels" . | nindent 4 }} data: @@ -11,3 +12,4 @@ data: {{- else }} privateKeys: {{ join "\n" .Values.node.l1Publisher.privateKeys | b64enc }} {{- end }} +{{- end }} diff --git a/spartan/aztec-node/templates/statefulset.yaml b/spartan/aztec-node/templates/statefulset.yaml index 824c07e675e3..7c58aa59b148 100644 --- a/spartan/aztec-node/templates/statefulset.yaml +++ b/spartan/aztec-node/templates/statefulset.yaml @@ -1,11 +1,11 @@ apiVersion: apps/v1 kind: StatefulSet metadata: - name: {{ include "chart.name" . }}-node + name: {{ include "chart.fullname" . }} labels: {{- include "chart.labels" . | nindent 4 }} spec: - serviceName: {{ include "chart.name" . }}-node + serviceName: {{ include "chart.fullname" . }} replicas: {{ .Values.node.replicas }} podManagementPolicy: {{ .Values.podManagementPolicy }} updateStrategy: @@ -23,6 +23,7 @@ spec: serviceAccountName: {{ include "chart.serviceAccountName" . }} dnsPolicy: ClusterFirstWithHostNet hostNetwork: {{ .Values.hostNetwork }} + {{- if or .Values.service.p2p.nodePortEnabled .Values.hostNetwork }} affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: @@ -34,6 +35,7 @@ spec: - node topologyKey: kubernetes.io/hostname namespaceSelector: {} + {{- end }} initContainers: {{- if .Values.initContainers }} {{- tpl (toYaml .Values.initContainers | nindent 8) $ }} @@ -77,7 +79,7 @@ spec: mountPath: /env {{- end }} containers: - - name: node + - name: aztec image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" imagePullPolicy: {{ .Values.image.pullPolicy }} command: @@ -119,6 +121,11 @@ spec: {{- end }} start_cmd+=({{ join " " .Values.node.startCmd }}) + + {{- if .Values.node.preStartScript }} + {{ .Values.node.preStartScript | nindent 14 }} + + {{- end }} "${start_cmd[@]}" startupProbe: httpGet: @@ -197,7 +204,7 @@ spec: value: {{ .Values.node.remoteUrl.archiver | quote }} {{- end }} {{- if .Values.node.remoteUrl.proverBroker }} - - name: PROVER_BROKER_URL + - name: PROVER_BROKER_HOST value: {{ .Values.node.remoteUrl.proverBroker | quote }} {{- end }} {{- if .Values.node.remoteUrl.blobSink }} @@ -251,19 +258,25 @@ spec: ports: - containerPort: {{ .Values.service.httpPort }} name: http-rpc + {{- if .Values.service.admin.enabled }} - containerPort: {{ .Values.service.admin.port }} name: admin + {{- end }} + {{- if .Values.service.p2p.enabled }} - containerPort: {{ .Values.service.p2p.port }} name: p2p-tcp - containerPort: {{ .Values.service.p2p.port }} protocol: UDP name: p2p-udp + {{- end }} resources: {{- toYaml .Values.node.resources | nindent 12 }} volumes: + {{- if or (has "--sequencer" .Values.node.startCmd) (has "--prover-node" .Values.node.startCmd) }} - name: l1-publisher secret: - secretName: {{ include "chart.name" . }}-l1-publisher + secretName: {{ include "chart.fullname" . }}-l1-publisher + {{- end }} {{- if or .Values.service.p2p.nodePortEnabled .Values.hostNetwork }} - name: init-nodeport emptyDir: {} diff --git a/spartan/aztec-node/templates/svc.headless.yaml b/spartan/aztec-node/templates/svc.headless.yaml index 17a98df794fc..2c9256e64496 100644 --- a/spartan/aztec-node/templates/svc.headless.yaml +++ b/spartan/aztec-node/templates/svc.headless.yaml @@ -1,3 +1,4 @@ +{{- if .Values.service.headless.enabled }} apiVersion: v1 kind: Service metadata: @@ -7,6 +8,7 @@ metadata: spec: clusterIP: None ports: + {{- if .Values.service.p2p.enabled }} - port: {{ .Values.service.p2p.port }} targetPort: p2p-tcp protocol: TCP @@ -15,13 +17,17 @@ spec: targetPort: p2p-udp protocol: UDP name: p2p-udp + {{- end }} - port: {{ .Values.service.httpPort }} targetPort: http-rpc protocol: TCP name: http-rpc + {{- if .Values.service.admin.enabled }} - port: {{ .Values.service.admin.port }} targetPort: admin protocol: TCP name: admin + {{- end }} selector: {{- include "chart.selectorLabels" . | nindent 4 }} +{{- end }} diff --git a/spartan/aztec-node/templates/svc.nodeport.yaml b/spartan/aztec-node/templates/svc.nodeport.yaml index e20cd60b227d..06a29b244303 100644 --- a/spartan/aztec-node/templates/svc.nodeport.yaml +++ b/spartan/aztec-node/templates/svc.nodeport.yaml @@ -1,4 +1,4 @@ -{{- if and .Values.service.p2p.nodePortEnabled (not .Values.hostNetwork) -}} +{{- if and .Values.service.p2p.enabled .Values.service.p2p.nodePortEnabled (not .Values.hostNetwork) -}} {{- range $i, $e := until (.Values.node.replicas | int) }} --- apiVersion: v1 diff --git a/spartan/aztec-node/templates/svc.yaml b/spartan/aztec-node/templates/svc.yaml index 0b5bfefed623..5c7ad63c861e 100644 --- a/spartan/aztec-node/templates/svc.yaml +++ b/spartan/aztec-node/templates/svc.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: Service metadata: - name: {{ include "chart.fullname" . }}-node-svc + name: {{ include "chart.fullname" . }} labels: {{- include "chart.labels" . | nindent 4 }} {{- with .Values.service.ingress.annotations }} @@ -11,6 +11,7 @@ metadata: spec: type: ClusterIP ports: + {{- if .Values.service.p2p.enabled }} - port: {{ .Values.service.p2p.port }} targetPort: p2p-tcp protocol: TCP @@ -19,13 +20,16 @@ spec: targetPort: p2p-udp protocol: UDP name: p2p-udp + {{- end }} - port: {{ .Values.service.httpPort }} targetPort: http-rpc protocol: TCP name: http-rpc + {{- if .Values.service.admin.enabled }} - port: {{ .Values.service.admin.port }} targetPort: admin protocol: TCP name: admin + {{- end }} selector: {{- include "chart.selectorLabels" . | nindent 4 }} diff --git a/spartan/aztec-node/values.yaml b/spartan/aztec-node/values.yaml index 249e45dc4875..86e3abad927d 100644 --- a/spartan/aztec-node/values.yaml +++ b/spartan/aztec-node/values.yaml @@ -54,6 +54,8 @@ node: ## Example: "X-API-KEY" l1ConsensusHostApiKeyHeaders: [] + preStartScript: "" + startCmd: - --node - --archiver @@ -138,6 +140,9 @@ service: hosts: [] # - node.example.com + headless: + enabled: true + p2p: enabled: true nodePortEnabled: true diff --git a/yarn-project/aztec/src/cli/cmds/start_prover_agent.ts b/yarn-project/aztec/src/cli/cmds/start_prover_agent.ts index 9d28bdd1fa1a..c00b4c34114a 100644 --- a/yarn-project/aztec/src/cli/cmds/start_prover_agent.ts +++ b/yarn-project/aztec/src/cli/cmds/start_prover_agent.ts @@ -11,6 +11,7 @@ import { proverAgentConfigMappings, } from '@aztec/prover-client/broker'; import { getProverNodeAgentConfigFromEnv } from '@aztec/prover-node'; +import { ProverAgentApiSchema } from '@aztec/stdlib/interfaces/server'; import { initTelemetryClient, makeTracedFetch, telemetryClientConfigMappings } from '@aztec/telemetry-client'; import { extractRelevantOptions, preloadCrsDataForServerSideProving } from '../util.js'; @@ -33,10 +34,12 @@ export async function startProverAgent( }; if (config.realProofs && (!config.bbBinaryPath || !config.acvmBinaryPath)) { + userLog(`Requested real proving but no path to bb or acvm binaries provided`); process.exit(1); } if (!config.proverBrokerUrl) { + userLog(`Missing prover broker URL. Pass --proverAgent.proverBrokerUrl `); process.exit(1); } @@ -61,6 +64,16 @@ export async function startProverAgent( ), ); + // expose all agents as individual services + for (let i = 0; i < agents.length; i++) { + services[`agent${i}`] = [agents[i], ProverAgentApiSchema, () => agents[i].getStatus().status !== 'stopped']; + } + + // shortcut in the most common case of having a single running agent + if (agents.length === 1) { + services[`agent`] = [agents[0], ProverAgentApiSchema, () => agents[0].getStatus().status !== 'stopped']; + } + await Promise.all(agents.map(agent => agent.start())); signalHandlers.push(async () => { diff --git a/yarn-project/prover-client/src/proving_broker/proving_agent.ts b/yarn-project/prover-client/src/proving_broker/proving_agent.ts index f37d92de45ee..c7debc20e65b 100644 --- a/yarn-project/prover-client/src/proving_broker/proving_agent.ts +++ b/yarn-project/prover-client/src/proving_broker/proving_agent.ts @@ -5,6 +5,7 @@ import { truncate } from '@aztec/foundation/string'; import { ProvingError } from '@aztec/stdlib/errors'; import type { GetProvingJobResponse, + ProverAgentStatus, ProvingJobConsumer, ProvingJobId, ProvingJobInputs, @@ -71,6 +72,19 @@ export class ProvingAgent implements Traceable { await this.runningPromise.stop(); } + public getStatus(): ProverAgentStatus { + if (this.currentJobController) { + return { + status: 'proving', + jobId: this.currentJobController.getJobId(), + proofType: this.currentJobController.getProofType(), + startedAtISO: new Date(this.currentJobController.getStartedAt()).toISOString(), + }; + } + + return this.runningPromise.isRunning() ? { status: 'running' } : { status: 'stopped' }; + } + @trackSpan('ProvingAgent.safeWork') private async work() { // every tick we need to take one of the following actions: diff --git a/yarn-project/stdlib/src/interfaces/prover-agent.test.ts b/yarn-project/stdlib/src/interfaces/prover-agent.test.ts index 9a23df0e18e5..59a80240f793 100644 --- a/yarn-project/stdlib/src/interfaces/prover-agent.test.ts +++ b/yarn-project/stdlib/src/interfaces/prover-agent.test.ts @@ -1,6 +1,9 @@ +import { randomBytes } from '@aztec/foundation/crypto'; import { type JsonRpcTestContext, createJsonRpcTestSetup } from '@aztec/foundation/json-rpc/test'; -import { type ProverAgentApi, ProverAgentApiSchema } from './prover-agent.js'; +import { ProvingRequestType } from '../proofs/proving_request_type.js'; +import { type ProverAgentApi, ProverAgentApiSchema, type ProverAgentStatus } from './prover-agent.js'; +import { makeProvingJobId } from './proving-job.js'; describe('ProverAgentApiSchema', () => { let handler: MockProverAgent; @@ -23,35 +26,32 @@ describe('ProverAgentApiSchema', () => { expect([...tested].sort()).toEqual(all.sort()); }); - it('setMaxConcurrency', async () => { - await context.client.setMaxConcurrency(1); - }); - - it('isRunning', async () => { - const running = await context.client.isRunning(); - expect(running).toBe(true); - }); - - it('getCurrentJobs', async () => { - const jobs = await context.client.getCurrentJobs(); - expect(jobs).toEqual([ - { id: '1', type: 'type1' }, - { id: '2', type: 'type2' }, - ]); + it.each([ + { + status: 'stopped', + }, + { + status: 'running', + }, + { + status: 'proving', + jobId: makeProvingJobId(42, ProvingRequestType.PUBLIC_BASE_ROLLUP, randomBytes(64).toString('hex')), + proofType: ProvingRequestType.PUBLIC_BASE_ROLLUP, + startedAtISO: new Date().toISOString(), + }, + ])('getStatus', async expectedStatus => { + handler.setStatus(expectedStatus); + const status = await context.client.getStatus(); + expect(status).toEqual(expectedStatus); }); }); class MockProverAgent implements ProverAgentApi { - setMaxConcurrency(_maxConcurrency: number): Promise { - return Promise.resolve(); - } - isRunning(): Promise { - return Promise.resolve(true); + private status: ProverAgentStatus = { status: 'stopped' }; + setStatus(status: ProverAgentStatus) { + this.status = status; } - getCurrentJobs(): Promise<{ id: string; type: string }[]> { - return Promise.resolve([ - { id: '1', type: 'type1' }, - { id: '2', type: 'type2' }, - ]); + getStatus() { + return Promise.resolve(this.status); } } diff --git a/yarn-project/stdlib/src/interfaces/prover-agent.ts b/yarn-project/stdlib/src/interfaces/prover-agent.ts index 1a8c06474778..78654da2a23c 100644 --- a/yarn-project/stdlib/src/interfaces/prover-agent.ts +++ b/yarn-project/stdlib/src/interfaces/prover-agent.ts @@ -2,19 +2,18 @@ import { z } from 'zod'; import type { ApiSchemaFor } from '../schemas/index.js'; -export interface ProverAgentApi { - setMaxConcurrency(maxConcurrency: number): Promise; +export const ProverAgentStatusSchema = z.discriminatedUnion('status', [ + z.object({ status: z.literal('stopped') }), + z.object({ status: z.literal('running') }), + z.object({ status: z.literal('proving'), jobId: z.string(), proofType: z.number(), startedAtISO: z.string() }), +]); - isRunning(): Promise; +export type ProverAgentStatus = z.infer; - getCurrentJobs(): Promise<{ id: string; type: string }[]>; +export interface ProverAgentApi { + getStatus(): Promise; } export const ProverAgentApiSchema: ApiSchemaFor = { - setMaxConcurrency: z.function().args(z.number().min(1).int()).returns(z.void()), - isRunning: z.function().args().returns(z.boolean()), - getCurrentJobs: z - .function() - .args() - .returns(z.array(z.object({ id: z.string(), type: z.string() }))), + getStatus: z.function().args().returns(ProverAgentStatusSchema), };