Skip to content

Commit

Permalink
fix: Prover node aborts execution at epoch end (#11111)
Browse files Browse the repository at this point in the history
Prover node aborts all jobs once it hits the end of the next epoch to be
proven.

Fixes #10802
  • Loading branch information
spalladino authored Jan 9, 2025
1 parent 4600f54 commit 2a77616
Show file tree
Hide file tree
Showing 29 changed files with 454 additions and 103 deletions.
4 changes: 2 additions & 2 deletions yarn-project/archiver/src/archiver/archiver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -511,8 +511,8 @@ export class Archiver implements ArchiveSource, Traceable {
return Promise.resolve();
}

public getL1Constants(): L1RollupConstants {
return this.l1constants;
public getL1Constants(): Promise<L1RollupConstants> {
return Promise.resolve(this.l1constants);
}

public getRollupAddress(): Promise<EthAddress> {
Expand Down
7 changes: 6 additions & 1 deletion yarn-project/archiver/src/test/mock_l2_block_source.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import {
type L1RollupConstants,
L2Block,
L2BlockHash,
type L2BlockSource,
type L2Tips,
type TxHash,
TxReceipt,
TxStatus,
getSlotRangeForEpoch,
} from '@aztec/circuit-types';
import { getSlotRangeForEpoch } from '@aztec/circuit-types';
import { type BlockHeader, EthAddress } from '@aztec/circuits.js';
import { DefaultL1ContractsConfig } from '@aztec/ethereum';
import { createLogger } from '@aztec/foundation/log';
Expand Down Expand Up @@ -187,6 +188,10 @@ export class MockL2BlockSource implements L2BlockSource {
throw new Error('Method not implemented.');
}

getL1Constants(): Promise<L1RollupConstants> {
throw new Error('Method not implemented.');
}

/**
* Starts the block source. In this mock implementation, this is a noop.
* @returns A promise that signals the initialization of the l2 block source on completion.
Expand Down
1 change: 0 additions & 1 deletion yarn-project/aztec.js/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,6 @@ export {
mockEpochProofQuote,
mockTx,
type AztecNode,
type EpochConstants,
type LogFilter,
type PXE,
type PartialAddress,
Expand Down
5 changes: 2 additions & 3 deletions yarn-project/circuit-types/src/epoch-helpers/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { type EpochConstants, getTimestampRangeForEpoch } from './index.js';
import { type L1RollupConstants, getTimestampRangeForEpoch } from './index.js';

describe('EpochHelpers', () => {
let constants: EpochConstants;
let constants: Omit<L1RollupConstants, 'l1StartBlock'>;
const l1GenesisTime = 1734440000n;

beforeEach(() => {
constants = {
l1GenesisBlock: 10n,
l1GenesisTime: l1GenesisTime,
epochDuration: 4,
slotDuration: 24,
Expand Down
42 changes: 15 additions & 27 deletions yarn-project/circuit-types/src/epoch-helpers/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import { type ZodFor, schemas } from '@aztec/foundation/schemas';

import { z } from 'zod';

export type L1RollupConstants = {
l1StartBlock: bigint;
l1GenesisTime: bigint;
Expand All @@ -14,30 +18,29 @@ export const EmptyL1RollupConstants: L1RollupConstants = {
ethereumSlotDuration: 1,
};

// REFACTOR: Merge this type with L1RollupConstants
export type EpochConstants = {
l1GenesisBlock: bigint;
l1GenesisTime: bigint;
epochDuration: number;
slotDuration: number;
ethereumSlotDuration: number;
};
export const L1RollupConstantsSchema = z.object({
l1StartBlock: schemas.BigInt,
l1GenesisTime: schemas.BigInt,
slotDuration: z.number(),
epochDuration: z.number(),
ethereumSlotDuration: z.number(),
}) satisfies ZodFor<L1RollupConstants>;

/** Returns the slot number for a given timestamp. */
export function getSlotAtTimestamp(ts: bigint, constants: Pick<EpochConstants, 'l1GenesisTime' | 'slotDuration'>) {
export function getSlotAtTimestamp(ts: bigint, constants: Pick<L1RollupConstants, 'l1GenesisTime' | 'slotDuration'>) {
return ts < constants.l1GenesisTime ? 0n : (ts - constants.l1GenesisTime) / BigInt(constants.slotDuration);
}

/** Returns the epoch number for a given timestamp. */
export function getEpochNumberAtTimestamp(
ts: bigint,
constants: Pick<EpochConstants, 'epochDuration' | 'slotDuration' | 'l1GenesisTime'>,
constants: Pick<L1RollupConstants, 'epochDuration' | 'slotDuration' | 'l1GenesisTime'>,
) {
return getSlotAtTimestamp(ts, constants) / BigInt(constants.epochDuration);
}

/** Returns the range of L2 slots (inclusive) for a given epoch number. */
export function getSlotRangeForEpoch(epochNumber: bigint, constants: Pick<EpochConstants, 'epochDuration'>) {
export function getSlotRangeForEpoch(epochNumber: bigint, constants: Pick<L1RollupConstants, 'epochDuration'>) {
const startSlot = epochNumber * BigInt(constants.epochDuration);
return [startSlot, startSlot + BigInt(constants.epochDuration) - 1n];
}
Expand All @@ -48,7 +51,7 @@ export function getSlotRangeForEpoch(epochNumber: bigint, constants: Pick<EpochC
*/
export function getTimestampRangeForEpoch(
epochNumber: bigint,
constants: Pick<EpochConstants, 'l1GenesisTime' | 'slotDuration' | 'epochDuration' | 'ethereumSlotDuration'>,
constants: Pick<L1RollupConstants, 'l1GenesisTime' | 'slotDuration' | 'epochDuration' | 'ethereumSlotDuration'>,
) {
const [startSlot, endSlot] = getSlotRangeForEpoch(epochNumber, constants);
const ethereumSlotsPerL2Slot = constants.slotDuration / constants.ethereumSlotDuration;
Expand All @@ -59,18 +62,3 @@ export function getTimestampRangeForEpoch(
BigInt((ethereumSlotsPerL2Slot - 1) * constants.ethereumSlotDuration),
];
}

/**
* Returns the range of L1 blocks (inclusive) for a given epoch number.
* @remarks This assumes no time warp has happened.
*/
export function getL1BlockRangeForEpoch(
epochNumber: bigint,
constants: Pick<EpochConstants, 'l1GenesisBlock' | 'epochDuration' | 'slotDuration'>,
) {
const epochDurationInL1Blocks = BigInt(constants.epochDuration) * BigInt(constants.slotDuration);
return [
epochNumber * epochDurationInL1Blocks + constants.l1GenesisBlock,
(epochNumber + 1n) * epochDurationInL1Blocks + constants.l1GenesisBlock - 1n,
];
}
9 changes: 9 additions & 0 deletions yarn-project/circuit-types/src/interfaces/archiver.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { readFileSync } from 'fs';
import omit from 'lodash.omit';
import { resolve } from 'path';

import { EmptyL1RollupConstants, type L1RollupConstants } from '../epoch-helpers/index.js';
import { type InBlock, randomInBlock } from '../in_block.js';
import { L2Block } from '../l2_block.js';
import { type L2Tips } from '../l2_block_source.js';
Expand Down Expand Up @@ -248,6 +249,11 @@ describe('ArchiverApiSchema', () => {
privateFunctions: [],
});
});

it('getL1Constants', async () => {
const result = await context.client.getL1Constants();
expect(result).toEqual(EmptyL1RollupConstants);
});
});

class MockArchiver implements ArchiverApi {
Expand Down Expand Up @@ -388,4 +394,7 @@ class MockArchiver implements ArchiverApi {
addContractClass(_contractClass: ContractClassPublic): Promise<void> {
return Promise.resolve();
}
getL1Constants(): Promise<L1RollupConstants> {
return Promise.resolve(EmptyL1RollupConstants);
}
}
2 changes: 2 additions & 0 deletions yarn-project/circuit-types/src/interfaces/archiver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { type ApiSchemaFor, optional, schemas } from '@aztec/foundation/schemas'

import { z } from 'zod';

import { L1RollupConstantsSchema } from '../epoch-helpers/index.js';
import { inBlockSchemaFor } from '../in_block.js';
import { L2Block } from '../l2_block.js';
import { type L2BlockSource, L2TipsSchema } from '../l2_block_source.js';
Expand Down Expand Up @@ -77,4 +78,5 @@ export const ArchiverApiSchema: ApiSchemaFor<ArchiverApi> = {
.function()
.args(schemas.AztecAddress, schemas.FunctionSelector)
.returns(optional(z.string())),
getL1Constants: z.function().args().returns(L1RollupConstantsSchema),
};
11 changes: 11 additions & 0 deletions yarn-project/circuit-types/src/interfaces/prover-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,21 @@ const EpochProvingJobState = [
'publishing-proof',
'completed',
'failed',
'stopped',
'timed-out',
] as const;

export type EpochProvingJobState = (typeof EpochProvingJobState)[number];

export const EpochProvingJobTerminalState: EpochProvingJobState[] = [
'completed',
'failed',
'stopped',
'timed-out',
] as const;

export type EpochProvingJobTerminalState = (typeof EpochProvingJobTerminalState)[number];

/** JSON RPC public interface to a prover node. */
export interface ProverNodeApi {
getJobs(): Promise<{ uuid: string; status: EpochProvingJobState }[]>;
Expand Down
6 changes: 6 additions & 0 deletions yarn-project/circuit-types/src/l2_block_source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { type BlockHeader, type EthAddress } from '@aztec/circuits.js';

import { z } from 'zod';

import { type L1RollupConstants } from './epoch-helpers/index.js';
import { type InBlock } from './in_block.js';
import { type L2Block } from './l2_block.js';
import { type TxHash } from './tx/tx_hash.js';
Expand Down Expand Up @@ -106,6 +107,11 @@ export interface L2BlockSource {
* Returns the tips of the L2 chain.
*/
getL2Tips(): Promise<L2Tips>;

/**
* Returns the rollup constants for the current chain.
*/
getL1Constants(): Promise<L1RollupConstants>;
}

/**
Expand Down
5 changes: 5 additions & 0 deletions yarn-project/circuits.js/src/structs/proof.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,11 @@ export class Proof {
this.buffer.length === EMPTY_PROOF_SIZE && this.buffer.every(byte => byte === 0) && this.numPublicInputs === 0
);
}

/** Returns an empty proof. */
static empty() {
return makeEmptyProof();
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,4 +168,8 @@ export class FeeRecipient {
}
return { recipient: this.recipient.toString(), value: this.value.toString() };
}

static random() {
return new FeeRecipient(EthAddress.random(), Fr.random());
}
}
19 changes: 19 additions & 0 deletions yarn-project/circuits.js/src/structs/rollup/root_rollup.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { makeTuple } from '@aztec/foundation/array';
import { Fr } from '@aztec/foundation/fields';
import { bufferSchemaFor } from '@aztec/foundation/schemas';
import { BufferReader, type Tuple, serializeToBuffer, serializeToFields } from '@aztec/foundation/serialize';
Expand Down Expand Up @@ -184,4 +185,22 @@ export class RootRollupPublicInputs {
static get schema() {
return bufferSchemaFor(RootRollupPublicInputs);
}

/** Creates a random instance. */
static random() {
return new RootRollupPublicInputs(
AppendOnlyTreeSnapshot.random(),
AppendOnlyTreeSnapshot.random(),
Fr.random(),
Fr.random(),
Fr.random(),
Fr.random(),
Fr.random(),
makeTuple(AZTEC_MAX_EPOCH_DURATION, FeeRecipient.random),
Fr.random(),
Fr.random(),
Fr.random(),
makeTuple(AZTEC_MAX_EPOCH_DURATION, BlockBlobPublicInputs.empty),
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,8 @@ export class AppendOnlyTreeSnapshot {
public equals(other: this) {
return this.root.equals(other.root) && this.nextAvailableLeafIndex === other.nextAvailableLeafIndex;
}

static random() {
return new AppendOnlyTreeSnapshot(Fr.random(), Math.floor(Math.random() * 1000));
}
}
15 changes: 4 additions & 11 deletions yarn-project/end-to-end/src/e2e_block_building.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ import { type TestDateProvider } from '@aztec/foundation/timer';
import { StatefulTestContract, StatefulTestContractArtifact } from '@aztec/noir-contracts.js/StatefulTest';
import { TestContract } from '@aztec/noir-contracts.js/Test';
import { TokenContract } from '@aztec/noir-contracts.js/Token';
import { type Sequencer, type SequencerClient, SequencerState } from '@aztec/sequencer-client';
import { type SequencerClient, SequencerState } from '@aztec/sequencer-client';
import { type TestSequencerClient } from '@aztec/sequencer-client/test';
import { PublicProcessorFactory, type PublicTxResult, PublicTxSimulator, type WorldStateDB } from '@aztec/simulator';
import { type TelemetryClient } from '@aztec/telemetry-client';
import { NoopTelemetryClient } from '@aztec/telemetry-client/noop';
Expand Down Expand Up @@ -59,7 +60,7 @@ describe('e2e_block_building', () => {
const artifact = StatefulTestContractArtifact;

beforeAll(async () => {
let sequencerClient;
let sequencerClient: SequencerClient | undefined;
({
teardown,
pxe,
Expand All @@ -70,8 +71,7 @@ describe('e2e_block_building', () => {
dateProvider,
cheatCodes,
} = await setup(2));
// Bypass accessibility modifiers in sequencer
sequencer = sequencerClient! as unknown as TestSequencerClient;
sequencer = sequencerClient! as TestSequencerClient;
});

afterEach(() => aztecNode.setConfig({ minTxsPerBlock: 1 }));
Expand Down Expand Up @@ -610,13 +610,6 @@ async function sendAndWait(calls: ContractFunctionInteraction[]) {
);
}

type TestSequencer = Omit<Sequencer, 'publicProcessorFactory' | 'timeTable'> & {
publicProcessorFactory: PublicProcessorFactory;
timeTable: Record<SequencerState, number>;
processTxTime: number;
};
type TestSequencerClient = Omit<SequencerClient, 'sequencer'> & { sequencer: TestSequencer };

const TEST_PUBLIC_TX_SIMULATION_DELAY_MS = 300;

class TestPublicTxSimulator extends PublicTxSimulator {
Expand Down
Loading

0 comments on commit 2a77616

Please sign in to comment.