diff --git a/yarn-project/archiver/src/store/message_store.ts b/yarn-project/archiver/src/store/message_store.ts index 4c07ba9f9c86..0408f7f0c3c2 100644 --- a/yarn-project/archiver/src/store/message_store.ts +++ b/yarn-project/archiver/src/store/message_store.ts @@ -137,7 +137,7 @@ export class MessageStore { ); } - // Check the first message in a block has the correct index. + // Check the first message in a checkpoint has the correct index. if ( (!lastMessage || message.checkpointNumber > lastMessage.checkpointNumber) && message.index !== expectedStart diff --git a/yarn-project/archiver/src/test/mock_l2_block_source.ts b/yarn-project/archiver/src/test/mock_l2_block_source.ts index ff4a0fe4af52..da295c09cb96 100644 --- a/yarn-project/archiver/src/test/mock_l2_block_source.ts +++ b/yarn-project/archiver/src/test/mock_l2_block_source.ts @@ -42,6 +42,12 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource { await this.createCheckpoints(numBlocks, 1); } + public getCheckpointNumber(): Promise { + return Promise.resolve( + this.checkpointList.length === 0 ? CheckpointNumber.ZERO : CheckpointNumber(this.checkpointList.length), + ); + } + /** Creates checkpoints, each containing `blocksPerCheckpoint` blocks. */ public async createCheckpoints(numCheckpoints: number, blocksPerCheckpoint: number = 1) { for (let c = 0; c < numCheckpoints; c++) { diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index 5b7f3f74b19f..0b971ec8082b 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -734,6 +734,10 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable { return await this.blockSource.getCheckpointedL2BlockNumber(); } + public getCheckpointNumber(): Promise { + return this.blockSource.getCheckpointNumber(); + } + /** * Method to fetch the version of the package. * @returns The node package version @@ -1041,11 +1045,9 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable { return [witness.index, witness.path]; } - public async getL1ToL2MessageBlock(l1ToL2Message: Fr): Promise { + public async getL1ToL2MessageCheckpoint(l1ToL2Message: Fr): Promise { const messageIndex = await this.l1ToL2MessageSource.getL1ToL2MessageIndex(l1ToL2Message); - return messageIndex - ? BlockNumber.fromCheckpointNumber(InboxLeaf.checkpointNumberFromIndex(messageIndex)) - : undefined; + return messageIndex ? InboxLeaf.checkpointNumberFromIndex(messageIndex) : undefined; } /** diff --git a/yarn-project/aztec.js/src/utils/cross_chain.ts b/yarn-project/aztec.js/src/utils/cross_chain.ts index 38c278fe9597..5ba97b49c758 100644 --- a/yarn-project/aztec.js/src/utils/cross_chain.ts +++ b/yarn-project/aztec.js/src/utils/cross_chain.ts @@ -8,17 +8,15 @@ import type { AztecNode } from '@aztec/stdlib/interfaces/client'; * @param l1ToL2MessageHash - Hash of the L1 to L2 message * @param opts - Options */ -export async function waitForL1ToL2MessageReady( - node: Pick, +export function waitForL1ToL2MessageReady( + node: Pick, l1ToL2MessageHash: Fr, opts: { /** Timeout for the operation in seconds */ timeoutSeconds: number; - /** True if the message is meant to be consumed from a public function */ forPublicConsumption: boolean; }, ) { - const messageBlockNumber = await node.getL1ToL2MessageBlock(l1ToL2MessageHash); return retryUntil( - () => isL1ToL2MessageReady(node, l1ToL2MessageHash, { ...opts, messageBlockNumber }), + () => isL1ToL2MessageReady(node, l1ToL2MessageHash), `L1 to L2 message ${l1ToL2MessageHash.toString()} ready`, opts.timeoutSeconds, 1, @@ -29,25 +27,18 @@ export async function waitForL1ToL2MessageReady( * Returns whether the L1 to L2 message is ready to be consumed. * @param node - Aztec node instance used to obtain the information about the message * @param l1ToL2MessageHash - Hash of the L1 to L2 message - * @param opts - Options * @returns True if the message is ready to be consumed, false otherwise */ export async function isL1ToL2MessageReady( - node: Pick, + node: Pick, l1ToL2MessageHash: Fr, - opts: { - /** True if the message is meant to be consumed from a public function */ forPublicConsumption: boolean; - /** Cached synced block number for the message (will be fetched from PXE otherwise) */ messageBlockNumber?: number; - }, ): Promise { - const blockNumber = await node.getBlockNumber(); - const messageBlockNumber = opts.messageBlockNumber ?? (await node.getL1ToL2MessageBlock(l1ToL2MessageHash)); - if (messageBlockNumber === undefined) { + const messageCheckpointNumber = await node.getL1ToL2MessageCheckpoint(l1ToL2MessageHash); + if (messageCheckpointNumber === undefined) { return false; } - // Note that public messages can be consumed 1 block earlier, since the sequencer will include the messages - // in the L1 to L2 message tree before executing the txs for the block. In private, however, we need to wait - // until the message is included so we can make use of the membership witness. - return opts.forPublicConsumption ? blockNumber + 1 >= messageBlockNumber : blockNumber >= messageBlockNumber; + // L1 to L2 messages are included in the first block of a checkpoint + const latestBlock = await node.getBlock('latest'); + return latestBlock !== undefined && latestBlock.checkpointNumber >= messageCheckpointNumber; } diff --git a/yarn-project/bot/src/cross_chain_bot.ts b/yarn-project/bot/src/cross_chain_bot.ts index 0165b5a778a8..52c59a54f58d 100644 --- a/yarn-project/bot/src/cross_chain_bot.ts +++ b/yarn-project/bot/src/cross_chain_bot.ts @@ -174,14 +174,7 @@ export class CrossChainBot extends BaseBot { ): Promise { const now = Date.now(); for (const msg of pendingMessages) { - const ready = await isL1ToL2MessageReady(this.node, Fr.fromHexString(msg.msgHash), { - // Use forPublicConsumption: false so we wait until blockNumber >= messageBlockNumber. - // With forPublicConsumption: true, the check returns true one block early (the sequencer - // includes L1→L2 messages before executing the block's txs), but gas estimation simulates - // against the current world state which doesn't yet have the message. - // See https://linear.app/aztec-labs/issue/A-548 for details. - forPublicConsumption: false, - }); + const ready = await isL1ToL2MessageReady(this.node, Fr.fromHexString(msg.msgHash)); if (ready) { return msg; } diff --git a/yarn-project/bot/src/factory.ts b/yarn-project/bot/src/factory.ts index 31b05f540b8e..89b7ed4be7da 100644 --- a/yarn-project/bot/src/factory.ts +++ b/yarn-project/bot/src/factory.ts @@ -158,11 +158,6 @@ export class BotFactory { const firstMsg = allMessages[0]; await waitForL1ToL2MessageReady(this.aztecNode, Fr.fromHexString(firstMsg.msgHash), { timeoutSeconds: this.config.l1ToL2MessageTimeoutSeconds, - // Use forPublicConsumption: false so we wait until the message is in the current world - // state. With true, it returns one block early which causes gas estimation simulation to - // fail since it runs against the current state. - // See https://linear.app/aztec-labs/issue/A-548 for details. - forPublicConsumption: false, }); this.log.info(`First L1→L2 message is ready`); } @@ -507,7 +502,6 @@ export class BotFactory { await this.withNoMinTxsPerBlock(() => waitForL1ToL2MessageReady(this.aztecNode, messageHash, { timeoutSeconds: this.config.l1ToL2MessageTimeoutSeconds, - forPublicConsumption: false, }), ); return existingClaim.claim; @@ -546,7 +540,6 @@ export class BotFactory { await this.withNoMinTxsPerBlock(() => waitForL1ToL2MessageReady(this.aztecNode, Fr.fromHexString(claim.messageHash), { timeoutSeconds: this.config.l1ToL2MessageTimeoutSeconds, - forPublicConsumption: false, }), ); diff --git a/yarn-project/end-to-end/src/bench/node_rpc_perf.test.ts b/yarn-project/end-to-end/src/bench/node_rpc_perf.test.ts index a0362bf60e56..385076c83cdf 100644 --- a/yarn-project/end-to-end/src/bench/node_rpc_perf.test.ts +++ b/yarn-project/end-to-end/src/bench/node_rpc_perf.test.ts @@ -280,6 +280,12 @@ describe('e2e_node_rpc_perf', () => { expect(stats.avg).toBeLessThan(1000); }); + it('benchmarks getCheckpointNumber', async () => { + const { stats } = await benchmark('getCheckpointNumber', () => aztecNode.getCheckpointNumber()); + addResult('getCheckpointNumber', stats); + expect(stats.avg).toBeLessThan(1000); + }); + it('benchmarks getProvenBlockNumber', async () => { const { stats } = await benchmark('getProvenBlockNumber', () => aztecNode.getProvenBlockNumber()); addResult('getProvenBlockNumber', stats); @@ -414,10 +420,12 @@ describe('e2e_node_rpc_perf', () => { }); describe('message APIs', () => { - it('benchmarks getL1ToL2MessageBlock', async () => { + it('benchmarks getL1ToL2MessageCheckpoint', async () => { const l1ToL2Message = Fr.random(); - const { stats } = await benchmark('getL1ToL2MessageBlock', () => aztecNode.getL1ToL2MessageBlock(l1ToL2Message)); - addResult('getL1ToL2MessageBlock', stats); + const { stats } = await benchmark('getL1ToL2MessageCheckpoint', () => + aztecNode.getL1ToL2MessageCheckpoint(l1ToL2Message), + ); + addResult('getL1ToL2MessageCheckpoint', stats); expect(stats.avg).toBeLessThan(2000); }); diff --git a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l1_to_l2.test.ts b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l1_to_l2.test.ts index e83b195c5f84..ec5ecbe78b57 100644 --- a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l1_to_l2.test.ts +++ b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l1_to_l2.test.ts @@ -6,7 +6,7 @@ import { isL1ToL2MessageReady } from '@aztec/aztec.js/messaging'; import type { AztecNode } from '@aztec/aztec.js/node'; import { TxExecutionResult } from '@aztec/aztec.js/tx'; import type { Wallet } from '@aztec/aztec.js/wallet'; -import { BlockNumber } from '@aztec/foundation/branded-types'; +import { BlockNumber, IndexWithinCheckpoint } from '@aztec/foundation/branded-types'; import { timesAsync } from '@aztec/foundation/collection'; import { retryUntil } from '@aztec/foundation/retry'; import { TestContract } from '@aztec/noir-test-contracts.js/Test'; @@ -56,7 +56,34 @@ describe('e2e_cross_chain_messaging l1_to_l2', () => { if (newBlock === block) { throw new Error(`Failed to advance block ${block}`); } - return undefined; + return newBlock; + }; + + const waitForBlockToCheckpoint = async (blockNumber: BlockNumber) => { + return await retryUntil( + async () => { + const checkpointedBlockNumber = await aztecNode.getCheckpointedBlockNumber(); + const isCheckpointed = checkpointedBlockNumber >= blockNumber; + if (!isCheckpointed) { + return undefined; + } + const [checkpointedBlock] = await aztecNode.getCheckpointedBlocks(blockNumber, 1); + return checkpointedBlock.checkpointNumber; + }, + 'wait for block to checkpoint', + 60, + ); + }; + + const advanceCheckpoint = async () => { + let checkpoint = await aztecNode.getCheckpointNumber(); + const originalCheckpoint = checkpoint; + log.warn(`Original checkpoint ${originalCheckpoint}`); + do { + const newBlock = await advanceBlock(); + checkpoint = await waitForBlockToCheckpoint(newBlock); + } while (checkpoint <= originalCheckpoint); + log.warn(`At checkpoint ${checkpoint}`); }; // Same as above but ignores errors. Useful if we expect a prune. @@ -68,12 +95,19 @@ describe('e2e_cross_chain_messaging l1_to_l2', () => { } }; - // Waits until the message is fetched by the archiver of the node and returns the msg target block + // Waits until the message is fetched by the archiver of the node and returns the msg target checkpoint const waitForMessageFetched = async (msgHash: Fr) => { log.warn(`Waiting until the message is fetched by the node`); return await retryUntil( - async () => (await aztecNode.getL1ToL2MessageBlock(msgHash)) ?? (await advanceBlock()), - 'get msg block', + async () => { + const checkpoint = await aztecNode.getL1ToL2MessageCheckpoint(msgHash); + if (checkpoint !== undefined) { + return checkpoint; + } + await advanceBlock(); + return undefined; + }, + 'get msg checkpoint', 60, ); }; @@ -84,20 +118,27 @@ describe('e2e_cross_chain_messaging l1_to_l2', () => { scope: 'private' | 'public', onNotReady?: (blockNumber: BlockNumber) => Promise, ) => { - const msgBlock = await waitForMessageFetched(msgHash); - log.warn(`Waiting until L2 reaches msg block ${msgBlock} (current is ${await aztecNode.getBlockNumber()})`); + const msgCheckpoint = await waitForMessageFetched(msgHash); + log.warn( + `Waiting until L2 reaches the first block of msg checkpoint ${msgCheckpoint} (current is ${await aztecNode.getCheckpointNumber()})`, + ); await retryUntil( async () => { - const blockNumber = await aztecNode.getBlockNumber(); + const [blockNumber, checkpointNumber] = await Promise.all([ + aztecNode.getBlockNumber(), + aztecNode.getCheckpointNumber(), + ]); const witness = await aztecNode.getL1ToL2MessageMembershipWitness('latest', msgHash); - const isReady = await isL1ToL2MessageReady(aztecNode, msgHash, { forPublicConsumption: scope === 'public' }); - log.info(`Block is ${blockNumber}. Message block is ${msgBlock}. Witness ${!!witness}. Ready ${isReady}.`); + const isReady = await isL1ToL2MessageReady(aztecNode, msgHash); + log.info( + `Block is ${blockNumber}, checkpoint is ${checkpointNumber}. Message checkpoint is ${msgCheckpoint}. Witness ${!!witness}. Ready ${isReady}.`, + ); if (!isReady) { await (onNotReady ? onNotReady(blockNumber) : advanceBlock()); } return isReady; }, - `wait for rollup to reach msg block ${msgBlock}`, + `wait for rollup to reach msg checkpoint ${msgCheckpoint}`, 120, ); }; @@ -118,12 +159,8 @@ describe('e2e_cross_chain_messaging l1_to_l2', () => { await waitForMessageReady(message1Hash, scope); - // The waitForMessageReady returns true earlier for public-land, so we can only check the membership - // witness for private-land here. - if (scope === 'private') { - const [message1Index] = (await aztecNode.getL1ToL2MessageMembershipWitness('latest', message1Hash))!; - expect(actualMessage1Index.toBigInt()).toBe(message1Index); - } + const [message1Index] = (await aztecNode.getL1ToL2MessageMembershipWitness('latest', message1Hash))!; + expect(actualMessage1Index.toBigInt()).toBe(message1Index); // We consume the L1 to L2 message using the test contract either from private or public await getConsumeMethod(scope)( @@ -143,12 +180,10 @@ describe('e2e_cross_chain_messaging l1_to_l2', () => { // We check that the duplicate message was correctly inserted by checking that its message index is defined await waitForMessageReady(message2Hash, scope); - if (scope === 'private') { - const [message2Index] = (await aztecNode.getL1ToL2MessageMembershipWitness('latest', message2Hash))!; - expect(message2Index).toBeDefined(); - expect(message2Index).toBeGreaterThan(actualMessage1Index.toBigInt()); - expect(actualMessage2Index.toBigInt()).toBe(message2Index); - } + const [message2Index] = (await aztecNode.getL1ToL2MessageMembershipWitness('latest', message2Hash))!; + expect(message2Index).toBeDefined(); + expect(message2Index).toBeGreaterThan(actualMessage1Index.toBigInt()); + expect(actualMessage2Index.toBigInt()).toBe(message2Index); // Now we consume the message again. Everything should pass because oracle should return the duplicate message // which is not nullified @@ -162,21 +197,22 @@ describe('e2e_cross_chain_messaging l1_to_l2', () => { 120_000, ); - // Inbox block number can drift on two scenarios: if the rollup reorgs and rolls back its own - // block number, or if the inbox receives too many messages and they are inserted faster than - // they are consumed. In this test, we mine several blocks without marking them as proven until + // Inbox checkpoint number can drift on two scenarios: if the rollup reorgs and rolls back its own + // checkpoint number, or if the inbox receives too many messages and they are inserted faster than + // they are consumed. In this test, we mine several checkpoints without marking them as proven until // we can trigger a reorg, and then wait until the message can be processed to consume it. it.each(['private', 'public'] as const)( 'can consume L1 to L2 message in %s after inbox drifts away from the rollup', async (scope: 'private' | 'public') => { // Stop proving const lastProven = await aztecNode.getBlockNumber(); - log.warn(`Stopping proof submission at block ${lastProven} to allow drift`); + const [checkpointedProvenBlock] = await aztecNode.getCheckpointedBlocks(lastProven, 1); + log.warn(`Stopping proof submission at checkpoint ${checkpointedProvenBlock.checkpointNumber} to allow drift`); t.context.watcher.setIsMarkingAsProven(false); - // Mine several blocks to ensure drift + // Mine several checkpoints to ensure drift log.warn(`Mining blocks to allow drift`); - await timesAsync(4, advanceBlock); + await timesAsync(4, advanceCheckpoint); // Generate and send the message to the L1 contract log.warn(`Sending L1 to L2 message`); @@ -185,9 +221,9 @@ describe('e2e_cross_chain_messaging l1_to_l2', () => { const { msgHash, globalLeafIndex } = await sendL1ToL2Message(message, crossChainTestHarness); // Wait until the Aztec node has synced it - const msgBlockNumber = await waitForMessageFetched(msgHash); - log.warn(`Message synced for block ${msgBlockNumber}`); - expect(lastProven + 4).toBeLessThan(msgBlockNumber); + const msgCheckpointNumber = await waitForMessageFetched(msgHash); + log.warn(`Message synced for checkpoint ${msgCheckpointNumber}`); + expect(checkpointedProvenBlock.checkpointNumber + 4).toBeLessThan(msgCheckpointNumber); // And keep mining until we prune back to the original block number. Now the "waiting for two blocks" // strategy for the message to be ready to use shouldn't work, since the lastProven block is more than @@ -214,25 +250,33 @@ describe('e2e_cross_chain_messaging l1_to_l2', () => { // On private, we simulate the tx locally and check that we get a missing message error, then we advance to the next block await expect(() => consume().simulate({ from: user1Address })).rejects.toThrow(/No L1 to L2 message found/); await tryAdvanceBlock(); - await t.context.watcher.markAsProven(); } else { - // On public, we actually send the tx and check that it reverts due to the missing message. - // This advances the block too as a side-effect. Note that we do not rely on a simulation since the cross chain messages - // do not get added at the beginning of the block during node_simulatePublicCalls (maybe they should?). + // In public it is harder to determine when a message becomes consumable. + // We send a transaction, this advances the chain and the message MIGHT be consumed in the new block. + // If it does get consumed then we check that the block contains the message. + // If it fails we check that the block doesn't contain the message const receipt = await consume().send({ from: user1Address, wait: { dontThrowOnRevert: true } }); - expect(receipt.executionResult).toEqual(TxExecutionResult.APP_LOGIC_REVERTED); - await t.context.watcher.markAsProven(); + if (receipt.executionResult === TxExecutionResult.SUCCESS) { + // The block the transaction included should be for the message checkpoint number + // and be the first block in the checkpoint + const block = await aztecNode.getBlock(receipt.blockNumber!); + expect(block).toBeDefined(); + expect(block!.checkpointNumber).toEqual(msgCheckpointNumber); + expect(block!.indexWithinCheckpoint).toEqual(IndexWithinCheckpoint.ZERO); + } else { + expect(receipt.executionResult).toEqual(TxExecutionResult.APP_LOGIC_REVERTED); + } } + await t.context.watcher.markAsProven(); }); // Verify the membership witness is available for creating the tx (private-land only) if (scope === 'private') { const [messageIndex] = (await aztecNode.getL1ToL2MessageMembershipWitness('latest', msgHash))!; expect(messageIndex).toEqual(globalLeafIndex.toBigInt()); + // And consume the message for private, public was already consumed. + await consume().send({ from: user1Address }); } - - // And consume the message - await consume().send({ from: user1Address }); }, ); }); diff --git a/yarn-project/end-to-end/src/e2e_epochs/epochs_mbps.parallel.test.ts b/yarn-project/end-to-end/src/e2e_epochs/epochs_mbps.parallel.test.ts index 1917f419e9f4..50154c9bdb0b 100644 --- a/yarn-project/end-to-end/src/e2e_epochs/epochs_mbps.parallel.test.ts +++ b/yarn-project/end-to-end/src/e2e_epochs/epochs_mbps.parallel.test.ts @@ -356,7 +356,7 @@ describe('e2e_epochs/epochs_mbps', () => { l1ToL2Messages.map(async ({ msgHash }, i) => { logger.warn(`Waiting for L1→L2 message ${i + 1} to be ready`); await retryUntil( - () => isL1ToL2MessageReady(context.aztecNode, msgHash, { forPublicConsumption: true }), + () => isL1ToL2MessageReady(context.aztecNode, msgHash), `L1→L2 message ${i + 1} ready`, test.L2_SLOT_DURATION_IN_S * 5, ); diff --git a/yarn-project/end-to-end/src/e2e_epochs/epochs_proof_public_cross_chain.test.ts b/yarn-project/end-to-end/src/e2e_epochs/epochs_proof_public_cross_chain.test.ts index 846cd5f82b96..699930ba4395 100644 --- a/yarn-project/end-to-end/src/e2e_epochs/epochs_proof_public_cross_chain.test.ts +++ b/yarn-project/end-to-end/src/e2e_epochs/epochs_proof_public_cross_chain.test.ts @@ -57,7 +57,6 @@ describe('e2e_epochs/epochs_proof_public_cross_chain', () => { logger.warn(`Waiting for message ${msgHash} with index ${globalLeafIndex} to be synced`); await waitForL1ToL2MessageReady(context.aztecNode, msgHash, { - forPublicConsumption: true, timeoutSeconds: test.L2_SLOT_DURATION_IN_S * 6, }); diff --git a/yarn-project/end-to-end/src/spartan/setup_test_wallets.ts b/yarn-project/end-to-end/src/spartan/setup_test_wallets.ts index 8009d474e601..790c19e277a4 100644 --- a/yarn-project/end-to-end/src/spartan/setup_test_wallets.ts +++ b/yarn-project/end-to-end/src/spartan/setup_test_wallets.ts @@ -278,7 +278,7 @@ async function bridgeL1FeeJuice( const claim = await portal.bridgeTokensPublic(recipient, amount, true /* mint */); const isSynced = async () => - (await aztecNode.getL1ToL2MessageBlock(Fr.fromHexString(claim.messageHash))) !== undefined; + (await aztecNode.getL1ToL2MessageCheckpoint(Fr.fromHexString(claim.messageHash))) !== undefined; await retryUntil(isSynced, `message ${claim.messageHash} sync`, 24, 0.5); log.info(`Created a claim for ${amount} L1 fee juice to ${recipient}.`, claim); diff --git a/yarn-project/stdlib/src/block/l2_block_source.ts b/yarn-project/stdlib/src/block/l2_block_source.ts index f1daf550d7eb..39368d09ad99 100644 --- a/yarn-project/stdlib/src/block/l2_block_source.ts +++ b/yarn-project/stdlib/src/block/l2_block_source.ts @@ -49,6 +49,12 @@ export interface L2BlockSource { */ getBlockNumber(): Promise; + /** + * Gets the number of the latest L2 checkpoint processed by the block source implementation. + * @returns The number of the latest L2 checkpoint processed by the block source implementation. + */ + getCheckpointNumber(): Promise; + /** * Gets the number of the latest L2 block proven seen by the block source implementation. * @returns The number of the latest L2 block proven seen by the block source implementation. diff --git a/yarn-project/stdlib/src/interfaces/archiver.test.ts b/yarn-project/stdlib/src/interfaces/archiver.test.ts index 2b5cb983325b..710118ceb5e9 100644 --- a/yarn-project/stdlib/src/interfaces/archiver.test.ts +++ b/yarn-project/stdlib/src/interfaces/archiver.test.ts @@ -81,6 +81,11 @@ describe('ArchiverApiSchema', () => { expect(result).toEqual(BlockNumber(1)); }); + it('getCheckpointNumber', async () => { + const result = await context.client.getCheckpointNumber(); + expect(result).toEqual(CheckpointNumber(1)); + }); + it('getProvenBlockNumber', async () => { const result = await context.client.getProvenBlockNumber(); expect(result).toEqual(BlockNumber(1)); @@ -405,6 +410,9 @@ class MockArchiver implements ArchiverApi { getCheckpointedL2BlockNumber(): Promise { return Promise.resolve(BlockNumber(1)); } + getCheckpointNumber(): Promise { + return Promise.resolve(CheckpointNumber(1)); + } getFinalizedL2BlockNumber(): Promise { return Promise.resolve(BlockNumber(0)); } diff --git a/yarn-project/stdlib/src/interfaces/archiver.ts b/yarn-project/stdlib/src/interfaces/archiver.ts index 9af2b49e6fbc..949c66575040 100644 --- a/yarn-project/stdlib/src/interfaces/archiver.ts +++ b/yarn-project/stdlib/src/interfaces/archiver.ts @@ -86,6 +86,7 @@ export const ArchiverApiSchema: ApiSchemaFor = { getBlockNumber: z.function().args().returns(BlockNumberSchema), getProvenBlockNumber: z.function().args().returns(BlockNumberSchema), getCheckpointedL2BlockNumber: z.function().args().returns(BlockNumberSchema), + getCheckpointNumber: z.function().args().returns(CheckpointNumberSchema), getFinalizedL2BlockNumber: z.function().args().returns(BlockNumberSchema), getBlock: z.function().args(BlockNumberSchema).returns(L2Block.schema.optional()), getBlockHeader: z diff --git a/yarn-project/stdlib/src/interfaces/aztec-node.test.ts b/yarn-project/stdlib/src/interfaces/aztec-node.test.ts index 436945dd773e..9afac73c16d5 100644 --- a/yarn-project/stdlib/src/interfaces/aztec-node.test.ts +++ b/yarn-project/stdlib/src/interfaces/aztec-node.test.ts @@ -115,8 +115,8 @@ describe('AztecNodeApiSchema', () => { expect(response).toEqual([1n, expect.any(SiblingPath)]); }); - it('getL1ToL2MessageBlock', async () => { - const response = await context.client.getL1ToL2MessageBlock(Fr.random()); + it('getL1ToL2MessageCheckpoint', async () => { + const response = await context.client.getL1ToL2MessageCheckpoint(Fr.random()); expect(response).toEqual(5); }); @@ -209,6 +209,11 @@ describe('AztecNodeApiSchema', () => { expect(response).toBe(BlockNumber(1)); }); + it('getCheckpointNumber', async () => { + const response = await context.client.getCheckpointNumber(); + expect(response).toBe(CheckpointNumber(1)); + }); + it('isReady', async () => { const response = await context.client.isReady(); expect(response).toBe(true); @@ -578,9 +583,9 @@ class MockAztecNode implements AztecNode { expect(noteHash).toBeInstanceOf(Fr); return Promise.resolve(MembershipWitness.random(NOTE_HASH_TREE_HEIGHT)); } - getL1ToL2MessageBlock(l1ToL2Message: Fr): Promise { + getL1ToL2MessageCheckpoint(l1ToL2Message: Fr): Promise { expect(l1ToL2Message).toBeInstanceOf(Fr); - return Promise.resolve(BlockNumber(5)); + return Promise.resolve(CheckpointNumber(5)); } isL1ToL2MessageSynced(l1ToL2Message: Fr): Promise { expect(l1ToL2Message).toBeInstanceOf(Fr); @@ -658,6 +663,9 @@ class MockAztecNode implements AztecNode { getCheckpointedBlockNumber(): Promise { return Promise.resolve(BlockNumber(1)); } + getCheckpointNumber(): Promise { + return Promise.resolve(CheckpointNumber(1)); + } isReady(): Promise { return Promise.resolve(true); } diff --git a/yarn-project/stdlib/src/interfaces/aztec-node.ts b/yarn-project/stdlib/src/interfaces/aztec-node.ts index 34b8639eee7f..7f613d9e6891 100644 --- a/yarn-project/stdlib/src/interfaces/aztec-node.ts +++ b/yarn-project/stdlib/src/interfaces/aztec-node.ts @@ -4,7 +4,9 @@ import { BlockNumber, BlockNumberPositiveSchema, BlockNumberSchema, + CheckpointNumber, CheckpointNumberPositiveSchema, + CheckpointNumberSchema, EpochNumber, EpochNumberSchema, type SlotNumber, @@ -172,14 +174,14 @@ export interface AztecNode l1ToL2Message: Fr, ): Promise<[bigint, SiblingPath] | undefined>; - /** Returns the L2 block number in which this L1 to L2 message becomes available, or undefined if not found. */ - getL1ToL2MessageBlock(l1ToL2Message: Fr): Promise; + /** Returns the L2 checkpoint number in which this L1 to L2 message becomes available, or undefined if not found. */ + getL1ToL2MessageCheckpoint(l1ToL2Message: Fr): Promise; /** * Returns whether an L1 to L2 message is synced by archiver. * @param l1ToL2Message - The L1 to L2 message to check. * @returns Whether the message is synced. - * @deprecated Use `getL1ToL2MessageBlock` instead. This method may return true even if the message is not ready to use. + * @deprecated Use `getL1ToL2MessageCheckpoint` instead. This method may return true even if the message is not ready to use. */ isL1ToL2MessageSynced(l1ToL2Message: Fr): Promise; @@ -230,6 +232,12 @@ export interface AztecNode */ getCheckpointedBlockNumber(): Promise; + /** + * Method to fetch the latest checkpoint number synchronized by the node. + * @returns The checkpoint number. + */ + getCheckpointNumber(): Promise; + /** * Method to determine if the node is ready to accept transactions. * @returns - Flag indicating the readiness for tx submission. @@ -517,7 +525,7 @@ export const AztecNodeApiSchema: ApiSchemaFor = { .args(BlockParameterSchema, schemas.Fr) .returns(z.tuple([schemas.BigInt, SiblingPath.schemaFor(L1_TO_L2_MSG_TREE_HEIGHT)]).optional()), - getL1ToL2MessageBlock: z.function().args(schemas.Fr).returns(BlockNumberSchema.optional()), + getL1ToL2MessageCheckpoint: z.function().args(schemas.Fr).returns(CheckpointNumberSchema.optional()), isL1ToL2MessageSynced: z.function().args(schemas.Fr).returns(z.boolean()), @@ -534,6 +542,8 @@ export const AztecNodeApiSchema: ApiSchemaFor = { getBlockNumber: z.function().returns(BlockNumberSchema), + getCheckpointNumber: z.function().returns(CheckpointNumberSchema), + getProvenBlockNumber: z.function().returns(BlockNumberSchema), getCheckpointedBlockNumber: z.function().returns(BlockNumberSchema),