Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion spartan/aztec-node/templates/secret.yaml
Original file line number Diff line number Diff line change
@@ -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:
Expand All @@ -11,3 +12,4 @@ data:
{{- else }}
privateKeys: {{ join "\n" .Values.node.l1Publisher.privateKeys | b64enc }}
{{- end }}
{{- end }}
23 changes: 18 additions & 5 deletions spartan/aztec-node/templates/statefulset.yaml
Original file line number Diff line number Diff line change
@@ -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:
Expand All @@ -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:
Expand All @@ -34,6 +35,7 @@ spec:
- node
topologyKey: kubernetes.io/hostname
namespaceSelector: {}
{{- end }}
initContainers:
{{- if .Values.initContainers }}
{{- tpl (toYaml .Values.initContainers | nindent 8) $ }}
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -119,6 +121,11 @@ spec:
{{- end }}

start_cmd+=({{ join " " .Values.node.startCmd }})

{{- if .Values.node.preStartScript }}
{{ .Values.node.preStartScript | nindent 14 }}

{{- end }}
Comment on lines +125 to +128
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My idea here is to use this script to run a DNS to discover validator pods and set the PROVER_COORDINATION_URLS

"${start_cmd[@]}"
startupProbe:
httpGet:
Expand Down Expand Up @@ -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 }}
Expand Down Expand Up @@ -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: {}
Expand Down
6 changes: 6 additions & 0 deletions spartan/aztec-node/templates/svc.headless.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{{- if .Values.service.headless.enabled }}
apiVersion: v1
kind: Service
metadata:
Expand All @@ -7,6 +8,7 @@ metadata:
spec:
clusterIP: None
ports:
{{- if .Values.service.p2p.enabled }}
- port: {{ .Values.service.p2p.port }}
targetPort: p2p-tcp
protocol: TCP
Expand All @@ -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 }}
2 changes: 1 addition & 1 deletion spartan/aztec-node/templates/svc.nodeport.yaml
Original file line number Diff line number Diff line change
@@ -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
Expand Down
6 changes: 5 additions & 1 deletion spartan/aztec-node/templates/svc.yaml
Original file line number Diff line number Diff line change
@@ -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 }}
Expand All @@ -11,6 +11,7 @@ metadata:
spec:
type: ClusterIP
ports:
{{- if .Values.service.p2p.enabled }}
- port: {{ .Values.service.p2p.port }}
targetPort: p2p-tcp
protocol: TCP
Expand All @@ -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 }}
5 changes: 5 additions & 0 deletions spartan/aztec-node/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ node:
## Example: "X-API-KEY"
l1ConsensusHostApiKeyHeaders: []

preStartScript: ""

startCmd:
- --node
- --archiver
Expand Down Expand Up @@ -138,6 +140,9 @@ service:
hosts: []
# - node.example.com

headless:
enabled: true

p2p:
enabled: true
nodePortEnabled: true
Expand Down
13 changes: 13 additions & 0 deletions yarn-project/aztec/src/cli/cmds/start_prover_agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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 <value>`);
process.exit(1);
}

Expand All @@ -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'];
}
Comment on lines +73 to +75
Copy link
Contributor Author

@alexghr alexghr May 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to have a service exposed through the services object in order to get an http server running. Without an http server running on the RPC port the agent pod would fail health checks

The RPC API for an agent contains just a single getStatus() method.


await Promise.all(agents.map(agent => agent.start()));

signalHandlers.push(async () => {
Expand Down
14 changes: 14 additions & 0 deletions yarn-project/prover-client/src/proving_broker/proving_agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { truncate } from '@aztec/foundation/string';
import { ProvingError } from '@aztec/stdlib/errors';
import type {
GetProvingJobResponse,
ProverAgentStatus,
ProvingJobConsumer,
ProvingJobId,
ProvingJobInputs,
Expand Down Expand Up @@ -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:
Expand Down
52 changes: 26 additions & 26 deletions yarn-project/stdlib/src/interfaces/prover-agent.test.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<ProverAgentStatus>([
{
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<void> {
return Promise.resolve();
}
isRunning(): Promise<boolean> {
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);
}
}
19 changes: 9 additions & 10 deletions yarn-project/stdlib/src/interfaces/prover-agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,18 @@ import { z } from 'zod';

import type { ApiSchemaFor } from '../schemas/index.js';

export interface ProverAgentApi {
setMaxConcurrency(maxConcurrency: number): Promise<void>;
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<boolean>;
export type ProverAgentStatus = z.infer<typeof ProverAgentStatusSchema>;

getCurrentJobs(): Promise<{ id: string; type: string }[]>;
export interface ProverAgentApi {
getStatus(): Promise<unknown>;
}

export const ProverAgentApiSchema: ApiSchemaFor<ProverAgentApi> = {
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),
};