diff --git a/l1-contracts/src/core/Rollup.sol b/l1-contracts/src/core/Rollup.sol index d2a15d03b8c..d6e7cd65559 100644 --- a/l1-contracts/src/core/Rollup.sol +++ b/l1-contracts/src/core/Rollup.sol @@ -127,13 +127,14 @@ contract Rollup is Leonidas, IRollup { function submitProof( bytes calldata _header, bytes32 _archive, + bytes32 _proverId, bytes calldata _aggregationObject, bytes calldata _proof ) external override(IRollup) { HeaderLib.Header memory header = HeaderLib.decode(_header); bytes32[] memory publicInputs = - new bytes32[](3 + Constants.HEADER_LENGTH + Constants.AGGREGATION_OBJECT_LENGTH); + new bytes32[](4 + Constants.HEADER_LENGTH + Constants.AGGREGATION_OBJECT_LENGTH); // the archive tree root publicInputs[0] = _archive; // this is the _next_ available leaf in the archive tree @@ -148,6 +149,8 @@ contract Rollup is Leonidas, IRollup { publicInputs[i + 3] = headerFields[i]; } + publicInputs[headerFields.length + 3] = _proverId; + // the block proof is recursive, which means it comes with an aggregation object // this snippet copies it into the public inputs needed for verification // it also guards against empty _aggregationObject used with mocked proofs @@ -157,14 +160,14 @@ contract Rollup is Leonidas, IRollup { assembly { part := calldataload(add(_aggregationObject.offset, mul(i, 32))) } - publicInputs[i + 3 + Constants.HEADER_LENGTH] = part; + publicInputs[i + 4 + Constants.HEADER_LENGTH] = part; } if (!verifier.verify(_proof, publicInputs)) { revert Errors.Rollup__InvalidProof(); } - emit L2ProofVerified(header.globalVariables.blockNumber); + emit L2ProofVerified(header.globalVariables.blockNumber, _proverId); } function _computePublicInputHash(bytes calldata _header, bytes32 _archive) diff --git a/l1-contracts/src/core/interfaces/IRollup.sol b/l1-contracts/src/core/interfaces/IRollup.sol index 006d5ccb2e4..ff66b2efd6b 100644 --- a/l1-contracts/src/core/interfaces/IRollup.sol +++ b/l1-contracts/src/core/interfaces/IRollup.sol @@ -4,13 +4,14 @@ pragma solidity >=0.8.18; interface IRollup { event L2BlockProcessed(uint256 indexed blockNumber); - event L2ProofVerified(uint256 indexed blockNumber); + event L2ProofVerified(uint256 indexed blockNumber, bytes32 indexed proverId); function process(bytes calldata _header, bytes32 _archive) external; function submitProof( bytes calldata _header, bytes32 _archive, + bytes32 _proverId, bytes calldata _aggregationObject, bytes calldata _proof ) external; diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/root/root_rollup_inputs.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/root/root_rollup_inputs.nr index 863a8074f9d..2331a093195 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/root/root_rollup_inputs.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/root/root_rollup_inputs.nr @@ -34,6 +34,8 @@ struct RootRollupInputs { // inputs required to add the block hash start_archive_snapshot : AppendOnlyTreeSnapshot, new_archive_sibling_path : [Field; ARCHIVE_HEIGHT], + + prover_id: Field, } impl RootRollupInputs { @@ -106,7 +108,7 @@ impl RootRollupInputs { 0 ); - RootRollupPublicInputs { archive, header, vk_tree_root } + RootRollupPublicInputs { archive, header, vk_tree_root, prover_id: self.prover_id } } } @@ -120,6 +122,7 @@ impl Empty for RootRollupInputs { start_l1_to_l2_message_tree_snapshot : AppendOnlyTreeSnapshot::zero(), start_archive_snapshot : AppendOnlyTreeSnapshot::zero(), new_archive_sibling_path : [0; ARCHIVE_HEIGHT], + prover_id: 0 } } } diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/root/root_rollup_public_inputs.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/root/root_rollup_public_inputs.nr index 40b352a3ff6..9a49f2c8d50 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/root/root_rollup_public_inputs.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/root/root_rollup_public_inputs.nr @@ -9,4 +9,7 @@ struct RootRollupPublicInputs { // New block header header: Header, + + // Identifier of the prover + prover_id: Field, } diff --git a/yarn-project/archiver/src/archiver/archiver.test.ts b/yarn-project/archiver/src/archiver/archiver.test.ts index 690c52e4f25..6c1984e094d 100644 --- a/yarn-project/archiver/src/archiver/archiver.test.ts +++ b/yarn-project/archiver/src/archiver/archiver.test.ts @@ -36,10 +36,12 @@ describe('Archiver', () => { let publicClient: MockProxy>; let archiverStore: ArchiverDataStore; + let proverId: Fr; beforeEach(() => { publicClient = mock>(); archiverStore = new MemoryArchiverStore(1000); + proverId = Fr.random(); }); it('can start, sync and stop and handle l1 to l2 messages and logs', async () => { @@ -67,7 +69,7 @@ describe('Archiver', () => { messageSent: [makeMessageSentEvent(98n, 1n, 0n), makeMessageSentEvent(99n, 1n, 1n)], txPublished: [makeTxsPublishedEvent(101n, blocks[0].body.getTxsEffectsHash())], l2BlockProcessed: [makeL2BlockProcessedEvent(101n, 1n)], - proofVerified: [makeProofVerifiedEvent(102n, 1n)], + proofVerified: [makeProofVerifiedEvent(102n, 1n, proverId)], }); mockGetLogs({ @@ -271,11 +273,12 @@ function makeMessageSentEvent(l1BlockNum: bigint, l2BlockNumber: bigint, index: } as Log; } -function makeProofVerifiedEvent(l1BlockNum: bigint, l2BlockNumber: bigint) { +function makeProofVerifiedEvent(l1BlockNum: bigint, l2BlockNumber: bigint, proverId: Fr) { return { blockNumber: l1BlockNum, args: { blockNumber: l2BlockNumber, + proverId: proverId.toString(), }, } as Log; } diff --git a/yarn-project/archiver/src/archiver/archiver.ts b/yarn-project/archiver/src/archiver/archiver.ts index 36dbb075eb0..006bccdfd4d 100644 --- a/yarn-project/archiver/src/archiver/archiver.ts +++ b/yarn-project/archiver/src/archiver/archiver.ts @@ -28,7 +28,6 @@ import { type EthAddress } from '@aztec/foundation/eth-address'; import { Fr } from '@aztec/foundation/fields'; import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log'; import { RunningPromise } from '@aztec/foundation/running-promise'; -import { RollupAbi } from '@aztec/l1-artifacts'; import { ClassRegistererAddress } from '@aztec/protocol-contracts/class-registerer'; import { type TelemetryClient } from '@aztec/telemetry-client'; import { @@ -41,7 +40,7 @@ import { } from '@aztec/types/contracts'; import groupBy from 'lodash.groupby'; -import { type Chain, type HttpTransport, type PublicClient, createPublicClient, getAbiItem, http } from 'viem'; +import { type Chain, type HttpTransport, type PublicClient, createPublicClient, http } from 'viem'; import { type ArchiverDataStore } from './archiver_store.js'; import { type ArchiverConfig } from './config.js'; @@ -50,6 +49,7 @@ import { retrieveBlockBodiesFromAvailabilityOracle, retrieveBlockMetadataFromRollup, retrieveL1ToL2Messages, + retrieveL2ProofVerifiedEvents, } from './data_retrieval.js'; import { ArchiverInstrumentation } from './instrumentation.js'; @@ -307,20 +307,14 @@ export class Archiver implements ArchiveSource { } private async updateLastProvenL2Block(fromBlock: bigint, toBlock: bigint) { - const logs = await this.publicClient.getLogs({ - address: this.rollupAddress.toString(), - fromBlock, - toBlock: toBlock + 1n, // toBlock is exclusive - strict: true, - event: getAbiItem({ abi: RollupAbi, name: 'L2ProofVerified' }), - }); + const logs = await retrieveL2ProofVerifiedEvents(this.publicClient, this.rollupAddress, fromBlock, toBlock); const lastLog = logs[logs.length - 1]; if (!lastLog) { return; } - const provenBlockNumber = lastLog.args.blockNumber; + const provenBlockNumber = lastLog.l2BlockNumber; if (!provenBlockNumber) { throw new Error(`Missing argument blockNumber from L2ProofVerified event`); } diff --git a/yarn-project/archiver/src/archiver/data_retrieval.ts b/yarn-project/archiver/src/archiver/data_retrieval.ts index 46dd01160f2..e39e9daff80 100644 --- a/yarn-project/archiver/src/archiver/data_retrieval.ts +++ b/yarn-project/archiver/src/archiver/data_retrieval.ts @@ -1,8 +1,9 @@ import { type Body, type InboxLeaf } from '@aztec/circuit-types'; -import { type AppendOnlyTreeSnapshot, type Header } from '@aztec/circuits.js'; +import { type AppendOnlyTreeSnapshot, Fr, type Header } from '@aztec/circuits.js'; import { type EthAddress } from '@aztec/foundation/eth-address'; +import { RollupAbi } from '@aztec/l1-artifacts'; -import { type PublicClient } from 'viem'; +import { type PublicClient, getAbiItem } from 'viem'; import { getL2BlockProcessedLogs, @@ -143,3 +144,25 @@ export async function retrieveL1ToL2Messages( } while (blockUntilSynced && searchStartBlock <= searchEndBlock); return { lastProcessedL1BlockNumber: searchStartBlock - 1n, retrievedData: retrievedL1ToL2Messages }; } + +/** Retrieves L2ProofVerified events from the rollup contract. */ +export async function retrieveL2ProofVerifiedEvents( + publicClient: PublicClient, + rollupAddress: EthAddress, + searchStartBlock: bigint, + searchEndBlock?: bigint, +): Promise<{ l1BlockNumber: bigint; l2BlockNumber: bigint; proverId: Fr }[]> { + const logs = await publicClient.getLogs({ + address: rollupAddress.toString(), + fromBlock: searchStartBlock, + toBlock: searchEndBlock ? searchEndBlock + 1n : undefined, + strict: true, + event: getAbiItem({ abi: RollupAbi, name: 'L2ProofVerified' }), + }); + + return logs.map(log => ({ + l1BlockNumber: log.blockNumber, + l2BlockNumber: log.args.blockNumber, + proverId: Fr.fromString(log.args.proverId), + })); +} diff --git a/yarn-project/archiver/src/index.ts b/yarn-project/archiver/src/index.ts index b961866522f..e47c3ebb5c5 100644 --- a/yarn-project/archiver/src/index.ts +++ b/yarn-project/archiver/src/index.ts @@ -12,6 +12,9 @@ export * from './archiver/index.js'; export * from './rpc/index.js'; export * from './factory.js'; +// We are not storing the info from these events in the archiver for now (and we don't really need to), so we expose this query directly +export { retrieveL2ProofVerifiedEvents } from './archiver/data_retrieval.js'; + const log = createDebugLogger('aztec:archiver'); /** diff --git a/yarn-project/circuit-types/src/interfaces/block-prover.ts b/yarn-project/circuit-types/src/interfaces/block-prover.ts index e238285326d..a2afdc2f494 100644 --- a/yarn-project/circuit-types/src/interfaces/block-prover.ts +++ b/yarn-project/circuit-types/src/interfaces/block-prover.ts @@ -64,4 +64,7 @@ export interface BlockProver { * Will pad the block to it's complete size with empty transactions and prove all the way to the root rollup. */ setBlockCompleted(): Promise; + + /** Returns an identifier for the prover or zero if not set. */ + getProverId(): Fr; } diff --git a/yarn-project/circuit-types/src/interfaces/prover-client.ts b/yarn-project/circuit-types/src/interfaces/prover-client.ts index f8e390c0297..bdf92e94e2c 100644 --- a/yarn-project/circuit-types/src/interfaces/prover-client.ts +++ b/yarn-project/circuit-types/src/interfaces/prover-client.ts @@ -1,4 +1,5 @@ import { type TxHash } from '@aztec/circuit-types'; +import { type Fr } from '@aztec/circuits.js'; import { type BlockProver } from './block-prover.js'; import { type ProvingJobSource } from './proving-job.js'; @@ -21,6 +22,8 @@ export type ProverConfig = { proverJobTimeoutMs: number; /** The interval to check job health status */ proverJobPollIntervalMs: number; + /** Identifier of the prover */ + proverId?: Fr; }; /** diff --git a/yarn-project/circuits.js/src/structs/rollup/root_rollup.ts b/yarn-project/circuits.js/src/structs/rollup/root_rollup.ts index dc1e47ef5a5..df1f06970b2 100644 --- a/yarn-project/circuits.js/src/structs/rollup/root_rollup.ts +++ b/yarn-project/circuits.js/src/structs/rollup/root_rollup.ts @@ -48,6 +48,8 @@ export class RootRollupInputs { * Sibling path of the new block tree root. */ public newArchiveSiblingPath: Tuple, + /** Identifier of the prover for this root rollup. */ + public proverId: Fr, ) {} /** @@ -89,6 +91,7 @@ export class RootRollupInputs { fields.startL1ToL2MessageTreeSnapshot, fields.startArchiveSnapshot, fields.newArchiveSiblingPath, + fields.proverId, ] as const; } @@ -107,6 +110,7 @@ export class RootRollupInputs { reader.readObject(AppendOnlyTreeSnapshot), reader.readObject(AppendOnlyTreeSnapshot), reader.readArray(ARCHIVE_HEIGHT, Fr), + reader.readObject(Fr), ); } @@ -133,10 +137,12 @@ export class RootRollupPublicInputs { public vkTreeRoot: Fr, /** A header of an L2 block. */ public header: Header, + /** Identifier of the prover who generated this proof. */ + public proverId: Fr, ) {} static getFields(fields: FieldsOf) { - return [fields.archive, fields.vkTreeRoot, fields.header] as const; + return [fields.archive, fields.vkTreeRoot, fields.header, fields.proverId] as const; } toBuffer() { @@ -160,8 +166,9 @@ export class RootRollupPublicInputs { const reader = BufferReader.asReader(buffer); return new RootRollupPublicInputs( reader.readObject(AppendOnlyTreeSnapshot), - Fr.fromBuffer(reader), + reader.readObject(Fr), reader.readObject(Header), + reader.readObject(Fr), ); } diff --git a/yarn-project/circuits.js/src/tests/factories.ts b/yarn-project/circuits.js/src/tests/factories.ts index c70f31f334c..c2e0b89388b 100644 --- a/yarn-project/circuits.js/src/tests/factories.ts +++ b/yarn-project/circuits.js/src/tests/factories.ts @@ -932,6 +932,7 @@ export function makeRootRollupInputs(seed = 0, globalVariables?: GlobalVariables makeAppendOnlyTreeSnapshot(seed + 0x2200), makeAppendOnlyTreeSnapshot(seed + 0x2200), makeTuple(ARCHIVE_HEIGHT, fr, 0x2400), + fr(0x2500), ); } @@ -983,6 +984,7 @@ export function makeRootRollupPublicInputs( archive: makeAppendOnlyTreeSnapshot(seed + 0x100), header: makeHeader(seed + 0x200, blockNumber), vkTreeRoot: fr(seed + 0x300), + proverId: fr(seed + 0x400), }); } diff --git a/yarn-project/end-to-end/src/composed/integration_proof_verification.test.ts b/yarn-project/end-to-end/src/composed/integration_proof_verification.test.ts index 8dc7b8630df..641bb5faded 100644 --- a/yarn-project/end-to-end/src/composed/integration_proof_verification.test.ts +++ b/yarn-project/end-to-end/src/composed/integration_proof_verification.test.ts @@ -33,6 +33,7 @@ import { getLogger, setupL1Contracts, startAnvil } from '../fixtures/utils.js'; */ describe('proof_verification', () => { let proof: Proof; + let proverId: Fr; let block: L2Block; let aggregationObject: Fr[]; let anvil: Anvil | undefined; @@ -121,6 +122,7 @@ describe('proof_verification', () => { block = L2Block.fromString(blockResult.block); proof = Proof.fromString(blockResult.proof); + proverId = Fr.ZERO; aggregationObject = blockResult.aggregationObject.map((x: string) => Fr.fromString(x)); }); @@ -183,6 +185,7 @@ describe('proof_verification', () => { const args = [ `0x${block.header.toBuffer().toString('hex')}`, `0x${block.archive.root.toBuffer().toString('hex')}`, + `0x${proverId.toBuffer().toString('hex')}`, `0x${serializeToBuffer(aggregationObject).toString('hex')}`, `0x${proof.withoutPublicInputs().toString('hex')}`, ] as const; diff --git a/yarn-project/end-to-end/src/e2e_prover_node.test.ts b/yarn-project/end-to-end/src/e2e_prover_node.test.ts index e2e5f6941c7..3035ff969f3 100644 --- a/yarn-project/end-to-end/src/e2e_prover_node.test.ts +++ b/yarn-project/end-to-end/src/e2e_prover_node.test.ts @@ -1,5 +1,5 @@ import { getSchnorrAccount } from '@aztec/accounts/schnorr'; -import { type Archiver } from '@aztec/archiver'; +import { type Archiver, retrieveL2ProofVerifiedEvents } from '@aztec/archiver'; import { type AccountWalletWithSecretKey, type AztecAddress, @@ -29,7 +29,7 @@ import { // Tests simple block building with a sequencer that does not upload proofs to L1, // and then follows with a prover node run (with real proofs disabled, but // still simulating all circuits via a prover-client), in order to test -// the coordination between the sequencer and the prover node. +// the coordination through L1 between the sequencer and the prover node. describe('e2e_prover_node', () => { let ctx: SubsystemsContext; let wallet: AccountWalletWithSecretKey; @@ -133,15 +133,22 @@ describe('e2e_prover_node', () => { // Kick off a prover node await sleep(1000); - logger.info('Creating prover node'); + const proverId = Fr.fromString(Buffer.from('awesome-prover', 'utf-8').toString('hex')); + logger.info(`Creating prover node ${proverId.toString()}`); // HACK: We have to use the existing archiver to fetch L2 data, since anvil's chain dump/load used by the // snapshot manager does not include events nor txs, so a new archiver would not "see" old blocks. - const proverConfig = { ...ctx.aztecNodeConfig, txProviderNodeUrl: undefined, dataDirectory: undefined }; + const proverConfig = { ...ctx.aztecNodeConfig, txProviderNodeUrl: undefined, dataDirectory: undefined, proverId }; const archiver = ctx.aztecNode.getBlockSource() as Archiver; const proverNode = await createProverNode(proverConfig, { aztecNodeTxProvider: ctx.aztecNode, archiver }); // Prove the first two blocks await prove(proverNode, firstBlock); await prove(proverNode, firstBlock + 1); + + // Check that the prover id made it to the emitted event + const { publicClient, l1ContractAddresses } = ctx.deployL1ContractsValues; + const logs = await retrieveL2ProofVerifiedEvents(publicClient, l1ContractAddresses.rollupAddress, 1n); + expect(logs[0].l2BlockNumber).toEqual(BigInt(firstBlock)); + expect(logs[0].proverId.toString()).toEqual(proverId.toString()); }); }); diff --git a/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts b/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts index c5e787298f5..4cc162811da 100644 --- a/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts +++ b/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts @@ -2005,6 +2005,7 @@ export function mapRootRollupInputsToNoir(rootRollupInputs: RootRollupInputs): R ), start_archive_snapshot: mapAppendOnlyTreeSnapshotToNoir(rootRollupInputs.startArchiveSnapshot), new_archive_sibling_path: mapTuple(rootRollupInputs.newArchiveSiblingPath, mapFieldToNoir), + prover_id: mapFieldToNoir(rootRollupInputs.proverId), }; } @@ -2045,6 +2046,7 @@ export function mapRootRollupPublicInputsFromNoir( mapAppendOnlyTreeSnapshotFromNoir(rootRollupPublicInputs.archive), mapFieldFromNoir(rootRollupPublicInputs.vk_tree_root), mapHeaderFromNoir(rootRollupPublicInputs.header), + mapFieldFromNoir(rootRollupPublicInputs.prover_id), ); } diff --git a/yarn-project/prover-client/src/config.ts b/yarn-project/prover-client/src/config.ts index 383e85fcb36..bf28cf93ee4 100644 --- a/yarn-project/prover-client/src/config.ts +++ b/yarn-project/prover-client/src/config.ts @@ -1,4 +1,5 @@ import { type ProverConfig } from '@aztec/circuit-types'; +import { Fr } from '@aztec/circuits.js'; import { tmpdir } from 'os'; @@ -39,6 +40,7 @@ export function getProverEnvVars(): ProverClientConfig { PROVER_REAL_PROOFS = '', PROVER_JOB_TIMEOUT_MS = '60000', PROVER_JOB_POLL_INTERVAL_MS = '1000', + PROVER_ID, } = process.env; const realProofs = ['1', 'true'].includes(PROVER_REAL_PROOFS); @@ -48,6 +50,7 @@ export function getProverEnvVars(): ProverClientConfig { const proverJobTimeoutMs = safeParseNumber(PROVER_JOB_TIMEOUT_MS, 60000); const proverJobPollIntervalMs = safeParseNumber(PROVER_JOB_POLL_INTERVAL_MS, 1000); const disableProver = ['1', 'true'].includes(PROVER_DISABLED); + const proverId = PROVER_ID ? parseProverId(PROVER_ID) : undefined; return { acvmWorkingDirectory: ACVM_WORKING_DIRECTORY, @@ -62,9 +65,14 @@ export function getProverEnvVars(): ProverClientConfig { nodeUrl: AZTEC_NODE_URL, proverJobPollIntervalMs, proverJobTimeoutMs, + proverId, }; } +function parseProverId(str: string) { + return Fr.fromString(str.startsWith('0x') ? str : Buffer.from(str, 'utf8').toString('hex')); +} + function safeParseNumber(value: string, defaultValue: number): number { const parsedValue = parseInt(value, 10); return Number.isSafeInteger(parsedValue) ? parsedValue : defaultValue; diff --git a/yarn-project/prover-client/src/mocks/test_context.ts b/yarn-project/prover-client/src/mocks/test_context.ts index c1852eaca9d..dbe28edd870 100644 --- a/yarn-project/prover-client/src/mocks/test_context.ts +++ b/yarn-project/prover-client/src/mocks/test_context.ts @@ -53,6 +53,9 @@ class DummyProverClient implements BlockProver { setBlockCompleted(): Promise { return this.orchestrator.setBlockCompleted(); } + getProverId(): Fr { + return this.orchestrator.proverId; + } } export class TestContext { diff --git a/yarn-project/prover-client/src/orchestrator/block-building-helpers.ts b/yarn-project/prover-client/src/orchestrator/block-building-helpers.ts index c0aaba59c46..556523a5263 100644 --- a/yarn-project/prover-client/src/orchestrator/block-building-helpers.ts +++ b/yarn-project/prover-client/src/orchestrator/block-building-helpers.ts @@ -224,6 +224,7 @@ export async function getRootRollupInput( messageTreeSnapshot: AppendOnlyTreeSnapshot, messageTreeRootSiblingPath: Tuple, db: MerkleTreeOperations, + proverId: Fr, ) { const previousRollupData: RootRollupInputs['previousRollupData'] = [ getPreviousRollupDataFromPublicInputs(rollupOutputLeft, rollupProofLeft, verificationKeyLeft), @@ -254,6 +255,7 @@ export async function getRootRollupInput( startL1ToL2MessageTreeSnapshot: messageTreeSnapshot, startArchiveSnapshot, newArchiveSiblingPath, + proverId, }); } diff --git a/yarn-project/prover-client/src/orchestrator/orchestrator.ts b/yarn-project/prover-client/src/orchestrator/orchestrator.ts index 0e78d0c7a89..e3ad5b357af 100644 --- a/yarn-project/prover-client/src/orchestrator/orchestrator.ts +++ b/yarn-project/prover-client/src/orchestrator/orchestrator.ts @@ -97,7 +97,12 @@ export class ProvingOrchestrator { public readonly tracer: Tracer; - constructor(private db: MerkleTreeOperations, private prover: ServerCircuitProver, telemetryClient: TelemetryClient) { + constructor( + private db: MerkleTreeOperations, + private prover: ServerCircuitProver, + telemetryClient: TelemetryClient, + public readonly proverId: Fr = Fr.ZERO, + ) { this.tracer = telemetryClient.getTracer('ProvingOrchestrator'); } @@ -739,6 +744,7 @@ export class ProvingOrchestrator { provingState.messageTreeSnapshot, provingState.messageTreeRootSiblingPath, this.db, + this.proverId, ); this.deferredProving( diff --git a/yarn-project/prover-client/src/tx-prover/tx-prover.ts b/yarn-project/prover-client/src/tx-prover/tx-prover.ts index 013ca478187..b6b78fc9835 100644 --- a/yarn-project/prover-client/src/tx-prover/tx-prover.ts +++ b/yarn-project/prover-client/src/tx-prover/tx-prover.ts @@ -7,7 +7,7 @@ import { type ProvingTicket, type ServerCircuitProver, } from '@aztec/circuit-types/interfaces'; -import { type Fr, type GlobalVariables } from '@aztec/circuits.js'; +import { Fr, type GlobalVariables } from '@aztec/circuits.js'; import { NativeACVMSimulator } from '@aztec/simulator'; import { type TelemetryClient } from '@aztec/telemetry-client'; import { type WorldStateSynchronizer } from '@aztec/world-state'; @@ -32,7 +32,16 @@ export class TxProver implements ProverClient { private agent?: ProverAgent, ) { this.queue = new MemoryProvingQueue(config.proverJobTimeoutMs, config.proverJobPollIntervalMs); - this.orchestrator = new ProvingOrchestrator(worldStateSynchronizer.getLatest(), this.queue, telemetry); + this.orchestrator = new ProvingOrchestrator( + worldStateSynchronizer.getLatest(), + this.queue, + telemetry, + config.proverId, + ); + } + + public getProverId(): Fr { + return this.config.proverId ?? Fr.ZERO; } async updateProverConfig(config: Partial): Promise { diff --git a/yarn-project/prover-node/src/job/block-proving-job.ts b/yarn-project/prover-node/src/job/block-proving-job.ts index e0d5c76e92f..37c5989a963 100644 --- a/yarn-project/prover-node/src/job/block-proving-job.ts +++ b/yarn-project/prover-node/src/job/block-proving-job.ts @@ -92,7 +92,13 @@ export class BlockProvingJob { this.log.info(`Finalised proof for block range`, { fromBlock, toBlock }); this.state = 'publishing-proof'; - await this.publisher.submitProof(block.header, block.archive.root, aggregationObject, proof); + await this.publisher.submitProof( + block.header, + block.archive.root, + this.prover.getProverId(), + aggregationObject, + proof, + ); this.log.info(`Submitted proof for block range`, { fromBlock, toBlock }); this.state = 'completed'; diff --git a/yarn-project/sequencer-client/src/publisher/l1-publisher.ts b/yarn-project/sequencer-client/src/publisher/l1-publisher.ts index a4bc9fde02d..7ea4c48bd46 100644 --- a/yarn-project/sequencer-client/src/publisher/l1-publisher.ts +++ b/yarn-project/sequencer-client/src/publisher/l1-publisher.ts @@ -113,6 +113,8 @@ export type L1SubmitProofArgs = { header: Buffer; /** A root of the archive tree after the L2 block is applied. */ archive: Buffer; + /** Identifier of the prover. */ + proverId: Buffer; /** The proof for the block. */ proof: Buffer; /** The aggregation object for the block's proof. */ @@ -238,12 +240,19 @@ export class L1Publisher implements L2BlockReceiver { return false; } - public async submitProof(header: Header, archiveRoot: Fr, aggregationObject: Fr[], proof: Proof): Promise { + public async submitProof( + header: Header, + archiveRoot: Fr, + proverId: Fr, + aggregationObject: Fr[], + proof: Proof, + ): Promise { const ctx = { blockNumber: header.globalVariables.blockNumber }; const txArgs: L1SubmitProofArgs = { header: header.toBuffer(), archive: archiveRoot.toBuffer(), + proverId: proverId.toBuffer(), aggregationObject: serializeToBuffer(aggregationObject), proof: proof.withoutPublicInputs(), }; diff --git a/yarn-project/sequencer-client/src/publisher/viem-tx-sender.ts b/yarn-project/sequencer-client/src/publisher/viem-tx-sender.ts index a9456301037..8669c13701f 100644 --- a/yarn-project/sequencer-client/src/publisher/viem-tx-sender.ts +++ b/yarn-project/sequencer-client/src/publisher/viem-tx-sender.ts @@ -176,10 +176,11 @@ export class ViemTxSender implements L1PublisherTxSender { * @returns The hash of the mined tx. */ async sendSubmitProofTx(submitProofArgs: L1SubmitProofArgs): Promise { - const { header, archive, aggregationObject, proof } = submitProofArgs; + const { header, archive, proverId, aggregationObject, proof } = submitProofArgs; const args = [ `0x${header.toString('hex')}`, `0x${archive.toString('hex')}`, + `0x${proverId.toString('hex')}`, `0x${aggregationObject.toString('hex')}`, `0x${proof.toString('hex')}`, ] as const; diff --git a/yarn-project/sequencer-client/src/sequencer/sequencer.ts b/yarn-project/sequencer-client/src/sequencer/sequencer.ts index 3a6ccb0cf72..8fbdff27634 100644 --- a/yarn-project/sequencer-client/src/sequencer/sequencer.ts +++ b/yarn-project/sequencer-client/src/sequencer/sequencer.ts @@ -365,7 +365,13 @@ export class Sequencer { // This is temporary while we submit one proof per block, but will have to change once we // move onto proving batches of multiple blocks at a time. if (aggregationObject && proof && !this.skipSubmitProofs) { - await this.publisher.submitProof(block.header, block.archive.root, aggregationObject, proof); + await this.publisher.submitProof( + block.header, + block.archive.root, + this.prover.getProverId(), + aggregationObject, + proof, + ); this.log.info(`Submitted proof for block ${block.number}`); } }