diff --git a/docs/docs/migration_notes.md b/docs/docs/migration_notes.md index a73799aaf364..986ccef45b1a 100644 --- a/docs/docs/migration_notes.md +++ b/docs/docs/migration_notes.md @@ -8,6 +8,14 @@ Aztec is in full-speed development. Literally every version breaks compatibility ## TBD +## [aztec.js] AztecNode.findLeavesIndexes returns indexes with block metadata + +It's common that we need block metadata of a block in which leaves where inserted when querying indexes of these tree leaves. +For this reason we now return that information along with the indexes. +This allows us to reduce the number of individual AztecNode queries. + +Along this change `findNullifiersIndexesWithBlock` and `findBlockNumbersForIndexes` functions wer removed as all its uses can now be replaced with the newly modified `findLeavesIndexes` function. + ## [aztec.js] AztecNode.getPublicDataTreeWitness renamed as AztecNode.getPublicDataWitness This change was done to have consistent naming across codebase. @@ -16,7 +24,7 @@ This change was done to have consistent naming across codebase. The `Wallet` interface in `aztec.js` is undergoing transformations, trying to be friendlier to wallet builders and reducing the surface of its API. This means `Wallet` no longer extends `PXE`, and instead just implements a subset of the methods of the former. This is NOT going to be its final form, but paves the way towards better interfaces and starts to clarify what the responsibilities of the wallet are: -``` typescript +```typescript /** * The wallet interface. */ @@ -24,34 +32,34 @@ export type Wallet = AccountInterface & Pick< PXE, // Simulation - | 'simulateTx' - | 'simulateUnconstrained' - | 'profileTx' + | "simulateTx" + | "simulateUnconstrained" + | "profileTx" // Sending - | 'sendTx' + | "sendTx" // Contract management (will probably be collapsed in the future to avoid instance and class versions) - | 'getContractClassMetadata' - | 'getContractMetadata' - | 'registerContract' - | 'registerContractClass' + | "getContractClassMetadata" + | "getContractMetadata" + | "registerContract" + | "registerContractClass" // Likely to be removed - | 'proveTx' + | "proveTx" // Will probably be collapsed - | 'getNodeInfo' - | 'getPXEInfo' + | "getNodeInfo" + | "getPXEInfo" // Fee info - | 'getCurrentBaseFees' + | "getCurrentBaseFees" // Still undecided, kept for the time being - | 'updateContract' + | "updateContract" // Sender management - | 'registerSender' - | 'getSenders' - | 'removeSender' + | "registerSender" + | "getSenders" + | "removeSender" // Tx status - | 'getTxReceipt' + | "getTxReceipt" // Events. Kept since events are going to be reworked and changes will come when that's done - | 'getPrivateEvents' - | 'getPublicEvents' + | "getPrivateEvents" + | "getPublicEvents" > & { createAuthWit(intent: IntentInnerHash | IntentAction): Promise; }; @@ -98,7 +106,6 @@ const witness = await wallet.createAuthWit({ caller, action }); ++await wallet.lookupValidity(wallet.getAddress(), { caller, action }, witness); ``` - ### [PXE] Concurrent contract function simulation disabled PXE is no longer be able to execute contract functions concurrently (e.g. by collecting calls to `simulateTx` and then using `await Promise.all`). They will instead be put in a job queue and executed sequentially in order of arrival. diff --git a/yarn-project/archiver/src/archiver/archiver.ts b/yarn-project/archiver/src/archiver/archiver.ts index 4c0b00a9d88c..4a840ff35b9d 100644 --- a/yarn-project/archiver/src/archiver/archiver.ts +++ b/yarn-project/archiver/src/archiver/archiver.ts @@ -25,7 +25,6 @@ import { type L2BlockSource, L2BlockSourceEvents, type L2Tips, - type NullifierWithBlockSource, } from '@aztec/stdlib/block'; import { type ContractClassPublic, @@ -67,11 +66,7 @@ import type { PublishedL2Block } from './structs/published.js'; /** * Helper interface to combine all sources this archiver implementation provides. */ -export type ArchiveSource = L2BlockSource & - L2LogsSource & - ContractDataSource & - L1ToL2MessageSource & - NullifierWithBlockSource; +export type ArchiveSource = L2BlockSource & L2LogsSource & ContractDataSource & L1ToL2MessageSource; /** * Pulls L2 blocks in a non-blocking manner and provides interface for their retrieval. @@ -734,17 +729,6 @@ export class Archiver extends EventEmitter implements ArchiveSource, Traceable { return this.store.getLogsByTags(tags); } - /** - * Returns the provided nullifier indexes scoped to the block - * they were first included in, or undefined if they're not present in the tree - * @param blockNumber Max block number to search for the nullifiers - * @param nullifiers Nullifiers to get - * @returns The block scoped indexes of the provided nullifiers, or undefined if the nullifier doesn't exist in the tree - */ - findNullifiersIndexesWithBlock(blockNumber: number, nullifiers: Fr[]): Promise<(InBlock | undefined)[]> { - return this.store.findNullifiersIndexesWithBlock(blockNumber, nullifiers); - } - /** * Gets public logs based on the provided filter. * @param filter - The filter to apply to the logs. @@ -883,8 +867,6 @@ class ArchiverStoreHelper ArchiverDataStore, | 'addLogs' | 'deleteLogs' - | 'addNullifiers' - | 'deleteNullifiers' | 'addContractClasses' | 'deleteContractClasses' | 'addContractInstances' @@ -1050,7 +1032,6 @@ class ArchiverStoreHelper ]) ).every(Boolean); }), - this.store.addNullifiers(blocks.map(block => block.block)), this.store.addBlocks(blocks), ]); @@ -1117,9 +1098,6 @@ class ArchiverStoreHelper getLogsByTags(tags: Fr[]): Promise { return this.store.getLogsByTags(tags); } - findNullifiersIndexesWithBlock(blockNumber: number, nullifiers: Fr[]): Promise<(InBlock | undefined)[]> { - return this.store.findNullifiersIndexesWithBlock(blockNumber, nullifiers); - } getPublicLogs(filter: LogFilter): Promise { return this.store.getPublicLogs(filter); } diff --git a/yarn-project/archiver/src/archiver/archiver_store.ts b/yarn-project/archiver/src/archiver/archiver_store.ts index 2e2a817018bc..4e1cbe4f3b9b 100644 --- a/yarn-project/archiver/src/archiver/archiver_store.ts +++ b/yarn-project/archiver/src/archiver/archiver_store.ts @@ -88,23 +88,6 @@ export interface ArchiverDataStore { addLogs(blocks: L2Block[]): Promise; deleteLogs(blocks: L2Block[]): Promise; - /** - * Append new nullifiers to the store's list. - * @param blocks - The blocks for which to add the nullifiers. - * @returns True if the operation is successful. - */ - addNullifiers(blocks: L2Block[]): Promise; - deleteNullifiers(blocks: L2Block[]): Promise; - - /** - * Returns the provided nullifier indexes scoped to the block - * they were first included in, or undefined if they're not present in the tree - * @param blockNumber Max block number to search for the nullifiers - * @param nullifiers Nullifiers to get - * @returns The block scoped indexes of the provided nullifiers, or undefined if the nullifier doesn't exist in the tree - */ - findNullifiersIndexesWithBlock(blockNumber: number, nullifiers: Fr[]): Promise<(InBlock | undefined)[]>; - /** * 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. diff --git a/yarn-project/archiver/src/archiver/archiver_store_test_suite.ts b/yarn-project/archiver/src/archiver/archiver_store_test_suite.ts index 350bc2ebb09a..cd4ced6ea3ce 100644 --- a/yarn-project/archiver/src/archiver/archiver_store_test_suite.ts +++ b/yarn-project/archiver/src/archiver/archiver_store_test_suite.ts @@ -1,7 +1,6 @@ import { INITIAL_L2_BLOCK_NUM, L1_TO_L2_MSG_SUBTREE_HEIGHT, - MAX_NULLIFIERS_PER_TX, PRIVATE_LOG_SIZE_IN_FIELDS, PUBLIC_LOG_DATA_SIZE_IN_FIELDS, } from '@aztec/constants'; @@ -776,58 +775,5 @@ export function describeArchiverDataStore( } }); }); - - describe('findNullifiersIndexesWithBlock', () => { - let blocks: L2Block[]; - const numBlocks = 10; - const nullifiersPerBlock = new Map(); - - beforeEach(async () => { - blocks = await timesParallel(numBlocks, (index: number) => L2Block.random(index + 1, 1)); - - blocks.forEach((block, blockIndex) => { - nullifiersPerBlock.set( - blockIndex, - block.body.txEffects.flatMap(txEffect => txEffect.nullifiers), - ); - }); - }); - - it('returns wrapped nullifiers with blocks if they exist', async () => { - await store.addNullifiers(blocks); - const nullifiersToRetrieve = [...nullifiersPerBlock.get(0)!, ...nullifiersPerBlock.get(5)!, Fr.random()]; - const blockScopedNullifiers = await store.findNullifiersIndexesWithBlock(10, nullifiersToRetrieve); - - expect(blockScopedNullifiers).toHaveLength(nullifiersToRetrieve.length); - const [undefinedNullifier] = blockScopedNullifiers.slice(-1); - const realNullifiers = blockScopedNullifiers.slice(0, -1); - realNullifiers.forEach((blockScopedNullifier, index) => { - expect(blockScopedNullifier).not.toBeUndefined(); - const { data, l2BlockNumber } = blockScopedNullifier!; - expect(data).toEqual(expect.any(BigInt)); - expect(l2BlockNumber).toEqual(index < MAX_NULLIFIERS_PER_TX ? 1 : 6); - }); - expect(undefinedNullifier).toBeUndefined(); - }); - - it('returns wrapped nullifiers filtering by blockNumber', async () => { - await store.addNullifiers(blocks); - const nullifiersToRetrieve = [...nullifiersPerBlock.get(0)!, ...nullifiersPerBlock.get(5)!]; - const blockScopedNullifiers = await store.findNullifiersIndexesWithBlock(5, nullifiersToRetrieve); - - expect(blockScopedNullifiers).toHaveLength(nullifiersToRetrieve.length); - const undefinedNullifiers = blockScopedNullifiers.slice(-MAX_NULLIFIERS_PER_TX); - const realNullifiers = blockScopedNullifiers.slice(0, -MAX_NULLIFIERS_PER_TX); - realNullifiers.forEach(blockScopedNullifier => { - expect(blockScopedNullifier).not.toBeUndefined(); - const { data, l2BlockNumber } = blockScopedNullifier!; - expect(data).toEqual(expect.any(BigInt)); - expect(l2BlockNumber).toEqual(1); - }); - undefinedNullifiers.forEach(undefinedNullifier => { - expect(undefinedNullifier).toBeUndefined(); - }); - }); - }); }); } diff --git a/yarn-project/archiver/src/archiver/kv_archiver_store/kv_archiver_store.ts b/yarn-project/archiver/src/archiver/kv_archiver_store/kv_archiver_store.ts index 7540efddd433..bd5975bc05ab 100644 --- a/yarn-project/archiver/src/archiver/kv_archiver_store/kv_archiver_store.ts +++ b/yarn-project/archiver/src/archiver/kv_archiver_store/kv_archiver_store.ts @@ -4,7 +4,7 @@ import { createLogger } from '@aztec/foundation/log'; import type { AztecAsyncKVStore, StoreSize } from '@aztec/kv-store'; import { FunctionSelector } from '@aztec/stdlib/abi'; import type { AztecAddress } from '@aztec/stdlib/aztec-address'; -import type { InBlock, L2Block } from '@aztec/stdlib/block'; +import type { L2Block } from '@aztec/stdlib/block'; import type { ContractClassPublic, ContractInstanceUpdateWithAddress, @@ -25,7 +25,6 @@ import { ContractClassStore } from './contract_class_store.js'; import { ContractInstanceStore } from './contract_instance_store.js'; import { LogStore } from './log_store.js'; import { MessageStore } from './message_store.js'; -import { NullifierStore } from './nullifier_store.js'; /** * LMDB implementation of the ArchiverDataStore interface. @@ -35,7 +34,6 @@ export class KVArchiverDataStore implements ArchiverDataStore { #blockStore: BlockStore; #logStore: LogStore; - #nullifierStore: NullifierStore; #messageStore: MessageStore; #contractClassStore: ContractClassStore; #contractInstanceStore: ContractInstanceStore; @@ -49,7 +47,6 @@ export class KVArchiverDataStore implements ArchiverDataStore { this.#messageStore = new MessageStore(db); this.#contractClassStore = new ContractClassStore(db); this.#contractInstanceStore = new ContractInstanceStore(db); - this.#nullifierStore = new NullifierStore(db); } // TODO: These function names are in memory only as they are for development/debugging. They require the full contract @@ -215,23 +212,6 @@ export class KVArchiverDataStore implements ArchiverDataStore { return this.#logStore.deleteLogs(blocks); } - /** - * Append new nullifiers to the store's list. - * @param blocks - The blocks for which to add the nullifiers. - * @returns True if the operation is successful. - */ - addNullifiers(blocks: L2Block[]): Promise { - return this.#nullifierStore.addNullifiers(blocks); - } - - deleteNullifiers(blocks: L2Block[]): Promise { - return this.#nullifierStore.deleteNullifiers(blocks); - } - - findNullifiersIndexesWithBlock(blockNumber: number, nullifiers: Fr[]): Promise<(InBlock | undefined)[]> { - return this.#nullifierStore.findNullifiersIndexesWithBlock(blockNumber, nullifiers); - } - getTotalL1ToL2MessageCount(): Promise { return this.#messageStore.getTotalL1ToL2MessageCount(); } diff --git a/yarn-project/archiver/src/archiver/kv_archiver_store/nullifier_store.ts b/yarn-project/archiver/src/archiver/kv_archiver_store/nullifier_store.ts deleted file mode 100644 index caac01c7d362..000000000000 --- a/yarn-project/archiver/src/archiver/kv_archiver_store/nullifier_store.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { MAX_NULLIFIERS_PER_TX } from '@aztec/constants'; -import type { Fr } from '@aztec/foundation/fields'; -import { createLogger } from '@aztec/foundation/log'; -import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store'; -import type { InBlock, L2Block } from '@aztec/stdlib/block'; - -export class NullifierStore { - #nullifiersToBlockNumber: AztecAsyncMap; - #nullifiersToBlockHash: AztecAsyncMap; - #nullifiersToIndex: AztecAsyncMap; - #log = createLogger('archiver:log_store'); - - constructor(private db: AztecAsyncKVStore) { - this.#nullifiersToBlockNumber = db.openMap('archiver_nullifiers_to_block_number'); - this.#nullifiersToBlockHash = db.openMap('archiver_nullifiers_to_block_hash'); - this.#nullifiersToIndex = db.openMap('archiver_nullifiers_to_index'); - } - - async addNullifiers(blocks: L2Block[]): Promise { - const blockHashes = await Promise.all(blocks.map(block => block.hash())); - await this.db.transactionAsync(async () => { - await Promise.all( - blocks.map((block, i) => { - const dataStartIndexForBlock = - block.header.state.partial.nullifierTree.nextAvailableLeafIndex - - block.body.txEffects.length * MAX_NULLIFIERS_PER_TX; - return Promise.all( - block.body.txEffects.map((txEffects, txIndex) => { - const dataStartIndexForTx = dataStartIndexForBlock + txIndex * MAX_NULLIFIERS_PER_TX; - return Promise.all( - txEffects.nullifiers.map(async (nullifier, nullifierIndex) => { - await this.#nullifiersToBlockNumber.set(nullifier.toString(), block.number); - await this.#nullifiersToBlockHash.set(nullifier.toString(), blockHashes[i].toString()); - await this.#nullifiersToIndex.set(nullifier.toString(), dataStartIndexForTx + nullifierIndex); - }), - ); - }), - ); - }), - ); - }); - return true; - } - - async deleteNullifiers(blocks: L2Block[]): Promise { - await this.db.transactionAsync(async () => { - for (const block of blocks) { - for (const nullifier of block.body.txEffects.flatMap(tx => tx.nullifiers)) { - await Promise.all([ - this.#nullifiersToBlockNumber.delete(nullifier.toString()), - this.#nullifiersToBlockHash.delete(nullifier.toString()), - this.#nullifiersToIndex.delete(nullifier.toString()), - ]); - } - } - }); - return true; - } - - async findNullifiersIndexesWithBlock( - blockNumber: number, - nullifiers: Fr[], - ): Promise<(InBlock | undefined)[]> { - const asStrings = nullifiers.map(x => x.toString()); - - const maybeNullifiers = await Promise.all( - asStrings.map(async nullifier => { - const [data, l2BlockNumber, l2BlockHash] = await Promise.all([ - this.#nullifiersToIndex.getAsync(nullifier), - this.#nullifiersToBlockNumber.getAsync(nullifier), - this.#nullifiersToBlockHash.getAsync(nullifier), - ]); - return { - data, - l2BlockNumber, - l2BlockHash, - }; - }), - ); - return maybeNullifiers.map(({ data, l2BlockNumber, l2BlockHash }) => { - if ( - data === undefined || - l2BlockNumber === undefined || - l2BlockHash === undefined || - l2BlockNumber > blockNumber - ) { - return undefined; - } else { - return { - data: BigInt(data), - l2BlockNumber, - l2BlockHash, - } as InBlock; - } - }); - } -} diff --git a/yarn-project/aztec-node/src/aztec-node/server.test.ts b/yarn-project/aztec-node/src/aztec-node/server.test.ts index c664d2e64ce7..f93a4c08f03a 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.test.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.test.ts @@ -5,7 +5,7 @@ import type { P2P } from '@aztec/p2p'; import { computeFeePayerBalanceLeafSlot } from '@aztec/protocol-contracts/fee-juice'; import type { GlobalVariableBuilder } from '@aztec/sequencer-client'; import { AztecAddress } from '@aztec/stdlib/aztec-address'; -import type { L2BlockSource, NullifierWithBlockSource } from '@aztec/stdlib/block'; +import type { L2BlockSource } from '@aztec/stdlib/block'; import type { ContractDataSource } from '@aztec/stdlib/contract'; import { GasFees } from '@aztec/stdlib/gas'; import type { AztecNode } from '@aztec/stdlib/interfaces/client'; @@ -95,8 +95,6 @@ describe('aztec node', () => { // all txs use the same allowed FPC class const contractSource = mock(); - const nullifierWithBlockSource = mock(); - const aztecNodeConfig: AztecNodeConfig = getConfigEnvVars(); node = new AztecNodeService( @@ -115,7 +113,6 @@ describe('aztec node', () => { l2LogsSource, contractSource, l1ToL2MessageSource, - nullifierWithBlockSource, worldState, undefined, 12345, diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index fa993c710498..2a466675ef22 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -33,14 +33,7 @@ import { } from '@aztec/sequencer-client'; import { PublicProcessorFactory } from '@aztec/simulator/server'; import { AztecAddress } from '@aztec/stdlib/aztec-address'; -import type { - InBlock, - L2Block, - L2BlockNumber, - L2BlockSource, - NullifierWithBlockSource, - PublishedL2Block, -} from '@aztec/stdlib/block'; +import type { InBlock, L2Block, L2BlockNumber, L2BlockSource, PublishedL2Block } from '@aztec/stdlib/block'; import type { ContractClassPublic, ContractDataSource, @@ -114,7 +107,6 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable { protected readonly logsSource: L2LogsSource, protected readonly contractDataSource: ContractDataSource, protected readonly l1ToL2MessageSource: L1ToL2MessageSource, - protected readonly nullifierSource: NullifierWithBlockSource, protected readonly worldStateSynchronizer: WorldStateSynchronizer, protected readonly sequencer: SequencerClient | undefined, protected readonly l1ChainId: number, @@ -231,7 +223,6 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable { archiver, archiver, archiver, - archiver, worldStateSynchronizer, sequencer, ethereumChain.chainInfo.id, @@ -520,48 +511,74 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable { } /** - * Find the indexes of the given leaves in the given tree. - * @param blockNumber - The block number at which to get the data or 'latest' for latest data + * Find the indexes of the given leaves in the given tree along with a block metadata pointing to the block in which + * the leaves were inserted. + * @param blockNumber - The block number at which to get the data or 'latest' for latest data. * @param treeId - The tree to search in. - * @param leafValue - The values to search for - * @returns The indexes of the given leaves in the given tree or undefined if not found. + * @param leafValues - The values to search for. + * @returns The indices of leaves and the block metadata of a block in which the leaves were inserted. */ public async findLeavesIndexes( blockNumber: L2BlockNumber, treeId: MerkleTreeId, leafValues: Fr[], - ): Promise<(bigint | undefined)[]> { + ): Promise<(InBlock | undefined)[]> { const committedDb = await this.#getWorldState(blockNumber); - return await committedDb.findLeafIndices( + const maybeIndices = await committedDb.findLeafIndices( treeId, leafValues.map(x => x.toBuffer()), ); - } + // We filter out undefined values + const indices = maybeIndices.filter(x => x !== undefined) as bigint[]; - /** - * Find the block numbers of the given leaf indices in the given tree. - * @param blockNumber - The block number at which to get the data or 'latest' for latest data - * @param treeId - The tree to search in. - * @param leafIndices - The values to search for - * @returns The indexes of the given leaves in the given tree or undefined if not found. - */ - public async findBlockNumbersForIndexes( - blockNumber: L2BlockNumber, - treeId: MerkleTreeId, - leafIndices: bigint[], - ): Promise<(bigint | undefined)[]> { - const committedDb = await this.#getWorldState(blockNumber); - return await committedDb.getBlockNumbersForLeafIndices(treeId, leafIndices); - } + // Now we find the block numbers for the indices + const blockNumbers = await committedDb.getBlockNumbersForLeafIndices(treeId, indices); - public async findNullifiersIndexesWithBlock( - blockNumber: L2BlockNumber, - nullifiers: Fr[], - ): Promise<(InBlock | undefined)[]> { - if (blockNumber === 'latest') { - blockNumber = await this.getBlockNumber(); + // If any of the block numbers are undefined, we throw an error. + for (let i = 0; i < indices.length; i++) { + if (blockNumbers[i] === undefined) { + throw new Error(`Block number is undefined for leaf index ${indices[i]} in tree ${MerkleTreeId[treeId]}`); + } } - return this.nullifierSource.findNullifiersIndexesWithBlock(blockNumber, nullifiers); + + // Get unique block numbers in order to optimize num calls to getLeafValue function. + const uniqueBlockNumbers = [...new Set(blockNumbers.filter(x => x !== undefined))]; + + // Now we obtain the block hashes from the archive tree by calling await `committedDb.getLeafValue(treeId, index)` + // (note that block number corresponds to the leaf index in the archive tree). + const blockHashes = await Promise.all( + uniqueBlockNumbers.map(blockNumber => { + return committedDb.getLeafValue(MerkleTreeId.ARCHIVE, blockNumber!); + }), + ); + + // If any of the block hashes are undefined, we throw an error. + for (let i = 0; i < uniqueBlockNumbers.length; i++) { + if (blockHashes[i] === undefined) { + throw new Error(`Block hash is undefined for block number ${uniqueBlockNumbers[i]}`); + } + } + + // Create InBlock objects by combining indices, blockNumbers and blockHashes and return them. + return maybeIndices.map((index, i) => { + if (index === undefined) { + return undefined; + } + const blockNumber = blockNumbers[i]; + if (blockNumber === undefined) { + return undefined; + } + const blockHashIndex = uniqueBlockNumbers.indexOf(blockNumber); + const blockHash = blockHashes[blockHashIndex]?.toString(); + if (!blockHash) { + return undefined; + } + return { + l2BlockNumber: Number(blockNumber), + l2BlockHash: blockHash, + data: index, + }; + }); } /** diff --git a/yarn-project/constants/src/constants.gen.ts b/yarn-project/constants/src/constants.gen.ts index 8ee0172dfa83..d3ca12b891ce 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; @@ -412,4 +417,4 @@ export enum GeneratorIndex { SYMMETRIC_KEY_2 = 55, PUBLIC_TX_HASH = 56, PRIVATE_TX_HASH = 57, -} \ No newline at end of file +} diff --git a/yarn-project/end-to-end/src/e2e_epochs/epochs_test.ts b/yarn-project/end-to-end/src/e2e_epochs/epochs_test.ts index de75e0c097f1..de735095ec88 100644 --- a/yarn-project/end-to-end/src/e2e_epochs/epochs_test.ts +++ b/yarn-project/end-to-end/src/e2e_epochs/epochs_test.ts @@ -1,8 +1,7 @@ import { AztecNodeService } from '@aztec/aztec-node'; -import { Fr, type Logger, getTimestampRangeForEpoch, retryUntil, sleep } from '@aztec/aztec.js'; +import { Fr, type Logger, MerkleTreeId, getTimestampRangeForEpoch, retryUntil, sleep } from '@aztec/aztec.js'; import { RollupContract } from '@aztec/ethereum/contracts'; -import { ChainMonitor } from '@aztec/ethereum/test'; -import { DelayedTxUtils, type Delayer, waitUntilL1Timestamp } from '@aztec/ethereum/test'; +import { ChainMonitor, DelayedTxUtils, type Delayer, waitUntilL1Timestamp } from '@aztec/ethereum/test'; import { randomBytes } from '@aztec/foundation/crypto'; import { withLogNameSuffix } from '@aztec/foundation/log'; import { ProverNode, ProverNodePublisher } from '@aztec/prover-node'; @@ -11,7 +10,6 @@ import type { SequencerPublisher } from '@aztec/sequencer-client'; import type { TestSequencerClient } from '@aztec/sequencer-client/test'; import type { L2BlockNumber } from '@aztec/stdlib/block'; import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers'; -import { MerkleTreeId } from '@aztec/stdlib/trees'; import { join } from 'path'; import type { Hex, PublicClient } from 'viem'; @@ -209,7 +207,7 @@ export class EpochsTestContext { /** Verifies whether the given block number is found on the aztec node. */ public async verifyHistoricBlock(blockNumber: L2BlockNumber, expectedSuccess: boolean) { const result = await this.context.aztecNode - .findBlockNumbersForIndexes(blockNumber, MerkleTreeId.NULLIFIER_TREE, [0n]) + .findLeavesIndexes(blockNumber, MerkleTreeId.NULLIFIER_TREE, [Fr.ZERO]) .then(_ => true) .catch(_ => false); expect(result).toBe(expectedSuccess); diff --git a/yarn-project/end-to-end/src/e2e_pruned_blocks.test.ts b/yarn-project/end-to-end/src/e2e_pruned_blocks.test.ts index 51c174e18512..b4fd5ca9690f 100644 --- a/yarn-project/end-to-end/src/e2e_pruned_blocks.test.ts +++ b/yarn-project/end-to-end/src/e2e_pruned_blocks.test.ts @@ -85,7 +85,8 @@ describe('e2e_pruned_blocks', () => { // We now make a historical query for the leaf index at the block number in which this first note was created and // check that we get a valid result, which indirectly means that the queried block has not yet been pruned. expect( - (await aztecNode.findLeavesIndexes(firstMintReceipt.blockNumber!, MerkleTreeId.NOTE_HASH_TREE, [mintedNote!]))[0], + (await aztecNode.findLeavesIndexes(firstMintReceipt.blockNumber!, MerkleTreeId.NOTE_HASH_TREE, [mintedNote!]))[0]! + .data, ).toBeGreaterThan(0); // We now mine dummy blocks, mark them as proven and wait for the node to process them, which should result in older diff --git a/yarn-project/pxe/src/pxe_oracle_interface/pxe_oracle_interface.ts b/yarn-project/pxe/src/pxe_oracle_interface/pxe_oracle_interface.ts index a987af358fc7..cfd3ac12baf9 100644 --- a/yarn-project/pxe/src/pxe_oracle_interface/pxe_oracle_interface.ts +++ b/yarn-project/pxe/src/pxe_oracle_interface/pxe_oracle_interface.ts @@ -162,7 +162,7 @@ export class PXEOracleInterface implements ExecutionDataProvider { async #findLeafIndex(blockNumber: L2BlockNumber, treeId: MerkleTreeId, leafValue: Fr): Promise { const [leafIndex] = await this.aztecNode.findLeavesIndexes(blockNumber, treeId, [leafValue]); - return leafIndex; + return leafIndex?.data; } public async getMembershipWitness(blockNumber: number, treeId: MerkleTreeId, leafValue: Fr): Promise { @@ -655,26 +655,17 @@ export class PXEOracleInterface implements ExecutionDataProvider { // We store notes by their index in the global note hash tree, which has the convenient side effect of validating // note existence in said tree. - const [uniqueNoteHashTreeIndex] = await this.aztecNode.findLeavesIndexes( + const [uniqueNoteHashTreeIndexInBlock] = await this.aztecNode.findLeavesIndexes( syncedBlockNumber, MerkleTreeId.NOTE_HASH_TREE, [uniqueNoteHash], ); - if (uniqueNoteHashTreeIndex === undefined) { + if (uniqueNoteHashTreeIndexInBlock === undefined) { throw new Error( `Note hash ${noteHash} (uniqued as ${uniqueNoteHash}) is not present on the tree at block ${syncedBlockNumber} (from tx ${txHash})`, ); } - // TODO (#12550): findLeavesIndexes should probably return an InBlock, same as findNullifiersIndexesWithBlock. This - // would save us from having to then query the tx receipt in order to get the block hash and number. Note regardless - // that we're not validating that the note was indeed created in this tx (which would require inspecting the tx - // effects), so maybe what we really want is an InTx. - const txReceipt = await this.aztecNode.getTxReceipt(new TxHash(txHash)); - if (txReceipt === undefined) { - throw new Error(`Failed to fetch tx receipt for tx hash ${txHash} when searching for note hashes`); - } - const noteDao = new NoteDao( new Note(content), contractAddress, @@ -683,9 +674,9 @@ export class PXEOracleInterface implements ExecutionDataProvider { noteHash, siloedNullifier, new TxHash(txHash), - txReceipt.blockNumber!, - txReceipt.blockHash!.toString(), - uniqueNoteHashTreeIndex, + uniqueNoteHashTreeIndexInBlock?.l2BlockNumber, + uniqueNoteHashTreeIndexInBlock?.l2BlockHash, + uniqueNoteHashTreeIndexInBlock?.data, recipient, ); @@ -697,7 +688,9 @@ export class PXEOracleInterface implements ExecutionDataProvider { nullifier: noteDao.siloedNullifier.toString(), }); - const [nullifierIndex] = await this.aztecNode.findNullifiersIndexesWithBlock(syncedBlockNumber, [siloedNullifier]); + const [nullifierIndex] = await this.aztecNode.findLeavesIndexes(syncedBlockNumber, MerkleTreeId.NULLIFIER_TREE, [ + siloedNullifier, + ]); if (nullifierIndex !== undefined) { const { data: _, ...blockHashAndNum } = nullifierIndex; await this.noteDataProvider.removeNullifiedNotes([{ data: siloedNullifier, ...blockHashAndNum }], recipient); @@ -754,7 +747,11 @@ export class PXEOracleInterface implements ExecutionDataProvider { for (const recipient of await this.keyStore.getAccounts()) { const currentNotesForRecipient = await this.noteDataProvider.getNotes({ contractAddress, recipient }); const nullifiersToCheck = currentNotesForRecipient.map(note => note.siloedNullifier); - const nullifierIndexes = await this.aztecNode.findNullifiersIndexesWithBlock('latest', nullifiersToCheck); + const nullifierIndexes = await this.aztecNode.findLeavesIndexes( + 'latest', + MerkleTreeId.NULLIFIER_TREE, + nullifiersToCheck, + ); const foundNullifiers = nullifiersToCheck .map((nullifier, i) => { diff --git a/yarn-project/stdlib/src/block/index.ts b/yarn-project/stdlib/src/block/index.ts index 203efc67b244..92e893735ad6 100644 --- a/yarn-project/stdlib/src/block/index.ts +++ b/yarn-project/stdlib/src/block/index.ts @@ -1,6 +1,5 @@ export * from './l2_block.js'; export * from './l2_block_downloader/index.js'; -export * from './nullifier_with_block_source.js'; export * from './in_block.js'; export * from './body.js'; export * from './l2_block_number.js'; diff --git a/yarn-project/stdlib/src/block/nullifier_with_block_source.ts b/yarn-project/stdlib/src/block/nullifier_with_block_source.ts deleted file mode 100644 index d00f13e6c4fa..000000000000 --- a/yarn-project/stdlib/src/block/nullifier_with_block_source.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { Fr } from '@aztec/foundation/fields'; - -import type { InBlock } from './in_block.js'; - -export interface NullifierWithBlockSource { - findNullifiersIndexesWithBlock(blockNumber: number, nullifiers: Fr[]): Promise<(InBlock | undefined)[]>; -} diff --git a/yarn-project/stdlib/src/interfaces/archiver.test.ts b/yarn-project/stdlib/src/interfaces/archiver.test.ts index 3136a58bc032..2ff5f714a497 100644 --- a/yarn-project/stdlib/src/interfaces/archiver.test.ts +++ b/yarn-project/stdlib/src/interfaces/archiver.test.ts @@ -8,7 +8,7 @@ import omit from 'lodash.omit'; import type { ContractArtifact } from '../abi/abi.js'; import { FunctionSelector } from '../abi/function_selector.js'; import { AztecAddress } from '../aztec-address/index.js'; -import { type InBlock, randomInBlock } from '../block/in_block.js'; +import type { InBlock } from '../block/in_block.js'; import { L2Block } from '../block/l2_block.js'; import type { L2Tips } from '../block/l2_block_source.js'; import type { PublishedL2Block } from '../block/published_l2_block.js'; @@ -146,18 +146,6 @@ describe('ArchiverApiSchema', () => { }); }); - it('findNullifiersIndexesWithBlock', async () => { - const result = await context.client.findNullifiersIndexesWithBlock(1, [Fr.random(), Fr.random()]); - expect(result).toEqual([ - { - data: expect.any(BigInt), - l2BlockNumber: expect.any(Number), - l2BlockHash: expect.any(String), - }, - undefined, - ]); - }); - it('getPrivateLogs', async () => { const result = await context.client.getPrivateLogs(1, 1); expect(result).toEqual([expect.any(PrivateLog)]); @@ -320,13 +308,6 @@ class MockArchiver implements ArchiverApi { expect(blockNumber).toEqual(1); return Promise.resolve(`0x01`); } - findNullifiersIndexesWithBlock(blockNumber: number, nullifiers: Fr[]): Promise<(InBlock | undefined)[]> { - expect(blockNumber).toEqual(1); - expect(nullifiers).toHaveLength(2); - expect(nullifiers[0]).toBeInstanceOf(Fr); - expect(nullifiers[1]).toBeInstanceOf(Fr); - return Promise.resolve([randomInBlock(Fr.random().toBigInt()), undefined]); - } getPrivateLogs(_from: number, _limit: number): Promise { return Promise.resolve([PrivateLog.random()]); } diff --git a/yarn-project/stdlib/src/interfaces/archiver.ts b/yarn-project/stdlib/src/interfaces/archiver.ts index 01f3a89094c9..d41ade7f903d 100644 --- a/yarn-project/stdlib/src/interfaces/archiver.ts +++ b/yarn-project/stdlib/src/interfaces/archiver.ts @@ -5,7 +5,6 @@ import { z } from 'zod'; import { inBlockSchemaFor } from '../block/in_block.js'; import { L2Block } from '../block/l2_block.js'; import { type L2BlockSource, L2TipsSchema } from '../block/l2_block_source.js'; -import type { NullifierWithBlockSource } from '../block/nullifier_with_block_source.js'; import { PublishedL2BlockSchema } from '../block/published_l2_block.js'; import { ContractClassPublicSchema, @@ -26,7 +25,7 @@ import { GetContractClassLogsResponseSchema, GetPublicLogsResponseSchema } from import type { L2LogsSource } from './l2_logs_source.js'; export type ArchiverApi = Omit< - L2BlockSource & L2LogsSource & ContractDataSource & L1ToL2MessageSource & NullifierWithBlockSource, + L2BlockSource & L2LogsSource & ContractDataSource & L1ToL2MessageSource, 'start' | 'stop' >; @@ -61,10 +60,6 @@ export const ArchiverApiSchema: ApiSchemaFor = { .function() .args(z.array(schemas.Fr)) .returns(z.array(z.array(TxScopedL2Log.schema))), - findNullifiersIndexesWithBlock: z - .function() - .args(z.number(), z.array(schemas.Fr)) - .returns(z.array(optional(inBlockSchemaFor(schemas.BigInt)))), getPublicLogs: z.function().args(LogFilterSchema).returns(GetPublicLogsResponseSchema), getContractClassLogs: z.function().args(LogFilterSchema).returns(GetContractClassLogsResponseSchema), getContractClass: z.function().args(schemas.Fr).returns(ContractClassPublicSchema.optional()), diff --git a/yarn-project/stdlib/src/interfaces/aztec-node.test.ts b/yarn-project/stdlib/src/interfaces/aztec-node.test.ts index 1add6f7cb609..81eafdcbf2b6 100644 --- a/yarn-project/stdlib/src/interfaces/aztec-node.test.ts +++ b/yarn-project/stdlib/src/interfaces/aztec-node.test.ts @@ -18,7 +18,7 @@ import omit from 'lodash.omit'; import type { ContractArtifact } from '../abi/abi.js'; import { AztecAddress } from '../aztec-address/index.js'; -import { type InBlock, randomInBlock } from '../block/in_block.js'; +import type { InBlock } from '../block/in_block.js'; import { L2Block } from '../block/l2_block.js'; import type { L2Tips } from '../block/l2_block_source.js'; import type { PublishedL2Block } from '../block/published_l2_block.js'; @@ -91,20 +91,7 @@ describe('AztecNodeApiSchema', () => { it('findLeavesIndexes', async () => { const response = await context.client.findLeavesIndexes(1, MerkleTreeId.ARCHIVE, [Fr.random(), Fr.random()]); - expect(response).toEqual([1n, undefined]); - }); - - it('findBlockNumbersForIndexes', async () => { - const response = await context.client.findBlockNumbersForIndexes(1, MerkleTreeId.ARCHIVE, [5n, 58n]); - expect(response).toEqual([3n, 9n]); - }); - - it('findNullifiersIndexesWithBlock', async () => { - const response = await context.client.findNullifiersIndexesWithBlock(1, [Fr.random(), Fr.random()]); - expect(response).toEqual([ - { data: 1n, l2BlockNumber: expect.any(Number), l2BlockHash: expect.any(String) }, - undefined, - ]); + expect(response).toEqual([{ data: 1n, l2BlockNumber: 1, l2BlockHash: '0x01' }, undefined]); }); it('getNullifierSiblingPath', async () => { @@ -376,29 +363,11 @@ class MockAztecNode implements AztecNode { blockNumber: number | 'latest', treeId: MerkleTreeId, leafValues: Fr[], - ): Promise<(bigint | undefined)[]> { + ): Promise<(InBlock | undefined)[]> { expect(leafValues).toHaveLength(2); expect(leafValues[0]).toBeInstanceOf(Fr); expect(leafValues[1]).toBeInstanceOf(Fr); - return Promise.resolve([1n, undefined]); - } - - findBlockNumbersForIndexes( - _blockNumber: number | 'latest', - _treeId: MerkleTreeId, - leafIndices: bigint[], - ): Promise<(bigint | undefined)[]> { - expect(leafIndices).toEqual([5n, 58n]); - return Promise.resolve([3n, 9n]); - } - findNullifiersIndexesWithBlock( - blockNumber: number | 'latest', - nullifiers: Fr[], - ): Promise<(InBlock | undefined)[]> { - expect(nullifiers).toHaveLength(2); - expect(nullifiers[0]).toBeInstanceOf(Fr); - expect(nullifiers[1]).toBeInstanceOf(Fr); - return Promise.resolve([randomInBlock(1n), undefined]); + return Promise.resolve([{ data: 1n, l2BlockNumber: 1, l2BlockHash: '0x01' }, undefined]); } getNullifierSiblingPath( blockNumber: number | 'latest', diff --git a/yarn-project/stdlib/src/interfaces/aztec-node.ts b/yarn-project/stdlib/src/interfaces/aztec-node.ts index 7b931fe7a1a5..1daaaf2cf339 100644 --- a/yarn-project/stdlib/src/interfaces/aztec-node.ts +++ b/yarn-project/stdlib/src/interfaces/aztec-node.ts @@ -74,41 +74,17 @@ export interface AztecNode getWorldStateSyncStatus(): Promise; /** - * Find the indexes of the given leaves in the given tree. - * @param blockNumber - The block number at which to get the data or 'latest' for latest data + * Find the indexes of the given leaves in the given tree along with a block metadata pointing to the block in which + * the leaves were inserted. + * @param blockNumber - The block number at which to get the data or 'latest' for latest data. * @param treeId - The tree to search in. - * @param leafValues - The values to search for - * @returns The indexes of the given leaves in the given tree or undefined if not found. + * @param leafValues - The values to search for. + * @returns The indices of leaves and the block metadata of a block in which the leaves were inserted. */ findLeavesIndexes( blockNumber: L2BlockNumber, treeId: MerkleTreeId, leafValues: Fr[], - ): Promise<(bigint | undefined)[]>; - - /** - * Find the indexes of the given leaves in the given tree. - * @param blockNumber - The block number at which to get the data or 'latest' for latest data - * @param treeId - The tree to search in. - * @param leafIndices - The values to search for - * @returns The indexes of the given leaves in the given tree or undefined if not found. - */ - findBlockNumbersForIndexes( - blockNumber: L2BlockNumber, - treeId: MerkleTreeId, - leafIndices: bigint[], - ): Promise<(bigint | undefined)[]>; - - /** - * Returns the indexes of the given nullifiers in the nullifier tree, - * scoped to the block they were included in. - * @param blockNumber - The block number at which to get the data. - * @param nullifiers - The nullifiers to search for. - * @returns The block scoped indexes of the given nullifiers in the nullifier tree, or undefined if not found. - */ - findNullifiersIndexesWithBlock( - blockNumber: L2BlockNumber, - nullifiers: Fr[], ): Promise<(InBlock | undefined)[]>; /** @@ -226,7 +202,7 @@ export interface AztecNode * @param number - The block number being requested. * @returns The requested block. */ - getBlock(number: number): Promise; + getBlock(number: L2BlockNumber): Promise; /** * Fetches the current block number. @@ -446,16 +422,6 @@ export const AztecNodeApiSchema: ApiSchemaFor = { findLeavesIndexes: z .function() .args(L2BlockNumberSchema, z.nativeEnum(MerkleTreeId), z.array(schemas.Fr)) - .returns(z.array(optional(schemas.BigInt))), - - findBlockNumbersForIndexes: z - .function() - .args(L2BlockNumberSchema, z.nativeEnum(MerkleTreeId), z.array(schemas.BigInt)) - .returns(z.array(optional(schemas.BigInt))), - - findNullifiersIndexesWithBlock: z - .function() - .args(L2BlockNumberSchema, z.array(schemas.Fr)) .returns(z.array(optional(inBlockSchemaFor(schemas.BigInt)))), getNullifierSiblingPath: z @@ -502,7 +468,7 @@ export const AztecNodeApiSchema: ApiSchemaFor = { getPublicDataWitness: z.function().args(L2BlockNumberSchema, schemas.Fr).returns(PublicDataWitness.schema.optional()), - getBlock: z.function().args(z.number()).returns(L2Block.schema.optional()), + getBlock: z.function().args(L2BlockNumberSchema).returns(L2Block.schema.optional()), getBlockNumber: z.function().returns(z.number()), diff --git a/yarn-project/txe/src/node/txe_node.ts b/yarn-project/txe/src/node/txe_node.ts index 8dcbcd4868a4..52f5714dfa74 100644 --- a/yarn-project/txe/src/node/txe_node.ts +++ b/yarn-project/txe/src/node/txe_node.ts @@ -57,7 +57,6 @@ export class TXENode implements AztecNode { #logsByTags = new Map(); #txEffectsByTxHash = new Map>(); #txReceiptsByTxHash = new Map(); - #blockNumberToNullifiers = new Map(); #noteIndex = 0; #logger = createLogger('aztec:txe_node'); @@ -129,51 +128,6 @@ export class TXENode implements AztecNode { ); } - /** - * Returns the indexes of the given nullifiers in the nullifier tree, - * scoped to the block they were included in. - * @param blockNumber - The block number at which to get the data. - * @param nullifiers - The nullifiers to search for. - * @returns The block scoped indexes of the given nullifiers in the nullifier tree, or undefined if not found. - */ - async findNullifiersIndexesWithBlock( - blockNumber: L2BlockNumber, - nullifiers: Fr[], - ): Promise<(InBlock | undefined)[]> { - const parsedBlockNumber = blockNumber === 'latest' ? await this.getBlockNumber() : blockNumber; - - const nullifiersInBlock: Fr[] = []; - for (const [key, val] of this.#blockNumberToNullifiers.entries()) { - if (key <= parsedBlockNumber) { - nullifiersInBlock.push(...val); - } - } - - return nullifiers.map(nullifier => { - const possibleNullifierIndex = nullifiersInBlock.findIndex(nullifierInBlock => - nullifierInBlock.equals(nullifier), - ); - return possibleNullifierIndex === -1 - ? undefined - : { - l2BlockNumber: parsedBlockNumber, - l2BlockHash: new Fr(parsedBlockNumber).toString(), - data: BigInt(possibleNullifierIndex), - }; - }); - } - - /** - * Returns the indexes of the given nullifiers in the nullifier tree, - * scoped to the block they were included in. - * @param blockNumber - The block number at which to get the data. - * @param nullifiers - The nullifiers to search for. - * @returns The block scoped indexes of the given nullifiers in the nullifier tree, or undefined if not found. - */ - setNullifiersIndexesWithBlock(blockNumber: number, nullifiers: Fr[]) { - this.#blockNumberToNullifiers.set(blockNumber, nullifiers); - } - /** * Adds private logs to the txe node, given a block * @param blockNumber - The block number at which to add the private logs. @@ -234,13 +188,13 @@ export class TXENode implements AztecNode { * @param blockNumber - The block number at which to get the data or 'latest' for latest data * @param treeId - The tree to search in. * @param leafValue - The values to search for - * @returns The indexes of the given leaves in the given tree or undefined if not found. + * @returns The indices of leaves and the block metadata of a block in which the leaf was inserted. */ async findLeavesIndexes( blockNumber: L2BlockNumber, treeId: MerkleTreeId, leafValues: Fr[], - ): Promise<(bigint | undefined)[]> { + ): Promise<(InBlock | undefined)[]> { // Temporary workaround to be able to respond this query: the trees are currently stored in the TXE oracle, but we // hold a reference to them. // We should likely migrate this so that the trees are owned by the node. @@ -251,10 +205,61 @@ export class TXENode implements AztecNode { ? this.baseFork : this.nativeWorldStateService.getSnapshot(blockNumber); - return await db.findLeafIndices( + const maybeIndices = await db.findLeafIndices( treeId, leafValues.map(x => x.toBuffer()), ); + + // We filter out undefined values + const indices = maybeIndices.filter(x => x !== undefined) as bigint[]; + + // Now we find the block numbers for the indices + const blockNumbers = await db.getBlockNumbersForLeafIndices(treeId, indices); + + // If any of the block numbers are undefined, we throw an error. + for (let i = 0; i < indices.length; i++) { + if (blockNumbers[i] === undefined) { + throw new Error(`Block number is undefined for leaf index ${indices[i]} in tree ${MerkleTreeId[treeId]}`); + } + } + // Get unique block numbers in order to optimize num calls to getLeafValue function. + const uniqueBlockNumbers = [...new Set(blockNumbers.filter(x => x !== undefined))]; + + // Now we obtain the block hashes from the archive tree by calling await `committedDb.getLeafValue(treeId, index)` + // (note that block number corresponds to the leaf index in the archive tree). + const blockHashes = await Promise.all( + uniqueBlockNumbers.map(blockNumber => { + return db.getLeafValue(MerkleTreeId.ARCHIVE, blockNumber!); + }), + ); + + // If any of the block hashes are undefined, we throw an error. + for (let i = 0; i < uniqueBlockNumbers.length; i++) { + if (blockHashes[i] === undefined) { + throw new Error(`Block hash is undefined for block number ${uniqueBlockNumbers[i]}`); + } + } + + // Create InBlock objects by combining indices, blockNumbers and blockHashes + return maybeIndices.map((index, i) => { + if (index === undefined) { + return undefined; + } + const blockNumber = blockNumbers[i]; + if (blockNumber === undefined) { + return undefined; + } + const blockHashIndex = uniqueBlockNumbers.indexOf(blockNumber); + const blockHash = blockHashes[blockHashIndex]?.toString(); + if (!blockHash) { + return undefined; + } + return { + l2BlockNumber: Number(blockNumber), + l2BlockHash: blockHash, + data: index, + }; + }); } /** @@ -659,21 +664,6 @@ export class TXENode implements AztecNode { throw new Error('TXE Node method getPrivateLogs not implemented'); } - /** - * Find the block numbers of the given leaf indices in the given tree. - * @param blockNumber - The block number at which to get the data or 'latest' for latest data - * @param treeId - The tree to search in. - * @param leafIndices - The values to search for - * @returns The indexes of the given leaves in the given tree or undefined if not found. - */ - findBlockNumbersForIndexes( - _blockNumber: L2BlockNumber, - _treeId: MerkleTreeId, - _leafIndices: bigint[], - ): Promise<(bigint | undefined)[]> { - throw new Error('TXE Node method findBlockNumbersForIndexes not implemented'); - } - /** * Returns the information about the server's node. Includes current Node version, compatible Noir version, * L1 chain identifier, protocol version, and L1 address of the rollup contract. diff --git a/yarn-project/txe/src/oracle/txe_oracle.ts b/yarn-project/txe/src/oracle/txe_oracle.ts index f05b4f50fc9f..da550da30a86 100644 --- a/yarn-project/txe/src/oracle/txe_oracle.ts +++ b/yarn-project/txe/src/oracle/txe_oracle.ts @@ -756,7 +756,6 @@ export class TXE implements TypedOracle { } await this.node.setTxEffect(blockNumber, new TxHash(new Fr(blockNumber)), txEffect); - this.node.setNullifiersIndexesWithBlock(blockNumber, txEffect.nullifiers); this.node.addPrivateLogsByTags(this.blockNumber, this.privateLogs); this.node.addPublicLogsByTags(this.blockNumber, this.publicLogs);