From f5d4941a780a35bdfb9172eecd5e49eb9cb375f6 Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Thu, 28 Nov 2024 18:45:09 -0300 Subject: [PATCH] Verify block built against synced world state --- .../src/interfaces/world_state.ts | 13 ++++------- .../prover-client/src/mocks/test_context.ts | 23 ++++++++++++++++--- .../src/orchestrator/orchestrator.ts | 19 +++++++++++++-- .../orchestrator_multiple_blocks.test.ts | 1 + .../src/prover-client/factory.ts | 4 ++-- .../src/prover-client/prover-client.ts | 6 ++--- .../prover-node/src/job/epoch-proving-job.ts | 4 ++-- .../src/world-state-db/merkle_tree_db.ts | 10 ++------ 8 files changed, 52 insertions(+), 28 deletions(-) diff --git a/yarn-project/circuit-types/src/interfaces/world_state.ts b/yarn-project/circuit-types/src/interfaces/world_state.ts index 1cb5c4af9cbb..4fd93acf259c 100644 --- a/yarn-project/circuit-types/src/interfaces/world_state.ts +++ b/yarn-project/circuit-types/src/interfaces/world_state.ts @@ -26,13 +26,16 @@ export interface WorldStateSynchronizerStatus { } /** Provides writeable forks of the world state at a given block number. */ -export interface ForkMerkleTreeWriteOperations { +export interface ForkMerkleTreeOperations { /** Forks the world state at the given block number, defaulting to the latest one. */ fork(block?: number): Promise; + + /** Gets a handle that allows reading the state as it was at the given block number. */ + getSnapshot(blockNumber: number): MerkleTreeReadOperations; } /** Defines the interface for a world state synchronizer. */ -export interface WorldStateSynchronizer extends ForkMerkleTreeWriteOperations { +export interface WorldStateSynchronizer extends ForkMerkleTreeOperations { /** * Starts the synchronizer. * @returns A promise that resolves once the initial sync is completed. @@ -61,10 +64,4 @@ export interface WorldStateSynchronizer extends ForkMerkleTreeWriteOperations { * Returns an instance of MerkleTreeAdminOperations that will not include uncommitted data. */ getCommitted(): MerkleTreeReadOperations; - - /** - * Returns a readonly instance of MerkleTreeAdminOperations where the state is as it was at the given block number - * @param block - The block number to look at - */ - getSnapshot(block: number): MerkleTreeReadOperations; } diff --git a/yarn-project/prover-client/src/mocks/test_context.ts b/yarn-project/prover-client/src/mocks/test_context.ts index e68b1f517eb5..e2df1346c11f 100644 --- a/yarn-project/prover-client/src/mocks/test_context.ts +++ b/yarn-project/prover-client/src/mocks/test_context.ts @@ -1,5 +1,6 @@ import { type BBProverConfig } from '@aztec/bb-prover'; import { + type L2Block, type ProcessedTx, type ProcessedTxHandler, type PublicExecutionRequest, @@ -8,7 +9,7 @@ import { type TxValidator, } from '@aztec/circuit-types'; import { makeBloatedProcessedTx } from '@aztec/circuit-types/test'; -import { type Gas, type GlobalVariables, Header } from '@aztec/circuits.js'; +import { type AppendOnlyTreeSnapshot, type Gas, type GlobalVariables, Header } from '@aztec/circuits.js'; import { times } from '@aztec/foundation/collection'; import { Fr } from '@aztec/foundation/fields'; import { type DebugLogger } from '@aztec/foundation/log'; @@ -49,7 +50,7 @@ export class TestContext { public globalVariables: GlobalVariables, public prover: ServerCircuitProver, public proverAgent: ProverAgent, - public orchestrator: ProvingOrchestrator, + public orchestrator: TestProvingOrchestrator, public blockNumber: number, public directoriesToCleanup: string[], public logger: DebugLogger, @@ -112,7 +113,7 @@ export class TestContext { } const queue = new MemoryProvingQueue(telemetry); - const orchestrator = new ProvingOrchestrator(ws, queue, telemetry, Fr.ZERO); + const orchestrator = new TestProvingOrchestrator(ws, queue, telemetry, Fr.ZERO); const agent = new ProverAgent(localProver, proverCount); queue.start(); @@ -256,3 +257,19 @@ export class TestContext { return await this.publicProcessor.process(txs, maxTransactions, txHandler, txValidator); } } + +class TestProvingOrchestrator extends ProvingOrchestrator { + public isVerifyBuiltBlockAgainstSyncedStateEnabled = false; + + // Disable this check by default, since it requires seeding world state with the block being built + // This is only enabled in some tests with multiple blocks that populate the pending chain via makePendingBlock + protected override verifyBuiltBlockAgainstSyncedState( + l2Block: L2Block, + newArchive: AppendOnlyTreeSnapshot, + ): Promise { + if (this.isVerifyBuiltBlockAgainstSyncedStateEnabled) { + return super.verifyBuiltBlockAgainstSyncedState(l2Block, newArchive); + } + return Promise.resolve(); + } +} diff --git a/yarn-project/prover-client/src/orchestrator/orchestrator.ts b/yarn-project/prover-client/src/orchestrator/orchestrator.ts index fe5242b722e1..3cecf9ad99eb 100644 --- a/yarn-project/prover-client/src/orchestrator/orchestrator.ts +++ b/yarn-project/prover-client/src/orchestrator/orchestrator.ts @@ -7,7 +7,7 @@ import { } from '@aztec/circuit-types'; import { type EpochProver, - type ForkMerkleTreeWriteOperations, + type ForkMerkleTreeOperations, type MerkleTreeWriteOperations, type ProofAndVerificationKey, } from '@aztec/circuit-types/interfaces'; @@ -15,6 +15,7 @@ import { type CircuitName } from '@aztec/circuit-types/stats'; import { AVM_PROOF_LENGTH_IN_FIELDS, AVM_VERIFICATION_KEY_LENGTH_IN_FIELDS, + type AppendOnlyTreeSnapshot, type BaseOrMergeRollupPublicInputs, BaseParityInputs, type BaseRollupHints, @@ -102,7 +103,7 @@ export class ProvingOrchestrator implements EpochProver { private dbs: Map = new Map(); constructor( - private dbProvider: ForkMerkleTreeWriteOperations, + private dbProvider: ForkMerkleTreeOperations, private prover: ServerCircuitProver, telemetryClient: TelemetryClient, private readonly proverId: Fr = Fr.ZERO, @@ -439,10 +440,24 @@ export class ProvingOrchestrator implements EpochProver { ); } + await this.verifyBuiltBlockAgainstSyncedState(l2Block, newArchive); + logger.verbose(`Orchestrator finalised block ${l2Block.number}`); provingState.block = l2Block; } + // Flagged as protected to disable in certain unit tests + protected async verifyBuiltBlockAgainstSyncedState(l2Block: L2Block, newArchive: AppendOnlyTreeSnapshot) { + const syncedArchive = await getTreeSnapshot(MerkleTreeId.ARCHIVE, this.dbProvider.getSnapshot(l2Block.number)); + if (!syncedArchive.equals(newArchive)) { + throw new Error( + `Archive tree mismatch for block ${l2Block.number}: world state synced to ${inspect( + syncedArchive, + )} but built ${inspect(newArchive)}`, + ); + } + } + // Enqueues the proving of the required padding transactions // If the fully proven padding transaction is not available, this will first be proven private enqueuePaddingTxs( diff --git a/yarn-project/prover-client/src/orchestrator/orchestrator_multiple_blocks.test.ts b/yarn-project/prover-client/src/orchestrator/orchestrator_multiple_blocks.test.ts index 47be03cac99d..baa3ad189abd 100644 --- a/yarn-project/prover-client/src/orchestrator/orchestrator_multiple_blocks.test.ts +++ b/yarn-project/prover-client/src/orchestrator/orchestrator_multiple_blocks.test.ts @@ -10,6 +10,7 @@ describe('prover/orchestrator/multi-block', () => { beforeEach(async () => { context = await TestContext.new(logger); + context.orchestrator.isVerifyBuiltBlockAgainstSyncedStateEnabled = true; }); afterEach(async () => { diff --git a/yarn-project/prover-client/src/prover-client/factory.ts b/yarn-project/prover-client/src/prover-client/factory.ts index d1458606116b..45e10ed630bf 100644 --- a/yarn-project/prover-client/src/prover-client/factory.ts +++ b/yarn-project/prover-client/src/prover-client/factory.ts @@ -1,4 +1,4 @@ -import { type ForkMerkleTreeWriteOperations, type ProvingJobBroker } from '@aztec/circuit-types'; +import { type ForkMerkleTreeOperations, type ProvingJobBroker } from '@aztec/circuit-types'; import { type TelemetryClient } from '@aztec/telemetry-client'; import { NoopTelemetryClient } from '@aztec/telemetry-client/noop'; @@ -7,7 +7,7 @@ import { ProverClient } from './prover-client.js'; export function createProverClient( config: ProverClientConfig, - worldState: ForkMerkleTreeWriteOperations, + worldState: ForkMerkleTreeOperations, broker: ProvingJobBroker, telemetry: TelemetryClient = new NoopTelemetryClient(), ) { diff --git a/yarn-project/prover-client/src/prover-client/prover-client.ts b/yarn-project/prover-client/src/prover-client/prover-client.ts index db036855a0c9..3cc5b9aa32b0 100644 --- a/yarn-project/prover-client/src/prover-client/prover-client.ts +++ b/yarn-project/prover-client/src/prover-client/prover-client.ts @@ -3,7 +3,7 @@ import { type ActualProverConfig, type EpochProver, type EpochProverManager, - type ForkMerkleTreeWriteOperations, + type ForkMerkleTreeOperations, type ProverCache, type ProvingJobBroker, type ProvingJobConsumer, @@ -34,7 +34,7 @@ export class ProverClient implements EpochProverManager { private constructor( private config: ProverClientConfig, - private worldState: ForkMerkleTreeWriteOperations, + private worldState: ForkMerkleTreeOperations, private telemetry: TelemetryClient, private orchestratorClient: ProvingJobProducer, private agentClient?: ProvingJobConsumer, @@ -107,7 +107,7 @@ export class ProverClient implements EpochProverManager { */ public static async new( config: ProverClientConfig, - worldState: ForkMerkleTreeWriteOperations, + worldState: ForkMerkleTreeOperations, broker: ProvingJobBroker, telemetry: TelemetryClient, ) { diff --git a/yarn-project/prover-node/src/job/epoch-proving-job.ts b/yarn-project/prover-node/src/job/epoch-proving-job.ts index 64c0a5a74207..56deb373a960 100644 --- a/yarn-project/prover-node/src/job/epoch-proving-job.ts +++ b/yarn-project/prover-node/src/job/epoch-proving-job.ts @@ -2,7 +2,7 @@ import { EmptyTxValidator, type EpochProver, type EpochProvingJobState, - type ForkMerkleTreeWriteOperations, + type ForkMerkleTreeOperations, type L1ToL2MessageSource, type L2Block, type L2BlockSource, @@ -35,7 +35,7 @@ export class EpochProvingJob { private runPromise: Promise | undefined; constructor( - private dbProvider: ForkMerkleTreeWriteOperations, + private dbProvider: ForkMerkleTreeOperations, private epochNumber: bigint, private blocks: L2Block[], private prover: EpochProver, diff --git a/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts b/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts index d5484464650e..6410c8bc16d1 100644 --- a/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts +++ b/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts @@ -1,5 +1,5 @@ import { type L2Block, type MerkleTreeId } from '@aztec/circuit-types'; -import { type ForkMerkleTreeWriteOperations, type MerkleTreeReadOperations } from '@aztec/circuit-types/interfaces'; +import { type ForkMerkleTreeOperations, type MerkleTreeReadOperations } from '@aztec/circuit-types/interfaces'; import { type Fr, MAX_NULLIFIERS_PER_TX, MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX } from '@aztec/circuits.js'; import { type IndexedTreeSnapshot, type TreeSnapshot } from '@aztec/merkle-tree'; @@ -32,7 +32,7 @@ export type TreeSnapshots = { [MerkleTreeId.ARCHIVE]: TreeSnapshot; }; -export interface MerkleTreeAdminDatabase extends ForkMerkleTreeWriteOperations { +export interface MerkleTreeAdminDatabase extends ForkMerkleTreeOperations { /** * Handles a single L2 block (i.e. Inserts the new note hashes into the merkle tree). * @param block - The L2 block to handle. @@ -45,12 +45,6 @@ export interface MerkleTreeAdminDatabase extends ForkMerkleTreeWriteOperations { */ getCommitted(): MerkleTreeReadOperations; - /** - * Gets a handle that allows reading the state as it was at the given block number - * @param blockNumber - The block number to get the snapshot for - */ - getSnapshot(blockNumber: number): MerkleTreeReadOperations; - /** * Removes all historical snapshots up to but not including the given block number * @param toBlockNumber The block number of the new oldest historical block