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 40f3ae8b1984..a523c098303d 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 @@ -49,21 +49,21 @@ export class LogStore { const txHash = txEffect.txHash; const dataStartIndexForTx = dataStartIndexForBlock + txIndex * MAX_NOTE_HASHES_PER_TX; - txEffect.privateLogs.forEach(log => { + txEffect.privateLogs.forEach((log, logIndex) => { const tag = log.fields[0]; this.#log.debug(`Found private log with tag ${tag.toString()} in block ${block.number}`); const currentLogs = taggedLogs.get(tag.toString()) ?? []; - currentLogs.push(new TxScopedL2Log(txHash, dataStartIndexForTx, block.number, log).toBuffer()); + currentLogs.push(new TxScopedL2Log(txHash, dataStartIndexForTx, logIndex, block.number, log).toBuffer()); taggedLogs.set(tag.toString(), currentLogs); }); - txEffect.publicLogs.forEach(log => { + txEffect.publicLogs.forEach((log, logIndex) => { const tag = log.log[0]; this.#log.debug(`Found public log with tag ${tag.toString()} in block ${block.number}`); const currentLogs = taggedLogs.get(tag.toString()) ?? []; - currentLogs.push(new TxScopedL2Log(txHash, dataStartIndexForTx, block.number, log).toBuffer()); + currentLogs.push(new TxScopedL2Log(txHash, dataStartIndexForTx, logIndex, block.number, log).toBuffer()); taggedLogs.set(tag.toString(), currentLogs); }); }); diff --git a/yarn-project/pxe/src/pxe_oracle_interface/pxe_oracle_interface.test.ts b/yarn-project/pxe/src/pxe_oracle_interface/pxe_oracle_interface.test.ts index 788ed71166a4..0d37975016bc 100644 --- a/yarn-project/pxe/src/pxe_oracle_interface/pxe_oracle_interface.test.ts +++ b/yarn-project/pxe/src/pxe_oracle_interface/pxe_oracle_interface.test.ts @@ -103,7 +103,7 @@ describe('PXEOracleInterface', () => { for (const sender of senders) { const tag = await computeSiloedTagForIndex(sender, recipient.address, contractAddress, tagIndex); const blockNumber = 1; - const log = new TxScopedL2Log(TxHash.random(), 0, blockNumber, PrivateLog.random(tag)); + const log = new TxScopedL2Log(TxHash.random(), 0, 0, blockNumber, PrivateLog.random(tag)); logs[tag.toString()] = [log]; } // Accumulated logs intended for recipient: NUM_SENDERS @@ -112,7 +112,7 @@ describe('PXEOracleInterface', () => { // Compute the tag as sender (knowledge of preaddress and ivsk) const firstSender = senders[0]; const tag = await computeSiloedTagForIndex(firstSender, recipient.address, contractAddress, tagIndex); - const log = new TxScopedL2Log(TxHash.random(), 1, 0, PrivateLog.random(tag)); + const log = new TxScopedL2Log(TxHash.random(), 1, 0, 0, PrivateLog.random(tag)); logs[tag.toString()].push(log); // Accumulated logs intended for recipient: NUM_SENDERS + 1 @@ -122,7 +122,7 @@ describe('PXEOracleInterface', () => { const sender = senders[i]; const tag = await computeSiloedTagForIndex(sender, recipient.address, contractAddress, tagIndex + 1); const blockNumber = 2; - const log = new TxScopedL2Log(TxHash.random(), 0, blockNumber, PrivateLog.random(tag)); + const log = new TxScopedL2Log(TxHash.random(), 0, 0, blockNumber, PrivateLog.random(tag)); logs[tag.toString()] = [log]; } // Accumulated logs intended for recipient: NUM_SENDERS + 1 + NUM_SENDERS / 2 @@ -135,7 +135,7 @@ describe('PXEOracleInterface', () => { const randomRecipient = await computeAddress(keys.publicKeys, partialAddress); const tag = await computeSiloedTagForIndex(sender, randomRecipient, contractAddress, tagIndex); const blockNumber = 3; - const log = new TxScopedL2Log(TxHash.random(), 0, blockNumber, PrivateLog.random(tag)); + const log = new TxScopedL2Log(TxHash.random(), 0, 0, blockNumber, PrivateLog.random(tag)); logs[tag.toString()] = [log]; } // Accumulated logs intended for recipient: NUM_SENDERS + 1 + NUM_SENDERS / 2 @@ -406,7 +406,7 @@ describe('PXEOracleInterface', () => { typeof PUBLIC_LOG_DATA_SIZE_IN_FIELDS >; const log = new PublicLog(await AztecAddress.random(), logContent); - const scopedLog = new TxScopedL2Log(TxHash.random(), 1, 0, log); + const scopedLog = new TxScopedL2Log(TxHash.random(), 1, 0, 0, log); logs[tag.toString()] = [scopedLog]; aztecNode.getLogsByTags.mockImplementation(tags => { @@ -457,7 +457,7 @@ describe('PXEOracleInterface', () => { function mockTaggedLogs(numLogs: number) { return Array(numLogs) .fill(0) - .map(() => new TxScopedL2Log(TxHash.random(), 0, 0, PrivateLog.random(Fr.random()))); + .map(() => new TxScopedL2Log(TxHash.random(), 0, 0, 0, PrivateLog.random(Fr.random()))); } it('should call processLog on multiple logs', async () => { 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 42fc4624f855..6b6670c5c7ef 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 @@ -1,6 +1,5 @@ import { type L1_TO_L2_MSG_TREE_HEIGHT, MAX_NOTE_HASHES_PER_TX, PRIVATE_LOG_SIZE_IN_FIELDS } from '@aztec/constants'; import { timesParallel } from '@aztec/foundation/collection'; -import { randomInt } from '@aztec/foundation/crypto'; import { Fr, Point } from '@aztec/foundation/fields'; import { createLogger } from '@aztec/foundation/log'; import type { KeyStore } from '@aztec/key-store'; @@ -27,13 +26,7 @@ import { computeUniqueNoteHash, siloNoteHash, siloNullifier } from '@aztec/stdli import type { AztecNode } from '@aztec/stdlib/interfaces/client'; import type { KeyValidationRequest } from '@aztec/stdlib/kernel'; import { computeAddressSecret, computeAppTaggingSecret } from '@aztec/stdlib/keys'; -import { - IndexedTaggingSecret, - LogWithTxData, - PrivateLog, - TxScopedL2Log, - deriveEcdhSharedSecret, -} from '@aztec/stdlib/logs'; +import { IndexedTaggingSecret, LogWithTxData, TxScopedL2Log, deriveEcdhSharedSecret } from '@aztec/stdlib/logs'; import { getNonNullifiedL1ToL2MessageWitness } from '@aztec/stdlib/messaging'; import { Note, type NoteStatus } from '@aztec/stdlib/note'; import { MerkleTreeId, type NullifierMembershipWitness, PublicDataWitness } from '@aztec/stdlib/trees'; @@ -613,19 +606,6 @@ export class PXEOracleInterface implements ExecutionDataProvider { throw new Error(`Could not find tx effect for tx hash ${scopedLog.txHash}`); } - // TODO(#13155): Handle multiple found indexes for the same log. - let logIndexInTx = txEffect.data.privateLogs.findIndex(log => log.equals(scopedLog.log as PrivateLog)); - - // TODO(#13137): The following is a workaround to disable the logIndexInTx check for TXE tests as TXE currently - // returns nonsensical tx effects and the tx has is incremented from 0 up (so it never crosses a 1000). - if (scopedLog.txHash.toBigInt() < 1000n) { - logIndexInTx = randomInt(10); - } - - if (logIndexInTx === -1) { - throw new Error(`Could not find log in tx effect for tx hash ${scopedLog.txHash}`); - } - // This will trigger calls to the deliverNote oracle await this.callProcessLog( contractAddress, @@ -633,7 +613,7 @@ export class PXEOracleInterface implements ExecutionDataProvider { scopedLog.txHash, txEffect.data.noteHashes, txEffect.data.nullifiers[0], - logIndexInTx, + scopedLog.logIndexInTx, recipient, simulator, ); diff --git a/yarn-project/stdlib/src/logs/tx_scoped_l2_log.ts b/yarn-project/stdlib/src/logs/tx_scoped_l2_log.ts index 7b0df2d89a73..af8b24eded89 100644 --- a/yarn-project/stdlib/src/logs/tx_scoped_l2_log.ts +++ b/yarn-project/stdlib/src/logs/tx_scoped_l2_log.ts @@ -17,6 +17,11 @@ export class TxScopedL2Log { * with the log so the noteHashIndex can be reconstructed after decryption. */ public dataStartIndexForTx: number, + /* + * The index of the log in the transaction. Note that public and private logs are in separate arrays in the tx + * effect and for this reason these indices are independent (a private and public log can have the same index). + */ + public logIndexInTx: number, /* * The block this log is included in */ @@ -36,12 +41,13 @@ export class TxScopedL2Log { .object({ txHash: TxHash.schema, dataStartIndexForTx: z.number(), + logIndexInTx: z.number(), blockNumber: z.number(), log: z.union([PrivateLog.schema, PublicLog.schema]), }) .transform( - ({ txHash, dataStartIndexForTx, blockNumber, log }) => - new TxScopedL2Log(txHash, dataStartIndexForTx, blockNumber, log), + ({ txHash, dataStartIndexForTx, logIndexInTx, blockNumber, log }) => + new TxScopedL2Log(txHash, dataStartIndexForTx, logIndexInTx, blockNumber, log), ); } @@ -50,6 +56,7 @@ export class TxScopedL2Log { return Buffer.concat([ this.txHash.toBuffer(), numToUInt32BE(this.dataStartIndexForTx), + numToUInt32BE(this.logIndexInTx), numToUInt32BE(this.blockNumber), boolToBuffer(isFromPublic), this.log.toBuffer(), @@ -60,23 +67,25 @@ export class TxScopedL2Log { const reader = BufferReader.asReader(buffer); const txHash = reader.readObject(TxHash); const dataStartIndexForTx = reader.readNumber(); + const logIndexInTx = reader.readNumber(); const blockNumber = reader.readNumber(); const isFromPublic = reader.readBoolean(); const log = isFromPublic ? PublicLog.fromBuffer(reader) : PrivateLog.fromBuffer(reader); - return new TxScopedL2Log(txHash, dataStartIndexForTx, blockNumber, log); + return new TxScopedL2Log(txHash, dataStartIndexForTx, logIndexInTx, blockNumber, log); } static async random() { const isFromPublic = Math.random() < 0.5; const log = isFromPublic ? await PublicLog.random() : PrivateLog.random(); - return new TxScopedL2Log(TxHash.random(), 1, 1, log); + return new TxScopedL2Log(TxHash.random(), 1, 1, 1, log); } equals(other: TxScopedL2Log) { return ( this.txHash.equals(other.txHash) && this.dataStartIndexForTx === other.dataStartIndexForTx && + this.logIndexInTx === other.logIndexInTx && this.blockNumber === other.blockNumber && ((this.log instanceof PublicLog && other.log instanceof PublicLog) || (this.log instanceof PrivateLog && other.log instanceof PrivateLog)) && diff --git a/yarn-project/txe/src/node/txe_node.ts b/yarn-project/txe/src/node/txe_node.ts index 00dbca17b237..fd9bfe92ef97 100644 --- a/yarn-project/txe/src/node/txe_node.ts +++ b/yarn-project/txe/src/node/txe_node.ts @@ -35,7 +35,7 @@ import type { SequencerConfig, WorldStateSyncStatus, } from '@aztec/stdlib/interfaces/server'; -import { type LogFilter, type PrivateLog, type PublicLog, TxScopedL2Log } from '@aztec/stdlib/logs'; +import { type LogFilter, type PrivateLog, TxScopedL2Log } from '@aztec/stdlib/logs'; import { MerkleTreeId, type NullifierMembershipWitness, @@ -98,12 +98,12 @@ export class TXENode implements AztecNode { } /** - * Sets a tx effect and receipt for a given block number. + * Processes a tx effect and receipt for a given block number. * @param blockNumber - The block number that this tx effect resides. * @param txHash - The transaction hash of the transaction. * @param effect - The tx effect to set. */ - async setTxEffect(blockNumber: number, txHash: TxHash, effect: TxEffect) { + async processTxEffect(blockNumber: number, txHash: TxHash, effect: TxEffect) { // We are not creating real blocks on which membership proofs can be constructed - we instead define its hash as // simply the hash of the block number. const blockHash = await poseidon2Hash([blockNumber]); @@ -127,44 +127,45 @@ export class TXENode implements AztecNode { blockNumber, ), ); - } - /** - * Adds private logs to the txe node, given a block - * @param blockNumber - The block number at which to add the private logs. - * @param privateLogs - The privateLogs that contain the private logs to be added. - */ - addPrivateLogsByTags(blockNumber: number, privateLogs: PrivateLog[]) { - privateLogs.forEach(log => { + // Store the private logs + effect.privateLogs.forEach((log, logIndexInTx) => { const tag = log.fields[0]; this.#logger.verbose(`Found private log with tag ${tag.toString()} in block ${this.getBlockNumber()}`); const currentLogs = this.#logsByTags.get(tag.toString()) ?? []; - const scopedLog = new TxScopedL2Log(new TxHash(new Fr(blockNumber)), this.#noteIndex, blockNumber, log); + const scopedLog = new TxScopedL2Log( + new TxHash(new Fr(blockNumber)), + this.#noteIndex, + logIndexInTx, + blockNumber, + log, + ); currentLogs.push(scopedLog); this.#logsByTags.set(tag.toString(), currentLogs); }); - this.#noteIndex += privateLogs.length; - } + this.#noteIndex += effect.privateLogs.length; - /** - * Adds public logs to the txe node, given a block - * @param blockNumber - The block number at which to add the public logs. - * @param publicLogs - The public logs to be added. - */ - addPublicLogsByTags(blockNumber: number, publicLogs: PublicLog[]) { - publicLogs.forEach(log => { + // Store the public logs + effect.publicLogs.forEach((log, logIndexInTx) => { const tag = log.log[0]; this.#logger.verbose(`Found public log with tag ${tag.toString()} in block ${this.getBlockNumber()}`); const currentLogs = this.#logsByTags.get(tag.toString()) ?? []; - const scopedLog = new TxScopedL2Log(new TxHash(new Fr(blockNumber)), this.#noteIndex, blockNumber, log); + const scopedLog = new TxScopedL2Log( + new TxHash(new Fr(blockNumber)), + this.#noteIndex, + logIndexInTx, + blockNumber, + log, + ); currentLogs.push(scopedLog); this.#logsByTags.set(tag.toString(), currentLogs); }); } + /** * 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. diff --git a/yarn-project/txe/src/oracle/txe_oracle.ts b/yarn-project/txe/src/oracle/txe_oracle.ts index a2fb99936482..233224f9b39f 100644 --- a/yarn-project/txe/src/oracle/txe_oracle.ts +++ b/yarn-project/txe/src/oracle/txe_oracle.ts @@ -732,6 +732,8 @@ export class TXE implements TypedOracle { } txEffect.publicDataWrites = this.publicDataWrites; + txEffect.privateLogs = this.privateLogs; + txEffect.publicLogs = this.publicLogs; const body = new Body([txEffect]); @@ -771,9 +773,7 @@ export class TXE implements TypedOracle { } } - await this.node.setTxEffect(blockNumber, new TxHash(new Fr(blockNumber)), txEffect); - this.node.addPrivateLogsByTags(this.blockNumber, this.privateLogs); - this.node.addPublicLogsByTags(this.blockNumber, this.publicLogs); + await this.node.processTxEffect(blockNumber, new TxHash(new Fr(blockNumber)), txEffect); const stateReference = await fork.getStateReference(); const archiveInfo = await fork.getTreeInfo(MerkleTreeId.ARCHIVE);