diff --git a/yarn-project/archiver/src/archiver/archiver.test.ts b/yarn-project/archiver/src/archiver/archiver.test.ts index d5f16e1db422..7a6dfbe3f3c8 100644 --- a/yarn-project/archiver/src/archiver/archiver.test.ts +++ b/yarn-project/archiver/src/archiver/archiver.test.ts @@ -6,6 +6,7 @@ import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr } from '@aztec/foundation/fields'; import { type Logger, createLogger } from '@aztec/foundation/log'; import { sleep } from '@aztec/foundation/sleep'; +import { openTmpStore } from '@aztec/kv-store/lmdb-v2'; import { ForwarderAbi, type InboxAbi, RollupAbi } from '@aztec/l1-artifacts'; import { L2Block } from '@aztec/stdlib/block'; import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers'; @@ -20,7 +21,7 @@ import { type Log, type Transaction, encodeFunctionData, toHex } from 'viem'; import { Archiver } from './archiver.js'; import type { ArchiverDataStore } from './archiver_store.js'; import type { ArchiverInstrumentation } from './instrumentation.js'; -import { MemoryArchiverStore } from './memory_archiver_store/memory_archiver_store.js'; +import { KVArchiverDataStore } from './kv_archiver_store/kv_archiver_store.js'; interface MockRollupContractRead { /** Given an L2 block number, returns the archive. */ @@ -104,7 +105,7 @@ describe('Archiver', () => { const tracer = getTelemetryClient().getTracer(''); instrumentation = mock({ isEnabled: () => true, tracer }); - archiverStore = new MemoryArchiverStore(1000); + archiverStore = new KVArchiverDataStore(await openTmpStore('archiver_test'), 1000); l1Constants = { l1GenesisTime: BigInt(now), l1StartBlock: 0n, @@ -361,7 +362,11 @@ describe('Archiver', () => { const rollupTxs = await Promise.all(blocks.map(makeRollupTx)); const blobHashes = await Promise.all(blocks.map(makeVersionedBlobHashes)); - publicClient.getBlockNumber.mockResolvedValueOnce(50n).mockResolvedValueOnce(100n).mockResolvedValueOnce(150n); + let mockedBlockNum = 0n; + publicClient.getBlockNumber.mockImplementation(() => { + mockedBlockNum += 50n; + return Promise.resolve(mockedBlockNum); + }); // We will return status at first to have an empty round, then as if we have 2 pending blocks, and finally // Just a single pending block returning a "failure" for the expected pending block @@ -402,7 +407,7 @@ describe('Archiver', () => { expect(loggerSpy).toHaveBeenCalledWith(`No blocks to retrieve from 1 to 50`); // Lets take a look to see if we can find re-org stuff! - await sleep(1000); + await sleep(2000); expect(loggerSpy).toHaveBeenCalledWith(`L2 prune has been detected.`); diff --git a/yarn-project/archiver/src/archiver/index.ts b/yarn-project/archiver/src/archiver/index.ts index f7a14b62255b..72515a0441b2 100644 --- a/yarn-project/archiver/src/archiver/index.ts +++ b/yarn-project/archiver/src/archiver/index.ts @@ -1,7 +1,6 @@ export * from './archiver.js'; export * from './config.js'; export { type PublishedL2Block, type L1PublishedData } from './structs/published.js'; -export { MemoryArchiverStore } from './memory_archiver_store/memory_archiver_store.js'; export type { ArchiverDataStore } from './archiver_store.js'; export { KVArchiverDataStore } from './kv_archiver_store/kv_archiver_store.js'; export { ContractInstanceStore } from './kv_archiver_store/contract_instance_store.js'; diff --git a/yarn-project/archiver/src/archiver/kv_archiver_store/log_store.ts b/yarn-project/archiver/src/archiver/kv_archiver_store/log_store.ts index 11b6daadde42..40f3ae8b1984 100644 --- a/yarn-project/archiver/src/archiver/kv_archiver_store/log_store.ts +++ b/yarn-project/archiver/src/archiver/kv_archiver_store/log_store.ts @@ -158,6 +158,7 @@ export class LogStore { this.#privateLogsByBlock.delete(block.number), this.#publicLogsByBlock.delete(block.number), this.#logTagsByBlock.delete(block.number), + this.#contractClassLogsByBlock.delete(block.number), ]), ), ); diff --git a/yarn-project/archiver/src/archiver/memory_archiver_store/memory_archiver_store.test.ts b/yarn-project/archiver/src/archiver/memory_archiver_store/memory_archiver_store.test.ts deleted file mode 100644 index ad43983937c4..000000000000 --- a/yarn-project/archiver/src/archiver/memory_archiver_store/memory_archiver_store.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { times, timesParallel } from '@aztec/foundation/collection'; -import { Signature } from '@aztec/foundation/eth-signature'; -import { L2Block } from '@aztec/stdlib/block'; - -import type { ArchiverDataStore } from '../archiver_store.js'; -import { describeArchiverDataStore } from '../archiver_store_test_suite.js'; -import { MemoryArchiverStore } from './memory_archiver_store.js'; - -describe('MemoryArchiverStore', () => { - let archiverStore: ArchiverDataStore; - - beforeEach(() => { - archiverStore = new MemoryArchiverStore(1000); - }); - - describeArchiverDataStore('implements ArchiverStore', () => archiverStore); - - describe('getPublicLogs config', () => { - it('does not return more than "maxLogs" logs', async () => { - const maxLogs = 5; - archiverStore = new MemoryArchiverStore(maxLogs); - const blocks = await timesParallel(10, async (index: number) => ({ - block: await L2Block.random(index + 1, 4, 3, 2), - l1: { blockNumber: BigInt(index), blockHash: `0x${index}`, timestamp: BigInt(index) }, - signatures: times(3, Signature.random), - })); - - await archiverStore.addBlocks(blocks); - await archiverStore.addLogs(blocks.map(b => b.block)); - - const response = await archiverStore.getPublicLogs({}); - - expect(response.maxLogsHit).toBeTruthy(); - expect(response.logs.length).toEqual(maxLogs); - }); - }); -}); diff --git a/yarn-project/archiver/src/archiver/memory_archiver_store/memory_archiver_store.ts b/yarn-project/archiver/src/archiver/memory_archiver_store/memory_archiver_store.ts deleted file mode 100644 index 15e09d3714e0..000000000000 --- a/yarn-project/archiver/src/archiver/memory_archiver_store/memory_archiver_store.ts +++ /dev/null @@ -1,770 +0,0 @@ -import { INITIAL_L2_BLOCK_NUM, MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX } from '@aztec/constants'; -import { Fr } from '@aztec/foundation/fields'; -import { createLogger } from '@aztec/foundation/log'; -import { FunctionSelector } from '@aztec/stdlib/abi'; -import type { AztecAddress } from '@aztec/stdlib/aztec-address'; -import { type InBlock, type L2Block, L2BlockHash, wrapInBlock } from '@aztec/stdlib/block'; -import type { - ContractClassPublic, - ContractClassPublicWithBlockNumber, - ContractInstanceUpdateWithAddress, - ContractInstanceWithAddress, - ExecutablePrivateFunctionWithMembershipProof, - UnconstrainedFunctionWithMembershipProof, -} from '@aztec/stdlib/contract'; -import type { GetContractClassLogsResponse, GetPublicLogsResponse } from '@aztec/stdlib/interfaces/client'; -import { - ContractClassLog, - ExtendedContractClassLog, - ExtendedPublicLog, - type LogFilter, - LogId, - type PrivateLog, - type PublicLog, - TxScopedL2Log, -} from '@aztec/stdlib/logs'; -import type { InboxLeaf } from '@aztec/stdlib/messaging'; -import { type BlockHeader, TxEffect, TxHash, TxReceipt } from '@aztec/stdlib/tx'; - -import type { ArchiverDataStore, ArchiverL1SynchPoint } from '../archiver_store.js'; -import type { DataRetrieval } from '../structs/data_retrieval.js'; -import type { PublishedL2Block } from '../structs/published.js'; -import { L1ToL2MessageStore } from './l1_to_l2_message_store.js'; - -type StoredContractInstanceUpdate = ContractInstanceUpdateWithAddress & { blockNumber: number; logIndex: number }; - -/** - * Simple, in-memory implementation of an archiver data store. - */ -export class MemoryArchiverStore implements ArchiverDataStore { - /** - * An array containing all the L2 blocks that have been fetched so far. - */ - private l2Blocks: PublishedL2Block[] = []; - - /** - * An array containing all the tx effects in the L2 blocks that have been fetched so far. - */ - private txEffects: InBlock[] = []; - - private taggedLogs: Map = new Map(); - - private logTagsPerBlock: Map = new Map(); - - private privateLogsPerBlock: Map = new Map(); - - private publicLogsPerBlock: Map = new Map(); - - private contractClassLogsPerBlock: Map = new Map(); - - private blockScopedNullifiers: Map = new Map(); - - /** - * Contains all L1 to L2 messages. - */ - private l1ToL2Messages = new L1ToL2MessageStore(); - - private contractClasses: Map = new Map(); - - private bytecodeCommitments: Map = new Map(); - - private privateFunctions: Map = new Map(); - - private unconstrainedFunctions: Map = new Map(); - - private contractInstances: Map = new Map(); - - private contractInstanceUpdates: Map = new Map(); - - private lastL1BlockNewBlocks: bigint | undefined = undefined; - - private lastL1BlockNewMessages: bigint | undefined = undefined; - - private lastProvenL2BlockNumber: number = 0; - private lastProvenL2EpochNumber: number = 0; - - private functionNames = new Map(); - - #log = createLogger('archiver:data-store'); - - constructor( - /** The max number of logs that can be obtained in 1 "getPublicLogs" call. */ - public readonly maxLogs: number, - ) {} - - public getContractClass(id: Fr): Promise { - const contractClass = this.contractClasses.get(id.toString()); - return Promise.resolve( - contractClass && { - ...contractClass, - privateFunctions: this.privateFunctions.get(id.toString()) ?? [], - unconstrainedFunctions: this.unconstrainedFunctions.get(id.toString()) ?? [], - }, - ); - } - - public getContractClassIds(): Promise { - return Promise.resolve(Array.from(this.contractClasses.keys()).map(key => Fr.fromHexString(key))); - } - - public getContractInstance( - address: AztecAddress, - blockNumber: number, - ): Promise { - const instance = this.contractInstances.get(address.toString()); - if (!instance) { - return Promise.resolve(undefined); - } - const updates = this.contractInstanceUpdates.get(address.toString()) || []; - if (updates.length > 0) { - const lastUpdate = updates[0]; - if (blockNumber >= lastUpdate.blockOfChange) { - instance.currentContractClassId = lastUpdate.newContractClassId; - } else if (!lastUpdate.prevContractClassId.isZero()) { - instance.currentContractClassId = lastUpdate.prevContractClassId; - } else { - instance.currentContractClassId = instance.originalContractClassId; - } - } - return Promise.resolve(instance); - } - - public getBytecodeCommitment(contractClassId: Fr): Promise { - return Promise.resolve(this.bytecodeCommitments.get(contractClassId.toString())); - } - - public addFunctions( - contractClassId: Fr, - newPrivateFunctions: ExecutablePrivateFunctionWithMembershipProof[], - newUnconstrainedFunctions: UnconstrainedFunctionWithMembershipProof[], - ): Promise { - const privateFunctions = this.privateFunctions.get(contractClassId.toString()) ?? []; - const unconstrainedFunctions = this.unconstrainedFunctions.get(contractClassId.toString()) ?? []; - const updatedPrivateFunctions = [ - ...privateFunctions, - ...newPrivateFunctions.filter(newFn => !privateFunctions.find(f => f.selector.equals(newFn.selector))), - ]; - const updatedUnconstrainedFunctions = [ - ...unconstrainedFunctions, - ...newUnconstrainedFunctions.filter( - newFn => !unconstrainedFunctions.find(f => f.selector.equals(newFn.selector)), - ), - ]; - this.privateFunctions.set(contractClassId.toString(), updatedPrivateFunctions); - this.unconstrainedFunctions.set(contractClassId.toString(), updatedUnconstrainedFunctions); - return Promise.resolve(true); - } - - public addContractClasses( - data: ContractClassPublic[], - bytecodeCommitments: Fr[], - blockNumber: number, - ): Promise { - for (let i = 0; i < data.length; i++) { - const contractClass = data[i]; - if (!this.contractClasses.has(contractClass.id.toString())) { - this.contractClasses.set(contractClass.id.toString(), { - ...contractClass, - l2BlockNumber: blockNumber, - }); - } - if (!this.bytecodeCommitments.has(contractClass.id.toString())) { - this.bytecodeCommitments.set(contractClass.id.toString(), bytecodeCommitments[i]); - } - } - return Promise.resolve(true); - } - - public deleteContractClasses(data: ContractClassPublic[], blockNumber: number): Promise { - for (const contractClass of data) { - const restored = this.contractClasses.get(contractClass.id.toString()); - if (restored && restored.l2BlockNumber >= blockNumber) { - this.contractClasses.delete(contractClass.id.toString()); - this.bytecodeCommitments.delete(contractClass.id.toString()); - } - } - return Promise.resolve(true); - } - - public addContractInstances(data: ContractInstanceWithAddress[], _blockNumber: number): Promise { - for (const contractInstance of data) { - this.contractInstances.set(contractInstance.address.toString(), contractInstance); - } - return Promise.resolve(true); - } - - public deleteContractInstances(data: ContractInstanceWithAddress[], _blockNumber: number): Promise { - for (const contractInstance of data) { - this.contractInstances.delete(contractInstance.address.toString()); - } - return Promise.resolve(true); - } - - public addContractInstanceUpdates(data: ContractInstanceUpdateWithAddress[], blockNumber: number): Promise { - for (let logIndex = 0; logIndex < data.length; logIndex++) { - const contractInstanceUpdate = data[logIndex]; - const updates = this.contractInstanceUpdates.get(contractInstanceUpdate.address.toString()) || []; - updates.unshift({ - ...contractInstanceUpdate, - blockNumber, - logIndex, - }); - this.contractInstanceUpdates.set(contractInstanceUpdate.address.toString(), updates); - } - return Promise.resolve(true); - } - - public deleteContractInstanceUpdates( - data: ContractInstanceUpdateWithAddress[], - blockNumber: number, - ): Promise { - for (let logIndex = 0; logIndex < data.length; logIndex++) { - const contractInstanceUpdate = data[logIndex]; - let updates = this.contractInstanceUpdates.get(contractInstanceUpdate.address.toString()) || []; - updates = updates.filter(update => !(update.blockNumber === blockNumber && update.logIndex === logIndex)); - this.contractInstanceUpdates.set(contractInstanceUpdate.address.toString(), updates); - } - return Promise.resolve(true); - } - - /** - * Append new blocks to the store's list. - * @param blocks - The L2 blocks to be added to the store and the last processed L1 block. - * @returns True if the operation is successful. - */ - public async addBlocks(blocks: PublishedL2Block[]): Promise { - if (blocks.length === 0) { - return Promise.resolve(true); - } - - this.lastL1BlockNewBlocks = blocks[blocks.length - 1].l1.blockNumber; - this.l2Blocks.push(...blocks); - const flatTxEffects = blocks.flatMap(b => b.block.body.txEffects.map(txEffect => ({ block: b, txEffect }))); - const wrappedTxEffects = await Promise.all( - flatTxEffects.map(flatTxEffect => wrapInBlock(flatTxEffect.txEffect, flatTxEffect.block.block)), - ); - this.txEffects.push(...wrappedTxEffects); - - return Promise.resolve(true); - } - - /** - * Unwinds blocks from the database - * @param from - The tip of the chain, passed for verification purposes, - * ensuring that we don't end up deleting something we did not intend - * @param blocksToUnwind - The number of blocks we are to unwind - * @returns True if the operation is successful - */ - public async unwindBlocks(from: number, blocksToUnwind: number): Promise { - const last = await this.getSynchedL2BlockNumber(); - if (from != last) { - throw new Error(`Can only unwind blocks from the tip (requested ${from} but current tip is ${last})`); - } - - const stopAt = from - blocksToUnwind; - while ((await this.getSynchedL2BlockNumber()) > stopAt) { - const block = this.l2Blocks.pop(); - if (block == undefined) { - break; - } - block.block.body.txEffects.forEach(() => this.txEffects.pop()); - } - - return Promise.resolve(true); - } - - #storeTaggedLogs(block: L2Block): void { - const dataStartIndexForBlock = - block.header.state.partial.noteHashTree.nextAvailableLeafIndex - - block.body.txEffects.length * MAX_NOTE_HASHES_PER_TX; - block.body.txEffects.forEach((txEffect, txIndex) => { - const txHash = txEffect.txHash; - const dataStartIndexForTx = dataStartIndexForBlock + txIndex * MAX_NOTE_HASHES_PER_TX; - - txEffect.privateLogs.forEach(log => { - const tag = log.fields[0]; - this.#log.verbose(`Storing private log with tag ${tag.toString()} from block ${block.number}`); - - const currentLogs = this.taggedLogs.get(tag.toString()) || []; - this.taggedLogs.set(tag.toString(), [ - ...currentLogs, - new TxScopedL2Log(txHash, dataStartIndexForTx, block.number, log), - ]); - const currentTagsInBlock = this.logTagsPerBlock.get(block.number) || []; - this.logTagsPerBlock.set(block.number, [...currentTagsInBlock, tag]); - }); - - txEffect.publicLogs.forEach(log => { - const tag = log.log[0]; - this.#log.verbose(`Storing public log with tag ${tag.toString()} from block ${block.number}`); - - const currentLogs = this.taggedLogs.get(tag.toString()) || []; - this.taggedLogs.set(tag.toString(), [ - ...currentLogs, - new TxScopedL2Log(txHash, dataStartIndexForTx, block.number, log), - ]); - const currentTagsInBlock = this.logTagsPerBlock.get(block.number) || []; - this.logTagsPerBlock.set(block.number, [...currentTagsInBlock, tag]); - }); - }); - } - - /** - * Append new logs to the store's list. - * @param block - The block for which to add the logs. - * @returns True if the operation is successful. - */ - addLogs(blocks: L2Block[]): Promise { - blocks.forEach(block => { - this.#storeTaggedLogs(block); - this.privateLogsPerBlock.set(block.number, block.body.txEffects.map(txEffect => txEffect.privateLogs).flat()); - this.publicLogsPerBlock.set(block.number, block.body.txEffects.map(txEffect => txEffect.publicLogs).flat()); - this.contractClassLogsPerBlock.set( - block.number, - block.body.txEffects.map(txEffect => txEffect.contractClassLogs).flat(), - ); - }); - return Promise.resolve(true); - } - - deleteLogs(blocks: L2Block[]): Promise { - const tagsToDelete = blocks.flatMap(block => this.logTagsPerBlock.get(block.number)); - tagsToDelete - .filter(tag => tag != undefined) - .forEach(tag => { - this.taggedLogs.delete(tag!.toString()); - }); - - blocks.forEach(block => { - this.privateLogsPerBlock.delete(block.number); - this.publicLogsPerBlock.delete(block.number); - this.logTagsPerBlock.delete(block.number); - this.contractClassLogsPerBlock.delete(block.number); - }); - - return Promise.resolve(true); - } - - async addNullifiers(blocks: L2Block[]): Promise { - await Promise.all( - blocks.map(async block => { - const dataStartIndexForBlock = - block.header.state.partial.nullifierTree.nextAvailableLeafIndex - - block.body.txEffects.length * MAX_NULLIFIERS_PER_TX; - const blockHash = await block.hash(); - block.body.txEffects.forEach((txEffects, txIndex) => { - const dataStartIndexForTx = dataStartIndexForBlock + txIndex * MAX_NULLIFIERS_PER_TX; - txEffects.nullifiers.forEach((nullifier, nullifierIndex) => { - this.blockScopedNullifiers.set(nullifier.toString(), { - index: BigInt(dataStartIndexForTx + nullifierIndex), - blockNumber: block.number, - blockHash: blockHash.toString(), - }); - }); - }); - }), - ); - return Promise.resolve(true); - } - - deleteNullifiers(blocks: L2Block[]): Promise { - blocks.forEach(block => { - block.body.txEffects.forEach(txEffect => { - txEffect.nullifiers.forEach(nullifier => { - this.blockScopedNullifiers.delete(nullifier.toString()); - }); - }); - }); - return Promise.resolve(true); - } - - findNullifiersIndexesWithBlock(blockNumber: number, nullifiers: Fr[]): Promise<(InBlock | undefined)[]> { - const blockScopedNullifiers = nullifiers.map(nullifier => { - const nullifierData = this.blockScopedNullifiers.get(nullifier.toString()); - if (nullifierData !== undefined && nullifierData.blockNumber <= blockNumber) { - return { - data: nullifierData.index, - l2BlockHash: nullifierData.blockHash, - l2BlockNumber: nullifierData.blockNumber, - } as InBlock; - } - return undefined; - }); - return Promise.resolve(blockScopedNullifiers); - } - - getTotalL1ToL2MessageCount(): Promise { - return Promise.resolve(this.l1ToL2Messages.getTotalL1ToL2MessageCount()); - } - - /** - * Append L1 to L2 messages to the store. - * @param messages - The L1 to L2 messages to be added to the store and the last processed L1 block. - * @returns True if the operation is successful. - */ - public addL1ToL2Messages(messages: DataRetrieval): Promise { - if ( - typeof this.lastL1BlockNewMessages === 'bigint' && - messages.lastProcessedL1BlockNumber <= this.lastL1BlockNewMessages - ) { - return Promise.resolve(false); - } - - this.lastL1BlockNewMessages = messages.lastProcessedL1BlockNumber; - for (const message of messages.retrievedData) { - this.l1ToL2Messages.addMessage(message); - } - return Promise.resolve(true); - } - - /** - * Gets the L1 to L2 message index in the L1 to L2 message tree. - * @param l1ToL2Message - The L1 to L2 message. - * @returns The index of the L1 to L2 message in the L1 to L2 message tree (undefined if not found). - */ - getL1ToL2MessageIndex(l1ToL2Message: Fr): Promise { - return Promise.resolve(this.l1ToL2Messages.getMessageIndex(l1ToL2Message)); - } - - /** - * Gets up to `limit` amount of L2 blocks starting from `from`. - * @param from - Number of the first block to return (inclusive). - * @param limit - The number of blocks to return. - * @returns The requested L2 blocks. - * @remarks When "from" is smaller than genesis block number, blocks from the beginning are returned. - */ - public getBlocks(from: number, limit: number): Promise { - if (limit < 1) { - return Promise.reject(new Error(`Invalid limit: ${limit}`)); - } - - if (from < INITIAL_L2_BLOCK_NUM) { - return Promise.reject(new Error(`Invalid start: ${from}`)); - } - - const fromIndex = from - INITIAL_L2_BLOCK_NUM; - if (fromIndex >= this.l2Blocks.length) { - return Promise.resolve([]); - } - - const toIndex = fromIndex + limit; - return Promise.resolve(this.l2Blocks.slice(fromIndex, toIndex)); - } - - public async getBlockHeaders(from: number, limit: number): Promise { - const blocks = await this.getBlocks(from, limit); - return blocks.map(block => block.block.header); - } - - /** - * Gets a tx effect. - * @param txHash - The txHash of the tx effect. - * @returns The requested tx effect. - */ - public getTxEffect(txHash: TxHash): Promise | undefined> { - const txEffect = this.txEffects.find(tx => tx.data.txHash.equals(txHash)); - return Promise.resolve(txEffect); - } - - /** - * Gets a receipt of a settled tx. - * @param txHash - The hash of a tx we try to get the receipt for. - * @returns The requested tx receipt (or undefined if not found). - */ - public async getSettledTxReceipt(txHash: TxHash): Promise { - for (const block of this.l2Blocks) { - for (const txEffect of block.block.body.txEffects) { - if (txEffect.txHash.equals(txHash)) { - return new TxReceipt( - txHash, - TxReceipt.statusFromRevertCode(txEffect.revertCode), - '', - txEffect.transactionFee.toBigInt(), - L2BlockHash.fromField(await block.block.hash()), - block.block.number, - ); - } - } - } - return undefined; - } - - /** - * Gets L1 to L2 message (to be) included in a given block. - * @param blockNumber - L2 block number to get messages for. - * @returns The L1 to L2 messages/leaves of the messages subtree (throws if not found). - */ - getL1ToL2Messages(blockNumber: bigint): Promise { - return Promise.resolve(this.l1ToL2Messages.getMessages(blockNumber)); - } - - /** - * Retrieves all private logs from up to `limit` blocks, starting from the block number `from`. - * @param from - The block number from which to begin retrieving logs. - * @param limit - The maximum number of blocks to retrieve logs from. - * @returns An array of private logs from the specified range of blocks. - */ - getPrivateLogs(from: number, limit: number): Promise { - if (from < INITIAL_L2_BLOCK_NUM || limit < 1) { - return Promise.resolve([]); - } - - if (from > this.l2Blocks.length) { - return Promise.resolve([]); - } - - const startIndex = from; - const endIndex = startIndex + limit; - const upper = Math.min(endIndex, this.l2Blocks.length + INITIAL_L2_BLOCK_NUM); - - const logsInBlocks = []; - for (let i = startIndex; i < upper; i++) { - const logs = this.privateLogsPerBlock.get(i); - if (logs) { - logsInBlocks.push(logs); - } - } - - return Promise.resolve(logsInBlocks.flat()); - } - - /** - * Gets all logs that match any of the received tags (i.e. logs with their first field equal to a tag). - * @param tags - The tags to filter the logs by. - * @returns For each received tag, an array of matching logs is returned. An empty array implies no logs match - * that tag. - */ - getLogsByTags(tags: Fr[]): Promise { - const logs = tags.map(tag => this.taggedLogs.get(tag.toString()) || []); - return Promise.resolve(logs); - } - - /** - * Gets public logs based on the provided filter. - * @param filter - The filter to apply to the logs. - * @returns The requested logs. - * @remarks Works by doing an intersection of all params in the filter. - */ - getPublicLogs(filter: LogFilter): Promise { - let txHash: TxHash | undefined; - let fromBlock = 0; - let toBlock = this.l2Blocks.length + INITIAL_L2_BLOCK_NUM; - let txIndexInBlock = 0; - let logIndexInTx = 0; - - if (filter.afterLog) { - // Continuation parameter is set --> tx hash is ignored - if (filter.fromBlock == undefined || filter.fromBlock <= filter.afterLog.blockNumber) { - fromBlock = filter.afterLog.blockNumber; - txIndexInBlock = filter.afterLog.txIndex; - logIndexInTx = filter.afterLog.logIndex + 1; // We want to start from the next log - } else { - fromBlock = filter.fromBlock; - } - } else { - txHash = filter.txHash; - - if (filter.fromBlock !== undefined) { - fromBlock = filter.fromBlock; - } - } - - if (filter.toBlock !== undefined) { - toBlock = filter.toBlock; - } - - // Ensure the indices are within block array bounds - fromBlock = Math.max(fromBlock, INITIAL_L2_BLOCK_NUM); - toBlock = Math.min(toBlock, this.l2Blocks.length + INITIAL_L2_BLOCK_NUM); - - if (fromBlock > this.l2Blocks.length || toBlock < fromBlock || toBlock <= 0) { - return Promise.resolve({ - logs: [], - maxLogsHit: false, - }); - } - - const contractAddress = filter.contractAddress; - - const logs: ExtendedPublicLog[] = []; - - for (; fromBlock < toBlock; fromBlock++) { - const block = this.l2Blocks[fromBlock - INITIAL_L2_BLOCK_NUM]; - const blockLogs = this.publicLogsPerBlock.get(fromBlock); - - if (blockLogs) { - for (let logIndex = 0; logIndex < blockLogs.length; logIndex++) { - const log = blockLogs[logIndex]; - const thisTxEffect = block.block.body.txEffects.filter(effect => effect.publicLogs.includes(log))[0]; - const thisTxIndexInBlock = block.block.body.txEffects.indexOf(thisTxEffect); - const thisLogIndexInTx = thisTxEffect.publicLogs.indexOf(log); - if ( - (!txHash || thisTxEffect.txHash.equals(txHash)) && - (!contractAddress || log.contractAddress.equals(contractAddress)) && - thisTxIndexInBlock >= txIndexInBlock && - thisLogIndexInTx >= logIndexInTx - ) { - logs.push(new ExtendedPublicLog(new LogId(block.block.number, thisTxIndexInBlock, thisLogIndexInTx), log)); - if (logs.length === this.maxLogs) { - return Promise.resolve({ - logs, - maxLogsHit: true, - }); - } - } - } - } - } - - return Promise.resolve({ - logs, - maxLogsHit: false, - }); - } - - /** - * Gets contract class logs based on the provided filter. - * NB: clone of the above fn, but for contract class logs - * @param filter - The filter to apply to the logs. - * @returns The requested logs. - * @remarks Works by doing an intersection of all params in the filter. - */ - getContractClassLogs(filter: LogFilter): Promise { - let txHash: TxHash | undefined; - let fromBlock = 0; - let toBlock = this.l2Blocks.length + INITIAL_L2_BLOCK_NUM; - let txIndexInBlock = 0; - let logIndexInTx = 0; - - if (filter.afterLog) { - // Continuation parameter is set --> tx hash is ignored - if (filter.fromBlock == undefined || filter.fromBlock <= filter.afterLog.blockNumber) { - fromBlock = filter.afterLog.blockNumber; - txIndexInBlock = filter.afterLog.txIndex; - logIndexInTx = filter.afterLog.logIndex + 1; // We want to start from the next log - } else { - fromBlock = filter.fromBlock; - } - } else { - txHash = filter.txHash; - - if (filter.fromBlock !== undefined) { - fromBlock = filter.fromBlock; - } - } - - if (filter.toBlock !== undefined) { - toBlock = filter.toBlock; - } - - // Ensure the indices are within block array bounds - fromBlock = Math.max(fromBlock, INITIAL_L2_BLOCK_NUM); - toBlock = Math.min(toBlock, this.l2Blocks.length + INITIAL_L2_BLOCK_NUM); - - if (fromBlock > this.l2Blocks.length || toBlock < fromBlock || toBlock <= 0) { - return Promise.resolve({ - logs: [], - maxLogsHit: false, - }); - } - - const contractAddress = filter.contractAddress; - - const logs: ExtendedContractClassLog[] = []; - - for (; fromBlock < toBlock; fromBlock++) { - const block = this.l2Blocks[fromBlock - INITIAL_L2_BLOCK_NUM]; - const blockLogs = this.contractClassLogsPerBlock.get(fromBlock); - - if (blockLogs) { - for (let logIndex = 0; logIndex < blockLogs.length; logIndex++) { - const log = blockLogs[logIndex]; - const thisTxEffect = block.block.body.txEffects.filter(effect => effect.contractClassLogs.includes(log))[0]; - const thisTxIndexInBlock = block.block.body.txEffects.indexOf(thisTxEffect); - const thisLogIndexInTx = thisTxEffect.contractClassLogs.indexOf(log); - if ( - (!txHash || thisTxEffect.txHash.equals(txHash)) && - (!contractAddress || log.contractAddress.equals(contractAddress)) && - thisTxIndexInBlock >= txIndexInBlock && - thisLogIndexInTx >= logIndexInTx - ) { - logs.push( - new ExtendedContractClassLog(new LogId(block.block.number, thisTxIndexInBlock, thisLogIndexInTx), log), - ); - if (logs.length === this.maxLogs) { - return Promise.resolve({ - logs, - maxLogsHit: true, - }); - } - } - } - } - } - - return Promise.resolve({ - logs, - maxLogsHit: false, - }); - } - - getLastBlockNumber(): number { - if (this.l2Blocks.length === 0) { - return INITIAL_L2_BLOCK_NUM - 1; - } - return this.l2Blocks[this.l2Blocks.length - 1].block.number; - } - - /** - * Gets the number of the latest L2 block processed. - * @returns The number of the latest L2 block processed. - */ - public getSynchedL2BlockNumber(): Promise { - return Promise.resolve(this.getLastBlockNumber()); - } - - public getProvenL2BlockNumber(): Promise { - return Promise.resolve(this.lastProvenL2BlockNumber); - } - - public setProvenL2BlockNumber(l2BlockNumber: number): Promise { - this.lastProvenL2BlockNumber = l2BlockNumber; - return Promise.resolve(); - } - - setBlockSynchedL1BlockNumber(l1BlockNumber: bigint) { - this.lastL1BlockNewBlocks = l1BlockNumber; - return Promise.resolve(); - } - - setMessageSynchedL1BlockNumber(l1BlockNumber: bigint) { - this.lastL1BlockNewMessages = l1BlockNumber; - return Promise.resolve(); - } - - public getSynchPoint(): Promise { - return Promise.resolve({ - blocksSynchedTo: this.lastL1BlockNewBlocks, - messagesSynchedTo: this.lastL1BlockNewMessages, - }); - } - - public getContractFunctionName(_address: AztecAddress, selector: FunctionSelector): Promise { - return Promise.resolve(this.functionNames.get(selector.toString())); - } - - public async registerContractFunctionSignatures(_address: AztecAddress, signatures: string[]): Promise { - for (const sig of signatures) { - try { - const selector = await FunctionSelector.fromSignature(sig); - this.functionNames.set(selector.toString(), sig.slice(0, sig.indexOf('('))); - } catch { - this.#log.warn(`Failed to parse signature: ${sig}. Ignoring`); - } - } - } - - public estimateSize(): Promise<{ mappingSize: number; actualSize: number; numItems: number }> { - return Promise.resolve({ mappingSize: 0, actualSize: 0, numItems: 0 }); - } -} diff --git a/yarn-project/constants/src/constants.gen.ts b/yarn-project/constants/src/constants.gen.ts index 037a0aa4e2cd..95be108b37d3 100644 --- a/yarn-project/constants/src/constants.gen.ts +++ b/yarn-project/constants/src/constants.gen.ts @@ -92,11 +92,16 @@ export const MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS = 3000; export const MAX_PACKED_BYTECODE_SIZE_PER_UNCONSTRAINED_FUNCTION_IN_FIELDS = 3000; export const REGISTERER_PRIVATE_FUNCTION_BROADCASTED_ADDITIONAL_FIELDS = 19; export const REGISTERER_UNCONSTRAINED_FUNCTION_BROADCASTED_ADDITIONAL_FIELDS = 12; -export const REGISTERER_CONTRACT_CLASS_REGISTERED_MAGIC_VALUE = 11121068431693264234253912047066709627593769337094408533543930778360n; -export const REGISTERER_PRIVATE_FUNCTION_BROADCASTED_MAGIC_VALUE = 2889881020989534926461066592611988634597302675057895885580456197069n; -export const REGISTERER_UNCONSTRAINED_FUNCTION_BROADCASTED_MAGIC_VALUE = 24399338136397901754495080759185489776044879232766421623673792970137n; -export const DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE = 14061769416655647708490531650437236735160113654556896985372298487345n; -export const DEPLOYER_CONTRACT_INSTANCE_UPDATED_MAGIC_VALUE = 1534834688047131268740281708431107902615560100979874281215533519862n; +export const REGISTERER_CONTRACT_CLASS_REGISTERED_MAGIC_VALUE = + 11121068431693264234253912047066709627593769337094408533543930778360n; +export const REGISTERER_PRIVATE_FUNCTION_BROADCASTED_MAGIC_VALUE = + 2889881020989534926461066592611988634597302675057895885580456197069n; +export const REGISTERER_UNCONSTRAINED_FUNCTION_BROADCASTED_MAGIC_VALUE = + 24399338136397901754495080759185489776044879232766421623673792970137n; +export const DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE = + 14061769416655647708490531650437236735160113654556896985372298487345n; +export const DEPLOYER_CONTRACT_INSTANCE_UPDATED_MAGIC_VALUE = + 1534834688047131268740281708431107902615560100979874281215533519862n; export const MAX_PROTOCOL_CONTRACTS = 7; export const CANONICAL_AUTH_REGISTRY_ADDRESS = 1; export const DEPLOYER_CONTRACT_ADDRESS = 2; @@ -411,4 +416,4 @@ export enum GeneratorIndex { SYMMETRIC_KEY_2 = 55, PUBLIC_TX_HASH = 56, PRIVATE_TX_HASH = 57, -} \ No newline at end of file +}