From 56ed296ee7c200a6cfe139a03570449ee16b0183 Mon Sep 17 00:00:00 2001 From: thunkar Date: Thu, 31 Oct 2024 13:36:48 +0000 Subject: [PATCH 01/15] wip --- .../aztec-nr/aztec/src/oracle/notes.nr | 13 ++ .../archiver/src/archiver/archiver.ts | 6 +- .../archiver/src/archiver/archiver_store.ts | 3 +- .../src/archiver/archiver_store_test_suite.ts | 12 +- .../kv_archiver_store/kv_archiver_store.ts | 3 +- .../archiver/kv_archiver_store/log_store.ts | 21 +++- .../memory_archiver_store.ts | 18 ++- .../aztec-node/src/aztec-node/server.ts | 3 +- .../src/interfaces/aztec-node.ts | 3 +- .../src/logs/get_logs_response.ts | 37 ++++++ .../src/logs/get_unencrypted_logs_response.ts | 16 --- yarn-project/circuit-types/src/logs/index.ts | 2 +- .../circuit-types/src/logs/l2_logs_source.ts | 5 +- yarn-project/circuit-types/src/tx/tx.ts | 2 +- yarn-project/circuit-types/src/tx_effect.ts | 2 +- .../src/orchestrator/orchestrator.ts | 1 + .../pxe/src/pxe_service/pxe_service.ts | 7 +- .../pxe/src/simulator_oracle/index.ts | 112 +++++++++++++++++- .../simulator_oracle/simulator_oracle.test.ts | 12 +- .../src/client/client_execution_context.ts | 5 + .../simulator/src/client/db_oracle.ts | 5 + 21 files changed, 233 insertions(+), 55 deletions(-) create mode 100644 yarn-project/circuit-types/src/logs/get_logs_response.ts delete mode 100644 yarn-project/circuit-types/src/logs/get_unencrypted_logs_response.ts diff --git a/noir-projects/aztec-nr/aztec/src/oracle/notes.nr b/noir-projects/aztec-nr/aztec/src/oracle/notes.nr index 39ed994516f..42242b7dfef 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/notes.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/notes.nr @@ -243,3 +243,16 @@ pub unconstrained fn get_app_tagging_secrets_for_senders( #[oracle(getAppTaggingSecretsForSenders)] unconstrained fn get_app_tagging_secrets_for_senders_oracle(_recipient: AztecAddress) -> [Field] {} + +pub fn sync_notes(targetContractAddress: AztecAddress, recipient: AztecAddress) { + unsafe { + sync_notes_oracle_wrapper(targetContractAddress, recipient); + } +} + +unconstrained fn sync_notes_oracle_wrapper(targetContractAddress: AztecAddress, recipient: AztecAddress) { + sync_notes_oracle(targetContractAddress, recipient); +} + +#[oracle(syncNotes)] +unconstrained fn sync_notes_oracle(_targetContractAddress: AztecAddress, _recipient: AztecAddress) {} diff --git a/yarn-project/archiver/src/archiver/archiver.ts b/yarn-project/archiver/src/archiver/archiver.ts index a205a5d3881..7fb68148e31 100644 --- a/yarn-project/archiver/src/archiver/archiver.ts +++ b/yarn-project/archiver/src/archiver/archiver.ts @@ -1,5 +1,4 @@ import { - type EncryptedL2NoteLog, type FromLogType, type GetUnencryptedLogsResponse, type InboxLeaf, @@ -15,6 +14,7 @@ import { type TxEffect, type TxHash, type TxReceipt, + type TxScopedEncryptedL2NoteLog, type UnencryptedL2Log, } from '@aztec/circuit-types'; import { @@ -634,7 +634,7 @@ export class Archiver implements ArchiveSource { * @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 { + getLogsByTags(tags: Fr[]): Promise { return this.store.getLogsByTags(tags); } @@ -934,7 +934,7 @@ class ArchiverStoreHelper ): Promise>[]> { return this.store.getLogs(from, limit, logType); } - getLogsByTags(tags: Fr[]): Promise { + getLogsByTags(tags: Fr[]): Promise { return this.store.getLogsByTags(tags); } getUnencryptedLogs(filter: LogFilter): Promise { diff --git a/yarn-project/archiver/src/archiver/archiver_store.ts b/yarn-project/archiver/src/archiver/archiver_store.ts index 9128b33db44..b1d2c6307ad 100644 --- a/yarn-project/archiver/src/archiver/archiver_store.ts +++ b/yarn-project/archiver/src/archiver/archiver_store.ts @@ -10,6 +10,7 @@ import { type TxEffect, type TxHash, type TxReceipt, + TxScopedEncryptedL2NoteLog, } from '@aztec/circuit-types'; import { type ContractClassPublic, @@ -142,7 +143,7 @@ export interface ArchiverDataStore { * @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; + getLogsByTags(tags: Fr[]): Promise; /** * Gets unencrypted logs based on the provided filter. 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 a41f01ab6e9..1311c6ec03e 100644 --- a/yarn-project/archiver/src/archiver/archiver_store_test_suite.ts +++ b/yarn-project/archiver/src/archiver/archiver_store_test_suite.ts @@ -401,8 +401,9 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch logsByTags.forEach((logsByTag, logIndex) => { expect(logsByTag).toHaveLength(1); - const [log] = logsByTag; - expect(log).toEqual( + const [scopedLog] = logsByTag; + expect(scopedLog.txHash).toEqual(blocks[targetBlockIndex].data.body.txEffects[targetTxIndex].txHash); + expect(scopedLog.log).toEqual( blocks[targetBlockIndex].data.body.noteEncryptedLogs.txLogs[targetTxIndex].unrollLogs()[logIndex], ); }); @@ -427,7 +428,7 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch logsByTags.forEach(logsByTag => { expect(logsByTag).toHaveLength(2); - const [tag0, tag1] = logsByTag.map(log => new Fr(log.data.subarray(0, 32))); + const [tag0, tag1] = logsByTag.map(scopedLog => new Fr(scopedLog.log.data.subarray(0, 32))); expect(tag0).toEqual(tag1); }); }); @@ -450,8 +451,9 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch populatedLogsByTags.forEach((logsByTag, logIndex) => { expect(logsByTag).toHaveLength(1); - const [log] = logsByTag; - expect(log).toEqual( + const [scopedLog] = logsByTag; + expect(scopedLog.txHash).toEqual(blocks[targetBlockIndex].data.body.txEffects[targetTxIndex].txHash); + expect(scopedLog.log).toEqual( blocks[targetBlockIndex].data.body.noteEncryptedLogs.txLogs[targetTxIndex].unrollLogs()[logIndex + 1], ); }); 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 0a1949f0a11..84430312b02 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 @@ -10,6 +10,7 @@ import { type TxEffect, type TxHash, type TxReceipt, + TxScopedEncryptedL2NoteLog, } from '@aztec/circuit-types'; import { type ContractClassPublic, @@ -245,7 +246,7 @@ export class KVArchiverDataStore implements ArchiverDataStore { * @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 { + getLogsByTags(tags: Fr[]): Promise { try { return this.#logStore.getLogsByTags(tags); } catch (err) { 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 5c093fb4b1e..3048d3f1284 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 @@ -10,11 +10,12 @@ import { type LogFilter, LogId, LogType, + TxScopedEncryptedL2NoteLog, UnencryptedL2BlockL2Logs, type UnencryptedL2Log, } from '@aztec/circuit-types'; import { Fr } from '@aztec/circuits.js'; -import { INITIAL_L2_BLOCK_NUM } from '@aztec/circuits.js/constants'; +import { INITIAL_L2_BLOCK_NUM, MAX_NOTE_HASHES_PER_TX } from '@aztec/circuits.js/constants'; import { createDebugLogger } from '@aztec/foundation/log'; import { type AztecKVStore, type AztecMap, type AztecMultiMap } from '@aztec/kv-store'; @@ -52,8 +53,13 @@ export class LogStore { addLogs(blocks: L2Block[]): Promise { return this.db.transaction(() => { blocks.forEach(block => { + const dataStartIndexForBlock = + block.header.state.partial.noteHashTree.nextAvailableLeafIndex - + block.body.numberOfTxsIncludingPadded * MAX_NOTE_HASHES_PER_TX; void this.#noteEncryptedLogsByBlock.set(block.number, block.body.noteEncryptedLogs.toBuffer()); - block.body.noteEncryptedLogs.txLogs.forEach(txLogs => { + block.body.noteEncryptedLogs.txLogs.forEach((txLogs, txIndex) => { + const txHash = block.body.txEffects[txIndex].txHash; + const dataStartIndexForTx = dataStartIndexForBlock + txIndex * MAX_NOTE_HASHES_PER_TX; const noteLogs = txLogs.unrollLogs(); noteLogs.forEach(noteLog => { if (noteLog.data.length < 32) { @@ -63,12 +69,15 @@ export class LogStore { try { const tag = new Fr(noteLog.data.subarray(0, 32)); const hexHash = noteLog.hash().toString('hex'); - // Ideally we'd store all of the logs for a matching tag in an AztecMultiMap, but this type doesn't doesn't + // Ideally we'd store all of the logs for a matching tag in an AztecMultiMap, but this type doesn't // handle storing buffers well. The 'ordered-binary' encoding returns an error trying to decode buffers // ('the number <> cannot be converted to a BigInt because it is not an integer'). We therefore store // instead the hashes of the logs. void this.#noteEncryptedLogHashesByTag.set(tag.toString(), hexHash); - void this.#noteEncryptedLogsByHash.set(hexHash, noteLog.toBuffer()); + void this.#noteEncryptedLogsByHash.set( + hexHash, + new TxScopedEncryptedL2NoteLog(txHash, dataStartIndexForTx, noteLog).toBuffer(), + ); void this.#noteEncryptedLogTagsByBlock.set(block.number, tag.toString()); } catch (err) { this.#log.warn(`Failed to add tagged note log to store: ${err}`); @@ -156,7 +165,7 @@ export class LogStore { * @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 { + getLogsByTags(tags: Fr[]): Promise { return this.db.transaction(() => { return tags.map(tag => { const logHashes = Array.from(this.#noteEncryptedLogHashesByTag.getValues(tag.toString())); @@ -166,7 +175,7 @@ export class LogStore { // addLogs should ensure that we never have undefined logs, but we filter them out regardless to protect // ourselves from database corruption .filter(noteLogBuffer => noteLogBuffer != undefined) - .map(noteLogBuffer => EncryptedL2NoteLog.fromBuffer(noteLogBuffer!)) + .map(noteLogBuffer => TxScopedEncryptedL2NoteLog.fromBuffer(noteLogBuffer!)) ); }); }); 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 index 4d3e887c072..c23469fd694 100644 --- 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 @@ -14,6 +14,7 @@ import { type TxEffect, type TxHash, TxReceipt, + TxScopedEncryptedL2NoteLog, type UnencryptedL2BlockL2Logs, } from '@aztec/circuit-types'; import { @@ -24,6 +25,7 @@ import { Fr, type Header, INITIAL_L2_BLOCK_NUM, + MAX_NOTE_HASHES_PER_TX, type UnconstrainedFunctionWithMembershipProof, } from '@aztec/circuits.js'; import { type ContractArtifact } from '@aztec/foundation/abi'; @@ -51,7 +53,7 @@ export class MemoryArchiverStore implements ArchiverDataStore { private noteEncryptedLogsPerBlock: Map = new Map(); - private taggedNoteEncryptedLogs: Map = new Map(); + private taggedNoteEncryptedLogs: Map = new Map(); private noteEncryptedLogTagsPerBlock: Map = new Map(); @@ -213,8 +215,13 @@ export class MemoryArchiverStore implements ArchiverDataStore { */ addLogs(blocks: L2Block[]): Promise { blocks.forEach(block => { + const dataStartIndexForBlock = + block.header.state.partial.noteHashTree.nextAvailableLeafIndex - + block.body.numberOfTxsIncludingPadded * MAX_NOTE_HASHES_PER_TX; this.noteEncryptedLogsPerBlock.set(block.number, block.body.noteEncryptedLogs); - block.body.noteEncryptedLogs.txLogs.forEach(txLogs => { + block.body.noteEncryptedLogs.txLogs.forEach((txLogs, txIndex) => { + const txHash = block.body.txEffects[txIndex].txHash; + const dataStartIndexForTx = dataStartIndexForBlock + txIndex * MAX_NOTE_HASHES_PER_TX; const noteLogs = txLogs.unrollLogs(); noteLogs.forEach(noteLog => { if (noteLog.data.length < 32) { @@ -224,7 +231,10 @@ export class MemoryArchiverStore implements ArchiverDataStore { try { const tag = new Fr(noteLog.data.subarray(0, 32)); const currentNoteLogs = this.taggedNoteEncryptedLogs.get(tag.toString()) || []; - this.taggedNoteEncryptedLogs.set(tag.toString(), [...currentNoteLogs, noteLog]); + this.taggedNoteEncryptedLogs.set(tag.toString(), [ + ...currentNoteLogs, + new TxScopedEncryptedL2NoteLog(txHash, dataStartIndexForTx, noteLog), + ]); const currentTagsInBlock = this.noteEncryptedLogTagsPerBlock.get(block.number) || []; this.noteEncryptedLogTagsPerBlock.set(block.number, [...currentTagsInBlock, tag]); } catch (err) { @@ -419,7 +429,7 @@ export class MemoryArchiverStore implements ArchiverDataStore { * @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 { + getLogsByTags(tags: Fr[]): Promise { const noteLogs = tags.map(tag => this.taggedNoteEncryptedLogs.get(tag.toString()) || []); return Promise.resolve(noteLogs); } diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index a22e5ce6f8a..7818f0984d4 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -27,6 +27,7 @@ import { type TxEffect, type TxHash, TxReceipt, + TxScopedEncryptedL2NoteLog, TxStatus, type TxValidator, type WorldStateSynchronizer, @@ -314,7 +315,7 @@ export class AztecNodeService implements AztecNode { * @returns For each received tag, an array of matching logs is returned. An empty array implies no logs match * that tag. */ - public getLogsByTags(tags: Fr[]): Promise { + public getLogsByTags(tags: Fr[]): Promise { return this.encryptedLogsSource.getLogsByTags(tags); } diff --git a/yarn-project/circuit-types/src/interfaces/aztec-node.ts b/yarn-project/circuit-types/src/interfaces/aztec-node.ts index e12578d7c44..b309a22d646 100644 --- a/yarn-project/circuit-types/src/interfaces/aztec-node.ts +++ b/yarn-project/circuit-types/src/interfaces/aztec-node.ts @@ -22,6 +22,7 @@ import type { L2BlockL2Logs, LogFilter, LogType, + TxScopedEncryptedL2NoteLog, } from '../logs/index.js'; import type { MerkleTreeId } from '../merkle_tree_id.js'; import type { EpochProofQuote } from '../prover_coordination/epoch_proof_quote.js'; @@ -258,7 +259,7 @@ export interface AztecNode extends ProverCoordination { * @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; + getLogsByTags(tags: Fr[]): Promise; /** * Method to submit a transaction to the p2p pool. diff --git a/yarn-project/circuit-types/src/logs/get_logs_response.ts b/yarn-project/circuit-types/src/logs/get_logs_response.ts new file mode 100644 index 00000000000..698369c7e12 --- /dev/null +++ b/yarn-project/circuit-types/src/logs/get_logs_response.ts @@ -0,0 +1,37 @@ +import { Fr } from '@aztec/circuits.js'; +import { BufferReader, numToUInt32BE } from '@aztec/foundation/serialize'; + +import { EncryptedL2NoteLog, Tx, TxHash } from '../index.js'; +import { type ExtendedUnencryptedL2Log } from './extended_unencrypted_l2_log.js'; + +/** + * It provides documentation for the GetUnencryptedLogsResponse type. + */ +export type GetUnencryptedLogsResponse = { + /** + * An array of ExtendedUnencryptedL2Log elements. + */ + logs: ExtendedUnencryptedL2Log[]; + + /** + * Indicates if a limit has been reached. + */ + maxLogsHit: boolean; +}; + +export class TxScopedEncryptedL2NoteLog { + constructor(public txHash: TxHash, public dataStartIndexForTx: number, public log: EncryptedL2NoteLog) {} + + toBuffer() { + return Buffer.concat([this.txHash.toBuffer(), numToUInt32BE(this.dataStartIndexForTx), this.log.toBuffer()]); + } + + static fromBuffer(buffer: Buffer) { + const reader = BufferReader.asReader(buffer); + return new TxScopedEncryptedL2NoteLog( + TxHash.fromField(reader.readObject(Fr)), + reader.readNumber(), + EncryptedL2NoteLog.fromBuffer(reader.readToEnd()), + ); + } +} diff --git a/yarn-project/circuit-types/src/logs/get_unencrypted_logs_response.ts b/yarn-project/circuit-types/src/logs/get_unencrypted_logs_response.ts deleted file mode 100644 index b8c18fa278d..00000000000 --- a/yarn-project/circuit-types/src/logs/get_unencrypted_logs_response.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { type ExtendedUnencryptedL2Log } from './extended_unencrypted_l2_log.js'; - -/** - * It provides documentation for the GetUnencryptedLogsResponse type. - */ -export type GetUnencryptedLogsResponse = { - /** - * An array of ExtendedUnencryptedL2Log elements. - */ - logs: ExtendedUnencryptedL2Log[]; - - /** - * Indicates if a limit has been reached. - */ - maxLogsHit: boolean; -}; diff --git a/yarn-project/circuit-types/src/logs/index.ts b/yarn-project/circuit-types/src/logs/index.ts index af29a4c9677..2f10eb33f60 100644 --- a/yarn-project/circuit-types/src/logs/index.ts +++ b/yarn-project/circuit-types/src/logs/index.ts @@ -1,7 +1,7 @@ export * from './encrypted_l2_note_log.js'; export * from './encrypted_l2_log.js'; export * from './event_metadata.js'; -export * from './get_unencrypted_logs_response.js'; +export * from './get_logs_response.js'; export * from './function_l2_logs.js'; export * from './l2_block_l2_logs.js'; export * from './l2_logs_source.js'; diff --git a/yarn-project/circuit-types/src/logs/l2_logs_source.ts b/yarn-project/circuit-types/src/logs/l2_logs_source.ts index 9c00af874bf..07ebcfd8eb7 100644 --- a/yarn-project/circuit-types/src/logs/l2_logs_source.ts +++ b/yarn-project/circuit-types/src/logs/l2_logs_source.ts @@ -1,7 +1,6 @@ import { type Fr } from '@aztec/circuits.js'; -import { type EncryptedL2NoteLog } from './encrypted_l2_note_log.js'; -import { type GetUnencryptedLogsResponse } from './get_unencrypted_logs_response.js'; +import { type GetUnencryptedLogsResponse, type TxScopedEncryptedL2NoteLog } from './get_logs_response.js'; import { type L2BlockL2Logs } from './l2_block_l2_logs.js'; import { type LogFilter } from './log_filter.js'; import { type FromLogType, type LogType } from './log_type.js'; @@ -29,7 +28,7 @@ export interface L2LogsSource { * @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; + getLogsByTags(tags: Fr[]): Promise; /** * Gets unencrypted logs based on the provided filter. diff --git a/yarn-project/circuit-types/src/tx/tx.ts b/yarn-project/circuit-types/src/tx/tx.ts index 6bb380aa82d..6b3954af752 100644 --- a/yarn-project/circuit-types/src/tx/tx.ts +++ b/yarn-project/circuit-types/src/tx/tx.ts @@ -8,7 +8,7 @@ import { type Buffer32 } from '@aztec/foundation/buffer'; import { arraySerializedSizeOfNonEmpty } from '@aztec/foundation/collection'; import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; -import { type GetUnencryptedLogsResponse } from '../logs/get_unencrypted_logs_response.js'; +import { type GetUnencryptedLogsResponse } from '../logs/get_logs_response.js'; import { type L2LogsSource } from '../logs/l2_logs_source.js'; import { EncryptedNoteTxL2Logs, EncryptedTxL2Logs, UnencryptedTxL2Logs } from '../logs/tx_l2_logs.js'; import { Gossipable } from '../p2p/gossipable.js'; diff --git a/yarn-project/circuit-types/src/tx_effect.ts b/yarn-project/circuit-types/src/tx_effect.ts index 1115f1b2911..1e327bd42a8 100644 --- a/yarn-project/circuit-types/src/tx_effect.ts +++ b/yarn-project/circuit-types/src/tx_effect.ts @@ -261,7 +261,7 @@ export class TxEffect { [inspect.custom]() { // print out the non-empty fields - return `TxEffect { + return `TxEffect { revertCode: ${this.revertCode}, transactionFee: ${this.transactionFee}, note hashes: [${this.noteHashes.map(h => h.toString()).join(', ')}], diff --git a/yarn-project/prover-client/src/orchestrator/orchestrator.ts b/yarn-project/prover-client/src/orchestrator/orchestrator.ts index 67bbf75b88d..6244b197c43 100644 --- a/yarn-project/prover-client/src/orchestrator/orchestrator.ts +++ b/yarn-project/prover-client/src/orchestrator/orchestrator.ts @@ -26,6 +26,7 @@ import { type Header, L1_TO_L2_MSG_SUBTREE_HEIGHT, L1_TO_L2_MSG_SUBTREE_SIBLING_PATH_LENGTH, + MAX_NOTE_HASHES_PER_TX, type NESTED_RECURSIVE_PROOF_LENGTH, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, NUM_BASE_PARITY_PER_ROOT_PARITY, diff --git a/yarn-project/pxe/src/pxe_service/pxe_service.ts b/yarn-project/pxe/src/pxe_service/pxe_service.ts index 31490047558..e9cd583a719 100644 --- a/yarn-project/pxe/src/pxe_service/pxe_service.ts +++ b/yarn-project/pxe/src/pxe_service/pxe_service.ts @@ -8,6 +8,7 @@ import { type GetUnencryptedLogsResponse, type IncomingNotesFilter, L1EventPayload, + L1NotePayload, type L2Block, type LogFilter, MerkleTreeId, @@ -27,6 +28,7 @@ import { type TxHash, TxProvingResult, type TxReceipt, + TxScopedEncryptedL2NoteLog, TxSimulationResult, UniqueNote, getNonNullifiedL1ToL2MessageWitness, @@ -69,11 +71,14 @@ import { type AcirSimulator } from '@aztec/simulator'; import { type PXEServiceConfig, getPackageInfo } from '../config/index.js'; import { ContractDataOracle } from '../contract_data_oracle/index.js'; +import { DeferredNoteDao } from '../database/deferred_note_dao.js'; import { IncomingNoteDao } from '../database/incoming_note_dao.js'; import { type PxeDatabase } from '../database/index.js'; +import { OutgoingNoteDao } from '../database/outgoing_note_dao.js'; import { KernelOracle } from '../kernel_oracle/index.js'; import { KernelProver } from '../kernel_prover/kernel_prover.js'; import { TestPrivateKernelProver } from '../kernel_prover/test/test_circuit_prover.js'; +import { produceNoteDaos } from '../note_processor/utils/produce_note_daos.js'; import { getAcirSimulator } from '../simulator/index.js'; import { Synchronizer } from '../synchronizer/index.js'; import { enrichPublicSimulationError, enrichSimulationError } from './error_enriching.js'; @@ -91,8 +96,6 @@ export class PXEService implements PXE { // ensures that state is not changed while simulating private jobQueue = new SerialQueue(); - private fakeProofCreator = new TestPrivateKernelProver(); - constructor( private keyStore: KeyStore, private node: AztecNode, diff --git a/yarn-project/pxe/src/simulator_oracle/index.ts b/yarn-project/pxe/src/simulator_oracle/index.ts index b67a5dd2da1..06f913d2f67 100644 --- a/yarn-project/pxe/src/simulator_oracle/index.ts +++ b/yarn-project/pxe/src/simulator_oracle/index.ts @@ -1,11 +1,12 @@ import { type AztecNode, - type EncryptedL2NoteLog, + L1NotePayload, type L2Block, MerkleTreeId, type NoteStatus, type NullifierMembershipWitness, type PublicDataWitness, + TxScopedEncryptedL2NoteLog, getNonNullifiedL1ToL2MessageWitness, } from '@aztec/circuit-types'; import { @@ -19,6 +20,8 @@ import { type KeyValidationRequest, type L1_TO_L2_MSG_TREE_HEIGHT, TaggingSecret, + computeAddressSecret, + computePoint, computeTaggingSecret, } from '@aztec/circuits.js'; import { type FunctionArtifact, getFunctionArtifact } from '@aztec/foundation/abi'; @@ -28,7 +31,12 @@ import { type KeyStore } from '@aztec/key-store'; import { type DBOracle, MessageLoadOracleInputs } from '@aztec/simulator'; import { type ContractDataOracle } from '../contract_data_oracle/index.js'; +import { DeferredNoteDao } from '../database/deferred_note_dao.js'; +import { IncomingNoteDao } from '../database/incoming_note_dao.js'; import { type PxeDatabase } from '../database/index.js'; +import { OutgoingNoteDao } from '../database/outgoing_note_dao.js'; +import { produceNoteDaos } from '../note_processor/utils/produce_note_daos.js'; +import { getAcirSimulator } from '../simulator/index.js'; /** * A data oracle that provides information needed for simulating a transaction. @@ -303,7 +311,10 @@ export class SimulatorOracle implements DBOracle { * @param recipient - The address of the recipient * @returns A list of encrypted logs tagged with the recipient's address */ - public async syncTaggedLogs(contractAddress: AztecAddress, recipient: AztecAddress): Promise { + public async syncTaggedLogs( + contractAddress: AztecAddress, + recipient: AztecAddress, + ): Promise { // Ideally this algorithm would be implemented in noir, exposing its building blocks as oracles. // However it is impossible at the moment due to the language not supporting nested slices. // This nesting is necessary because for a given set of tags we don't @@ -313,7 +324,7 @@ export class SimulatorOracle implements DBOracle { // 1. Get all the secrets for the recipient and sender pairs (#9365) let appTaggingSecrets = await this.getAppTaggingSecretsForSenders(contractAddress, recipient); - const logs: EncryptedL2NoteLog[] = []; + const logs: TxScopedEncryptedL2NoteLog[] = []; while (appTaggingSecrets.length > 0) { // 2. Compute tags using the secrets, recipient and index. Obtain logs for each tag (#9380) const currentTags = appTaggingSecrets.map(({ secret, recipient, index }) => @@ -339,4 +350,99 @@ export class SimulatorOracle implements DBOracle { } return logs; } + + public async processTaggedLogs(logs: TxScopedEncryptedL2NoteLog[], recipient: AztecAddress): Promise { + const recipientCompleteAddress = await this.getCompleteAddress(recipient); + const ivskM = await this.keyStore.getMasterSecretKey( + recipientCompleteAddress.publicKeys.masterIncomingViewingPublicKey, + ); + const addressSecret = computeAddressSecret(recipientCompleteAddress.getPreaddress(), ivskM); + const ovskM = await this.keyStore.getMasterSecretKey( + recipientCompleteAddress.publicKeys.masterOutgoingViewingPublicKey, + ); + const excludedIndices: Set = new Set(); + const incomingNotes: IncomingNoteDao[] = []; + const outgoingNotes: OutgoingNoteDao[] = []; + const deferredIncomingNotes: DeferredNoteDao[] = []; + const deferredOutgoingNotes: DeferredNoteDao[] = []; + for (const scopedLog of logs) { + const incomingNotePayload = L1NotePayload.decryptAsIncoming(scopedLog.log.data, addressSecret); + const outgoingNotePayload = L1NotePayload.decryptAsOutgoing(scopedLog.log.data, ovskM); + + if (incomingNotePayload || outgoingNotePayload) { + if (incomingNotePayload && outgoingNotePayload && !incomingNotePayload.equals(outgoingNotePayload)) { + throw new Error( + `Incoming and outgoing note payloads do not match. Incoming: ${JSON.stringify( + incomingNotePayload, + )}, Outgoing: ${JSON.stringify(outgoingNotePayload)}`, + ); + } + + const payload = incomingNotePayload || outgoingNotePayload; + const txEffect = await this.aztecNode.getTxEffect(scopedLog.txHash); + + if (!txEffect) { + throw new Error(`No tx effect found for ${scopedLog.txHash}`); + } + + const { incomingNote, outgoingNote, incomingDeferredNote, outgoingDeferredNote } = await produceNoteDaos( + // This is disgusting + getAcirSimulator(this.db, this.aztecNode, this.keyStore, this.contractDataOracle), + this.db, + incomingNotePayload ? computePoint(recipient) : undefined, + outgoingNotePayload ? recipientCompleteAddress.publicKeys.masterOutgoingViewingPublicKey : undefined, + payload!, + txEffect.txHash, + txEffect.noteHashes, + scopedLog.dataStartIndexForTx, + excludedIndices, + this.log, + txEffect.unencryptedLogs, + ); + + if (incomingNote) { + incomingNotes.push(incomingNote); + } + if (outgoingNote) { + outgoingNotes.push(outgoingNote); + } + if (incomingDeferredNote) { + deferredIncomingNotes.push(incomingDeferredNote); + } + if (outgoingDeferredNote) { + deferredOutgoingNotes.push(outgoingDeferredNote); + } + } + } + if (deferredIncomingNotes.length || deferredOutgoingNotes.length) { + await this.db.addDeferredNotes([...deferredIncomingNotes, ...deferredOutgoingNotes]); + deferredIncomingNotes.forEach(noteDao => { + this.log.verbose( + `Deferred incoming note for contract ${noteDao.payload.contractAddress} at slot ${ + noteDao.payload.storageSlot + } in tx ${noteDao.txHash.toString()}`, + ); + }); + deferredOutgoingNotes.forEach(noteDao => { + this.log.verbose( + `Deferred outgoing note for contract ${noteDao.payload.contractAddress} at slot ${ + noteDao.payload.storageSlot + } in tx ${noteDao.txHash.toString()}`, + ); + }); + } + if (incomingNotes.length || outgoingNotes.length) { + await this.db.addNotes(incomingNotes, outgoingNotes, recipient); + incomingNotes.forEach(noteDao => { + this.log.verbose( + `Added incoming note for contract ${noteDao.contractAddress} at slot ${ + noteDao.storageSlot + } with nullifier ${noteDao.siloedNullifier.toString()}`, + ); + }); + outgoingNotes.forEach(noteDao => { + this.log.verbose(`Added outgoing note for contract ${noteDao.contractAddress} at slot ${noteDao.storageSlot}`); + }); + } + } } diff --git a/yarn-project/pxe/src/simulator_oracle/simulator_oracle.test.ts b/yarn-project/pxe/src/simulator_oracle/simulator_oracle.test.ts index d9e8728ad95..2ae62665a06 100644 --- a/yarn-project/pxe/src/simulator_oracle/simulator_oracle.test.ts +++ b/yarn-project/pxe/src/simulator_oracle/simulator_oracle.test.ts @@ -1,4 +1,4 @@ -import { type AztecNode, EncryptedL2NoteLog } from '@aztec/circuit-types'; +import { type AztecNode, EncryptedL2NoteLog, TxHash, TxScopedEncryptedL2NoteLog } from '@aztec/circuit-types'; import { AztecAddress, CompleteAddress, @@ -69,13 +69,13 @@ describe('Simulator oracle', () => { await database.addCompleteAddress(sender.completeAddress); } - const logs: { [k: string]: EncryptedL2NoteLog[] } = {}; + const logs: { [k: string]: TxScopedEncryptedL2NoteLog[] } = {}; // Add a random note from every address in the address book for our account with index 0 // Compute the tag as sender (knowledge of preaddress and ivsk) for (const sender of senders) { const tag = computeTagForIndex(sender, recipient.address, contractAddress, 0); - const log = EncryptedL2NoteLog.random(tag); + const log = new TxScopedEncryptedL2NoteLog(TxHash.random(), 0, EncryptedL2NoteLog.random(tag)); logs[tag.toString()] = [log]; } // Accumulated logs intended for recipient: NUM_SENDERS @@ -84,7 +84,7 @@ describe('Simulator oracle', () => { // Compute the tag as sender (knowledge of preaddress and ivsk) const firstSender = senders[0]; const tag = computeTagForIndex(firstSender, recipient.address, contractAddress, 0); - const log = EncryptedL2NoteLog.random(tag); + const log = new TxScopedEncryptedL2NoteLog(TxHash.random(), 0, EncryptedL2NoteLog.random(tag)); logs[tag.toString()].push(log); // Accumulated logs intended for recipient: NUM_SENDERS + 1 @@ -93,7 +93,7 @@ describe('Simulator oracle', () => { for (let i = NUM_SENDERS / 2; i < NUM_SENDERS; i++) { const sender = senders[i]; const tag = computeTagForIndex(sender, recipient.address, contractAddress, 1); - const log = EncryptedL2NoteLog.random(tag); + const log = new TxScopedEncryptedL2NoteLog(TxHash.random(), 0, EncryptedL2NoteLog.random(tag)); logs[tag.toString()] = [log]; } // Accumulated logs intended for recipient: NUM_SENDERS + 1 + NUM_SENDERS / 2 @@ -105,7 +105,7 @@ describe('Simulator oracle', () => { const partialAddress = Fr.random(); const randomRecipient = computeAddress(keys.publicKeys, partialAddress); const tag = computeTagForIndex(sender, randomRecipient, contractAddress, 0); - const log = EncryptedL2NoteLog.random(tag); + const log = new TxScopedEncryptedL2NoteLog(TxHash.random(), 0, EncryptedL2NoteLog.random(tag)); logs[tag.toString()] = [log]; } // Accumulated logs intended for recipient: NUM_SENDERS + 1 + NUM_SENDERS / 2 diff --git a/yarn-project/simulator/src/client/client_execution_context.ts b/yarn-project/simulator/src/client/client_execution_context.ts index 06c51c9b0bf..5bdf3b305b8 100644 --- a/yarn-project/simulator/src/client/client_execution_context.ts +++ b/yarn-project/simulator/src/client/client_execution_context.ts @@ -609,4 +609,9 @@ export class ClientExecutionContext extends ViewDataOracle { public getDebugFunctionName() { return this.db.getDebugFunctionName(this.contractAddress, this.callContext.functionSelector); } + + public async syncNotes(targetContractAddress: AztecAddress, recipient: AztecAddress) { + const taggedLogs = await this.db.syncTaggedLogs(targetContractAddress, recipient); + await this.db.processTaggedLogs(taggedLogs, recipient); + } } diff --git a/yarn-project/simulator/src/client/db_oracle.ts b/yarn-project/simulator/src/client/db_oracle.ts index c19a0b1636a..cbae7c06852 100644 --- a/yarn-project/simulator/src/client/db_oracle.ts +++ b/yarn-project/simulator/src/client/db_oracle.ts @@ -4,6 +4,7 @@ import { type NoteStatus, type NullifierMembershipWitness, type PublicDataWitness, + TxScopedEncryptedL2NoteLog, } from '@aztec/circuit-types'; import { type CompleteAddress, @@ -219,4 +220,8 @@ export interface DBOracle extends CommitmentsDB { contractAddress: AztecAddress, recipient: AztecAddress, ): Promise; + + syncTaggedLogs(contractAddress: AztecAddress, recipient: AztecAddress): Promise; + + processTaggedLogs(logs: TxScopedEncryptedL2NoteLog[], recipient: AztecAddress): Promise; } From 68925980fc14e965daa9e1a7374dffccda5e1087 Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 5 Nov 2024 06:53:46 +0000 Subject: [PATCH 02/15] fixes and cleanup --- yarn-project/circuit-types/src/tx/tx_hash.ts | 10 + .../pxe/src/simulator_oracle/index.ts | 32 +- .../simulator_oracle/simulator_oracle.test.ts | 505 +++++++++++++++--- .../simulator/src/acvm/oracle/oracle.ts | 5 - .../simulator/src/acvm/oracle/typed_oracle.ts | 4 - .../simulator/src/client/db_oracle.ts | 21 +- .../simulator/src/client/view_data_oracle.ts | 12 +- yarn-project/txe/src/oracle/txe_oracle.ts | 2 +- .../txe/src/txe_service/txe_service.ts | 7 - 9 files changed, 489 insertions(+), 109 deletions(-) diff --git a/yarn-project/circuit-types/src/tx/tx_hash.ts b/yarn-project/circuit-types/src/tx/tx_hash.ts index 921903d75a0..7781fe4b30e 100644 --- a/yarn-project/circuit-types/src/tx/tx_hash.ts +++ b/yarn-project/circuit-types/src/tx/tx_hash.ts @@ -1,3 +1,4 @@ +import { Fr } from '@aztec/circuits.js'; import { Buffer32 } from '@aztec/foundation/buffer'; /** @@ -12,4 +13,13 @@ export class TxHash extends Buffer32 { ) { super(hash); } + + /* + * TxHashes are generated from the first nullifier of a transaction, which is a Fr. + * Using Buffer32.random() could potentially generate invalid TxHashes. + * @returns A random TxHash. + */ + static override random() { + return new TxHash(Fr.random().toBuffer()); + } } diff --git a/yarn-project/pxe/src/simulator_oracle/index.ts b/yarn-project/pxe/src/simulator_oracle/index.ts index c06ba7e805d..17642e7e26d 100644 --- a/yarn-project/pxe/src/simulator_oracle/index.ts +++ b/yarn-project/pxe/src/simulator_oracle/index.ts @@ -28,7 +28,7 @@ import { type FunctionArtifact, getFunctionArtifact } from '@aztec/foundation/ab import { poseidon2Hash } from '@aztec/foundation/crypto'; import { createDebugLogger } from '@aztec/foundation/log'; import { type KeyStore } from '@aztec/key-store'; -import { type DBOracle, MessageLoadOracleInputs } from '@aztec/simulator'; +import { AcirSimulator, type DBOracle, MessageLoadOracleInputs } from '@aztec/simulator'; import { type ContractDataOracle } from '../contract_data_oracle/index.js'; import { DeferredNoteDao } from '../database/deferred_note_dao.js'; @@ -252,7 +252,7 @@ export class SimulatorOracle implements DBOracle { /** * Returns the tagging secret for a given sender and recipient pair. For this to work, the ivpsk_m of the sender must be known. - * Includes the last known index used for tagging with this secret. + * Includes the last seen index used for tagging with this secret, or 0 if this combination hasn't been seen before. * @param contractAddress - The contract address to silo the secret for * @param sender - The address sending the note * @param recipient - The address receiving the note @@ -300,7 +300,7 @@ export class SimulatorOracle implements DBOracle { * @param recipient - The address receiving the notes * @returns A list of siloed tagging secrets */ - public async getAppTaggingSecretsForSenders( + async #getAppTaggingSecretsForSenders( contractAddress: AztecAddress, recipient: AztecAddress, ): Promise { @@ -339,7 +339,7 @@ export class SimulatorOracle implements DBOracle { // length, since we don't really know the note they correspond to until we decrypt them. // 1. Get all the secrets for the recipient and sender pairs (#9365) - let appTaggingSecrets = await this.getAppTaggingSecretsForSenders(contractAddress, recipient); + let appTaggingSecrets = await this.#getAppTaggingSecretsForSenders(contractAddress, recipient); const logs: TxScopedEncryptedL2NoteLog[] = []; while (appTaggingSecrets.length > 0) { @@ -366,7 +366,16 @@ export class SimulatorOracle implements DBOracle { return logs; } - public async processTaggedLogs(logs: TxScopedEncryptedL2NoteLog[], recipient: AztecAddress): Promise { + /** + * Processes the tagged logs returned by syncTaggedLogs by decrypting them and storing them in the database. + * @param logs - The logs to process. + * @param recipient - The recipient of the logs. + */ + public async processTaggedLogs( + logs: TxScopedEncryptedL2NoteLog[], + recipient: AztecAddress, + simulator?: AcirSimulator, + ): Promise { const recipientCompleteAddress = await this.getCompleteAddress(recipient); const ivskM = await this.keyStore.getMasterSecretKey( recipientCompleteAddress.publicKeys.masterIncomingViewingPublicKey, @@ -375,7 +384,9 @@ export class SimulatorOracle implements DBOracle { const ovskM = await this.keyStore.getMasterSecretKey( recipientCompleteAddress.publicKeys.masterOutgoingViewingPublicKey, ); - const excludedIndices: Set = new Set(); + // Since we could have notes with the same index for different txs, we need + // to keep track of them scoping by txHash + const excludedIndices: Map> = new Map(); const incomingNotes: IncomingNoteDao[] = []; const outgoingNotes: OutgoingNoteDao[] = []; const deferredIncomingNotes: DeferredNoteDao[] = []; @@ -399,10 +410,11 @@ export class SimulatorOracle implements DBOracle { if (!txEffect) { throw new Error(`No tx effect found for ${scopedLog.txHash}`); } - + if (!excludedIndices.has(scopedLog.txHash.toString())) { + excludedIndices.set(scopedLog.txHash.toString(), new Set()); + } const { incomingNote, outgoingNote, incomingDeferredNote, outgoingDeferredNote } = await produceNoteDaos( - // This is disgusting - getAcirSimulator(this.db, this.aztecNode, this.keyStore, this.contractDataOracle), + simulator ?? getAcirSimulator(this.db, this.aztecNode, this.keyStore, this.contractDataOracle), this.db, incomingNotePayload ? computePoint(recipient) : undefined, outgoingNotePayload ? recipientCompleteAddress.publicKeys.masterOutgoingViewingPublicKey : undefined, @@ -410,7 +422,7 @@ export class SimulatorOracle implements DBOracle { txEffect.txHash, txEffect.noteHashes, scopedLog.dataStartIndexForTx, - excludedIndices, + excludedIndices.get(scopedLog.txHash.toString())!, this.log, txEffect.unencryptedLogs, ); diff --git a/yarn-project/pxe/src/simulator_oracle/simulator_oracle.test.ts b/yarn-project/pxe/src/simulator_oracle/simulator_oracle.test.ts index 9b27d64bb36..6c733b8dd1d 100644 --- a/yarn-project/pxe/src/simulator_oracle/simulator_oracle.test.ts +++ b/yarn-project/pxe/src/simulator_oracle/simulator_oracle.test.ts @@ -1,25 +1,104 @@ -import { type AztecNode, EncryptedL2NoteLog, TxHash, TxScopedEncryptedL2NoteLog } from '@aztec/circuit-types'; +import { + type AztecNode, + EncryptedL2NoteLog, + EncryptedLogPayload, + L1NotePayload, + Note, + TxEffect, + TxHash, + TxScopedEncryptedL2NoteLog, +} from '@aztec/circuit-types'; import { AztecAddress, CompleteAddress, type Fq, Fr, + GrumpkinScalar, + INITIAL_L2_BLOCK_NUM, + KeyValidationRequest, + MAX_NOTE_HASHES_PER_TX, TaggingSecret, computeAddress, + computeOvskApp, computeTaggingSecret, deriveKeys, } from '@aztec/circuits.js'; -import { poseidon2Hash } from '@aztec/foundation/crypto'; +import { pedersenHash, poseidon2Hash } from '@aztec/foundation/crypto'; import { KeyStore } from '@aztec/key-store'; import { openTmpStore } from '@aztec/kv-store/utils'; +import { AcirSimulator } from '@aztec/simulator'; +import { jest } from '@jest/globals'; import { type MockProxy, mock } from 'jest-mock-extended'; import times from 'lodash.times'; +import { IncomingNoteDao } from '../database/incoming_note_dao.js'; import { type PxeDatabase } from '../database/index.js'; import { KVPxeDatabase } from '../database/kv_pxe_database.js'; +import { OutgoingNoteDao } from '../database/outgoing_note_dao.js'; import { ContractDataOracle } from '../index.js'; -import { SimulatorOracle } from './index.js'; +import { type SimulatorOracle } from './index.js'; + +const TXS_PER_BLOCK = 4; +const NUM_NOTE_HASHES_PER_BLOCK = TXS_PER_BLOCK * MAX_NOTE_HASHES_PER_TX; + +function getRandomNoteLogPayload(tag = Fr.random(), app = AztecAddress.random()): EncryptedLogPayload { + return new EncryptedLogPayload(tag, app, L1NotePayload.random(app).toIncomingBodyPlaintext()); +} + +/** A wrapper containing info about a note we want to mock and insert into a block. */ +class MockNoteRequest { + constructor( + /** Log payload corresponding to a note we want to insert into a block. */ + public readonly logPayload: EncryptedLogPayload, + /** Block number this note corresponds to. */ + public readonly blockNumber: number, + /** Index of a tx within a block this note corresponds to. */ + public readonly txIndex: number, + /** Index of a note hash within a list of note hashes for 1 tx. */ + public readonly noteHashIndex: number, + /** Address point we use when encrypting a note. */ + public readonly recipient: AztecAddress, + /** ovKeys we use when encrypting a note. */ + public readonly ovKeys: KeyValidationRequest, + ) { + if (blockNumber < INITIAL_L2_BLOCK_NUM) { + throw new Error(`Block number should be greater than or equal to ${INITIAL_L2_BLOCK_NUM}.`); + } + if (noteHashIndex >= MAX_NOTE_HASHES_PER_TX) { + throw new Error(`Data index should be less than ${MAX_NOTE_HASHES_PER_TX}.`); + } + if (txIndex >= TXS_PER_BLOCK) { + throw new Error(`Tx index should be less than ${TXS_PER_BLOCK}.`); + } + } + + encrypt(): EncryptedL2NoteLog { + const ephSk = GrumpkinScalar.random(); + const log = this.logPayload.encrypt(ephSk, this.recipient, this.ovKeys); + return new EncryptedL2NoteLog(log); + } + + get indexWithinNoteHashTree(): bigint { + return BigInt( + (this.blockNumber - 1) * NUM_NOTE_HASHES_PER_BLOCK + this.txIndex * MAX_NOTE_HASHES_PER_TX + this.noteHashIndex, + ); + } + + get snippetOfNoteDao() { + const payload = L1NotePayload.fromIncomingBodyPlaintextContractAndPublicValues( + this.logPayload.incomingBodyPlaintext, + this.logPayload.contractAddress, + [], + )!; + return { + note: new Note(payload.privateNoteValues), + contractAddress: payload.contractAddress, + storageSlot: payload.storageSlot, + noteTypeId: payload.noteTypeId, + }; + } +} function computeTagForIndex( sender: { completeAddress: CompleteAddress; ivsk: Fq }, @@ -40,84 +119,118 @@ describe('Simulator oracle', () => { let keyStore: KeyStore; let recipient: CompleteAddress; + let recipientOvKeys: KeyValidationRequest; let contractAddress: AztecAddress; - const NUM_SENDERS = 10; - let senders: { completeAddress: CompleteAddress; ivsk: Fq }[]; - beforeEach(async () => { const db = openTmpStore(); aztecNode = mock(); database = new KVPxeDatabase(db); contractDataOracle = new ContractDataOracle(database); keyStore = new KeyStore(db); - simulatorOracle = new SimulatorOracle(contractDataOracle, database, keyStore, aztecNode); + const simulatorOracleModule = await import('../simulator_oracle/index.js'); + simulatorOracle = new simulatorOracleModule.SimulatorOracle(contractDataOracle, database, keyStore, aztecNode); // Set up contract address contractAddress = AztecAddress.random(); // Set up recipient account recipient = await keyStore.addAccount(new Fr(69), Fr.random()); + const recipientOvskApp = await keyStore.getAppOutgoingViewingSecretKey(recipient.address, contractAddress); await database.addCompleteAddress(recipient); - // Set up the address book - senders = times(NUM_SENDERS).map((_, index) => { - const keys = deriveKeys(new Fr(index)); - const partialAddress = Fr.random(); - const address = computeAddress(keys.publicKeys, partialAddress); - const completeAddress = new CompleteAddress(address, keys.publicKeys, partialAddress); - return { completeAddress, ivsk: keys.masterIncomingViewingSecretKey }; - }); - for (const sender of senders) { - await database.addContactAddress(sender.completeAddress.address); - } + recipientOvKeys = new KeyValidationRequest(recipient.publicKeys.masterOutgoingViewingPublicKey, recipientOvskApp); + }); - const logs: { [k: string]: TxScopedEncryptedL2NoteLog[] } = {}; + describe('sync tagged logs', () => { + const NUM_SENDERS = 10; + let senders: { completeAddress: CompleteAddress; ivsk: Fq }[]; - // Add a random note from every address in the address book for our account with index 0 - // Compute the tag as sender (knowledge of preaddress and ivsk) - for (const sender of senders) { - const tag = computeTagForIndex(sender, recipient.address, contractAddress, 0); - const log = new TxScopedEncryptedL2NoteLog(TxHash.random(), 0, EncryptedL2NoteLog.random(tag)); - logs[tag.toString()] = [log]; - } - // Accumulated logs intended for recipient: NUM_SENDERS - - // Add a random note from the first sender in the address book, repeating the tag - // Compute the tag as sender (knowledge of preaddress and ivsk) - const firstSender = senders[0]; - const tag = computeTagForIndex(firstSender, recipient.address, contractAddress, 0); - const log = new TxScopedEncryptedL2NoteLog(TxHash.random(), 0, EncryptedL2NoteLog.random(tag)); - logs[tag.toString()].push(log); - // Accumulated logs intended for recipient: NUM_SENDERS + 1 - - // Add a random note from half the address book for our account with index 1 - // Compute the tag as sender (knowledge of preaddress and ivsk) - for (let i = NUM_SENDERS / 2; i < NUM_SENDERS; i++) { - const sender = senders[i]; - const tag = computeTagForIndex(sender, recipient.address, contractAddress, 1); - const log = new TxScopedEncryptedL2NoteLog(TxHash.random(), 0, EncryptedL2NoteLog.random(tag)); - logs[tag.toString()] = [log]; - } - // Accumulated logs intended for recipient: NUM_SENDERS + 1 + NUM_SENDERS / 2 - - // Add a random note from every address in the address book for a random recipient with index 0 - // Compute the tag as sender (knowledge of preaddress and ivsk) - for (const sender of senders) { - const keys = deriveKeys(Fr.random()); - const partialAddress = Fr.random(); - const randomRecipient = computeAddress(keys.publicKeys, partialAddress); - const tag = computeTagForIndex(sender, randomRecipient, contractAddress, 0); + beforeEach(async () => { + // Set up the address book + senders = times(NUM_SENDERS).map((_, index) => { + const keys = deriveKeys(new Fr(index)); + const partialAddress = Fr.random(); + const address = computeAddress(keys.publicKeys, partialAddress); + const completeAddress = new CompleteAddress(address, keys.publicKeys, partialAddress); + return { completeAddress, ivsk: keys.masterIncomingViewingSecretKey }; + }); + for (const sender of senders) { + await database.addContactAddress(sender.completeAddress.address); + } + + const logs: { [k: string]: TxScopedEncryptedL2NoteLog[] } = {}; + + // Add a random note from every address in the address book for our account with index 0 + // Compute the tag as sender (knowledge of preaddress and ivsk) + for (const sender of senders) { + const tag = computeTagForIndex(sender, recipient.address, contractAddress, 0); + const randomNote = new MockNoteRequest( + getRandomNoteLogPayload(tag, contractAddress), + 1, + 1, + 1, + recipient.address, + recipientOvKeys, + ); + const log = new TxScopedEncryptedL2NoteLog(TxHash.random(), 0, randomNote.encrypt()); + logs[tag.toString()] = [log]; + } + // Accumulated logs intended for recipient: NUM_SENDERS + + // Add a random note from the first sender in the address book, repeating the tag + // Compute the tag as sender (knowledge of preaddress and ivsk) + const firstSender = senders[0]; + const tag = computeTagForIndex(firstSender, recipient.address, contractAddress, 0); const log = new TxScopedEncryptedL2NoteLog(TxHash.random(), 0, EncryptedL2NoteLog.random(tag)); - logs[tag.toString()] = [log]; - } - // Accumulated logs intended for recipient: NUM_SENDERS + 1 + NUM_SENDERS / 2 + logs[tag.toString()].push(log); + // Accumulated logs intended for recipient: NUM_SENDERS + 1 + + // Add a random note from half the address book for our account with index 1 + // Compute the tag as sender (knowledge of preaddress and ivsk) + for (let i = NUM_SENDERS / 2; i < NUM_SENDERS; i++) { + const sender = senders[i]; + const tag = computeTagForIndex(sender, recipient.address, contractAddress, 1); + const randomNote = new MockNoteRequest( + getRandomNoteLogPayload(tag, contractAddress), + 1, + 1, + 1, + recipient.address, + recipientOvKeys, + ); + const log = new TxScopedEncryptedL2NoteLog(TxHash.random(), 0, randomNote.encrypt()); + logs[tag.toString()] = [log]; + } + // Accumulated logs intended for recipient: NUM_SENDERS + 1 + NUM_SENDERS / 2 - // Set up the getTaggedLogs mock + // Add a random note from every address in the address book for a random recipient with index 0 + // Compute the tag as sender (knowledge of preaddress and ivsk) + for (const sender of senders) { + const keys = deriveKeys(Fr.random()); + const partialAddress = Fr.random(); + const randomRecipient = computeAddress(keys.publicKeys, partialAddress); + const tag = computeTagForIndex(sender, randomRecipient, contractAddress, 0); + const randomNote = new MockNoteRequest( + getRandomNoteLogPayload(tag, contractAddress), + 1, + 1, + 1, + randomRecipient, + new KeyValidationRequest( + keys.publicKeys.masterOutgoingViewingPublicKey, + computeOvskApp(keys.masterOutgoingViewingSecretKey, contractAddress), + ), + ); + const log = new TxScopedEncryptedL2NoteLog(TxHash.random(), 0, randomNote.encrypt()); + logs[tag.toString()] = [log]; + } + // Accumulated logs intended for recipient: NUM_SENDERS + 1 + NUM_SENDERS / 2 - aztecNode.getLogsByTags.mockImplementation(tags => { - return Promise.resolve(tags.map(tag => logs[tag.toString()] ?? [])); + // Set up the getTaggedLogs mock + + aztecNode.getLogsByTags.mockImplementation(tags => { + return Promise.resolve(tags.map(tag => logs[tag.toString()] ?? [])); + }); }); - }); - describe('sync tagged logs', () => { it('should sync tagged logs', async () => { const syncedLogs = await simulatorOracle.syncTaggedLogs(contractAddress, recipient.address); // We expect to have all logs intended for the recipient, one per sender + 1 with a duplicated tag for the first one + half of the logs for the second index @@ -162,4 +275,274 @@ describe('Simulator oracle', () => { expect(aztecNode.getLogsByTags.mock.calls.length).toBe(2); }); }); + + describe('Process notes', () => { + let addNotesSpy: any; + let simulator: MockProxy; + + beforeEach(async () => { + addNotesSpy = jest.spyOn(database, 'addNotes'); + simulator = mock(); + simulator.computeNoteHashAndOptionallyANullifier.mockImplementation((...args: any) => + Promise.resolve({ + noteHash: Fr.random(), + uniqueNoteHash: Fr.random(), + siloedNoteHash: pedersenHash(args[5].items), // args[5] is note + innerNullifier: Fr.random(), + }), + ); + }); + + afterEach(() => { + addNotesSpy.mockReset(); + simulator.computeNoteHashAndOptionallyANullifier.mockReset(); + aztecNode.getTxEffect.mockReset(); + }); + + function mockTaggedLogs(requests: MockNoteRequest[]) { + const txEffectsMap: { [k: string]: { noteHashes: Fr[]; txHash: TxHash } } = {}; + const taggedLogs: TxScopedEncryptedL2NoteLog[] = []; + const groupedByTx = requests.reduce<{ [i: number]: { [j: number]: MockNoteRequest[] } }>((acc, request) => { + if (!acc[request.blockNumber]) { + acc[request.blockNumber] = {}; + } + if (!acc[request.blockNumber][request.txIndex]) { + acc[request.blockNumber][request.txIndex] = []; + } + acc[request.blockNumber][request.txIndex].push(request); + return acc; + }, {}); + Object.keys(groupedByTx).forEach(blockNumberKey => { + const blockNumber = parseInt(blockNumberKey); + Object.keys(groupedByTx[blockNumber]).forEach(txIndexKey => { + const txIndex = parseInt(txIndexKey); + const requestsInTx = groupedByTx[blockNumber][txIndex]; + const maxNoteIndex = Math.max(...requestsInTx.map(request => request.noteHashIndex)); + const txHash = TxHash.random(); + for (const request of requestsInTx) { + if (!txEffectsMap[txHash.toString()]) { + txEffectsMap[txHash.toString()] = { + txHash, + noteHashes: Array(maxNoteIndex + 1) + .fill(0) + .map(() => Fr.random()), + }; + } + const dataStartIndex = + (request.blockNumber - 1) * NUM_NOTE_HASHES_PER_BLOCK + request.txIndex * MAX_NOTE_HASHES_PER_TX; + const taggedLog = new TxScopedEncryptedL2NoteLog(txHash, dataStartIndex, request.encrypt()); + const note = request.snippetOfNoteDao.note; + const noteHash = pedersenHash(note.items); + txEffectsMap[txHash.toString()].noteHashes[request.noteHashIndex] = noteHash; + taggedLogs.push(taggedLog); + } + }); + }); + + aztecNode.getTxEffect.mockImplementation(async txHash => { + return Promise.resolve(txEffectsMap[txHash.toString()] as TxEffect); + }); + return taggedLogs; + } + + it('should store an incoming note that belongs to us', async () => { + const request = new MockNoteRequest( + getRandomNoteLogPayload(Fr.random(), contractAddress), + 4, + 0, + 2, + recipient.address, + KeyValidationRequest.random(), + ); + const taggedLogs = mockTaggedLogs([request]); + + await simulatorOracle.processTaggedLogs(taggedLogs, recipient.address, simulator); + + expect(addNotesSpy).toHaveBeenCalledTimes(1); + expect(addNotesSpy).toHaveBeenCalledWith( + [ + expect.objectContaining({ + ...request.snippetOfNoteDao, + index: request.indexWithinNoteHashTree, + }), + ], + [], + recipient.address, + ); + }, 25_000); + + it('should store an outgoing note that belongs to us', async () => { + const request = new MockNoteRequest( + getRandomNoteLogPayload(Fr.random(), contractAddress), + 4, + 0, + 2, + CompleteAddress.random().address, + recipientOvKeys, + ); + + const taggedLogs = mockTaggedLogs([request]); + + await simulatorOracle.processTaggedLogs(taggedLogs, recipient.address, simulator); + + expect(addNotesSpy).toHaveBeenCalledTimes(1); + // For outgoing notes, the resulting DAO does not contain index. + expect(addNotesSpy).toHaveBeenCalledWith( + [], + [expect.objectContaining(request.snippetOfNoteDao)], + recipient.address, + ); + }, 25_000); + + it('should store multiple notes that belong to us', async () => { + const requests = [ + new MockNoteRequest( + getRandomNoteLogPayload(Fr.random(), contractAddress), + 1, + 1, + 1, + recipient.address, + recipientOvKeys, + ), + new MockNoteRequest( + getRandomNoteLogPayload(Fr.random(), contractAddress), + 2, + 3, + 0, + CompleteAddress.random().address, + recipientOvKeys, + ), + new MockNoteRequest( + getRandomNoteLogPayload(Fr.random(), contractAddress), + 6, + 3, + 2, + recipient.address, + KeyValidationRequest.random(), + ), + new MockNoteRequest( + getRandomNoteLogPayload(Fr.random(), contractAddress), + 9, + 3, + 2, + CompleteAddress.random().address, + KeyValidationRequest.random(), + ), + new MockNoteRequest( + getRandomNoteLogPayload(Fr.random(), contractAddress), + 12, + 3, + 2, + recipient.address, + recipientOvKeys, + ), + ]; + + const taggedLogs = mockTaggedLogs(requests); + + await simulatorOracle.processTaggedLogs(taggedLogs, recipient.address, simulator); + + expect(addNotesSpy).toHaveBeenCalledTimes(1); + expect(addNotesSpy).toHaveBeenCalledWith( + // Incoming should contain notes from requests 0, 2, 4 because in those requests we set owner address point. + [ + expect.objectContaining({ + ...requests[0].snippetOfNoteDao, + index: requests[0].indexWithinNoteHashTree, + }), + expect.objectContaining({ + ...requests[2].snippetOfNoteDao, + index: requests[2].indexWithinNoteHashTree, + }), + expect.objectContaining({ + ...requests[4].snippetOfNoteDao, + index: requests[4].indexWithinNoteHashTree, + }), + ], + // Outgoing should contain notes from requests 0, 1, 4 because in those requests we set owner ovKeys. + [ + expect.objectContaining(requests[0].snippetOfNoteDao), + expect.objectContaining(requests[1].snippetOfNoteDao), + expect.objectContaining(requests[4].snippetOfNoteDao), + ], + recipient.address, + ); + }, 30_000); + + it('should not store notes that do not belong to us', async () => { + // Both notes should be ignored because the encryption keys do not belong to owner (they are random). + const requests = [ + new MockNoteRequest( + getRandomNoteLogPayload(), + 2, + 1, + 1, + CompleteAddress.random().address, + KeyValidationRequest.random(), + ), + new MockNoteRequest( + getRandomNoteLogPayload(), + 2, + 3, + 0, + CompleteAddress.random().address, + KeyValidationRequest.random(), + ), + ]; + + const taggedLogs = mockTaggedLogs(requests); + + await simulatorOracle.processTaggedLogs(taggedLogs, recipient.address, simulator); + + expect(addNotesSpy).toHaveBeenCalledTimes(0); + }); + + it('should be able to recover two note payloads containing the same note', async () => { + const note = getRandomNoteLogPayload(Fr.random(), contractAddress); + const note2 = getRandomNoteLogPayload(Fr.random(), contractAddress); + // All note payloads except one have the same contract address, storage slot, and the actual note. + const requests = [ + new MockNoteRequest(note, 3, 0, 0, recipient.address, recipientOvKeys), + new MockNoteRequest(note, 4, 0, 2, recipient.address, recipientOvKeys), + new MockNoteRequest(note, 4, 2, 0, recipient.address, recipientOvKeys), + new MockNoteRequest(note2, 5, 2, 1, recipient.address, recipientOvKeys), + new MockNoteRequest(note, 6, 2, 3, recipient.address, recipientOvKeys), + ]; + + const taggedLogs = mockTaggedLogs(requests); + + await simulatorOracle.processTaggedLogs(taggedLogs, recipient.address, simulator); + + // First we check incoming + { + const addedIncoming: IncomingNoteDao[] = addNotesSpy.mock.calls[0][0]; + expect(addedIncoming.map(dao => dao)).toEqual([ + expect.objectContaining({ ...requests[0].snippetOfNoteDao, index: requests[0].indexWithinNoteHashTree }), + expect.objectContaining({ ...requests[1].snippetOfNoteDao, index: requests[1].indexWithinNoteHashTree }), + expect.objectContaining({ ...requests[2].snippetOfNoteDao, index: requests[2].indexWithinNoteHashTree }), + expect.objectContaining({ ...requests[3].snippetOfNoteDao, index: requests[3].indexWithinNoteHashTree }), + expect.objectContaining({ ...requests[4].snippetOfNoteDao, index: requests[4].indexWithinNoteHashTree }), + ]); + + // Check that every note has a different nonce. + const nonceSet = new Set(); + addedIncoming.forEach(info => nonceSet.add(info.nonce.value)); + expect(nonceSet.size).toBe(requests.length); + } + + // Then we check outgoing + { + const addedOutgoing: OutgoingNoteDao[] = addNotesSpy.mock.calls[0][1]; + expect(addedOutgoing.map(dao => dao)).toEqual([ + expect.objectContaining(requests[0].snippetOfNoteDao), + expect.objectContaining(requests[1].snippetOfNoteDao), + expect.objectContaining(requests[2].snippetOfNoteDao), + expect.objectContaining(requests[3].snippetOfNoteDao), + expect.objectContaining(requests[4].snippetOfNoteDao), + ]); + + // Outgoing note daos do not have a nonce so we don't check it. + } + }); + }); }); diff --git a/yarn-project/simulator/src/acvm/oracle/oracle.ts b/yarn-project/simulator/src/acvm/oracle/oracle.ts index 00f41304842..d2faa8f1ed6 100644 --- a/yarn-project/simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/simulator/src/acvm/oracle/oracle.ts @@ -423,9 +423,4 @@ export class Oracle { AztecAddress.fromString(recipient), ); } - - async getAppTaggingSecretsForSenders([recipient]: ACVMField[]): Promise { - const taggingSecrets = await this.typedOracle.getAppTaggingSecretsForSenders(AztecAddress.fromString(recipient)); - return taggingSecrets.flatMap(taggingSecret => taggingSecret.toFields().map(toACVMField)); - } } diff --git a/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts b/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts index 83aec5ed0d3..4b13ee3be2b 100644 --- a/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts @@ -261,8 +261,4 @@ export abstract class TypedOracle { incrementAppTaggingSecret(_sender: AztecAddress, _recipient: AztecAddress): Promise { throw new OracleMethodNotAvailableError('incrementAppTaggingSecret'); } - - getAppTaggingSecretsForSenders(_recipient: AztecAddress): Promise { - throw new OracleMethodNotAvailableError('getAppTaggingSecretsForSenders'); - } } diff --git a/yarn-project/simulator/src/client/db_oracle.ts b/yarn-project/simulator/src/client/db_oracle.ts index 20ec7f0725d..b0473c75352 100644 --- a/yarn-project/simulator/src/client/db_oracle.ts +++ b/yarn-project/simulator/src/client/db_oracle.ts @@ -198,7 +198,7 @@ export interface DBOracle extends CommitmentsDB { /** * Returns the tagging secret for a given sender and recipient pair. For this to work, the ivpsk_m of the sender must be known. - * Includes the last known index used for tagging with this secret. + * Includes the last seen index used for tagging with this secret, or 0 if this combination hasn't been seen before. * @param contractAddress - The contract address to silo the secret for * @param sender - The address sending the note * @param recipient - The address receiving the note @@ -223,17 +223,18 @@ export interface DBOracle extends CommitmentsDB { ): Promise; /** - * Returns the siloed tagging secrets for a given recipient and all the senders in the address book - * @param contractAddress - The contract address to silo the secret for - * @param recipient - The address receiving the notes - * @returns A list of siloed tagging secrets + * Synchronizes the logs tagged with the recipient's address and all the senders in the addressbook. + * Returns the unsynched logs and updates the indexes of the secrets used to tag them until there are no more logs to sync. + * @param contractAddress - The address of the contract that the logs are tagged for + * @param recipient - The address of the recipient + * @returns A list of encrypted logs tagged with the recipient's address */ - getAppTaggingSecretsForSenders( - contractAddress: AztecAddress, - recipient: AztecAddress, - ): Promise; - syncTaggedLogs(contractAddress: AztecAddress, recipient: AztecAddress): Promise; + /** + * Processes the tagged logs returned by syncTaggedLogs by decrypting them and storing them in the database. + * @param logs - The logs to process. + * @param recipient - The recipient of the logs. + */ processTaggedLogs(logs: TxScopedEncryptedL2NoteLog[], recipient: AztecAddress): Promise; } diff --git a/yarn-project/simulator/src/client/view_data_oracle.ts b/yarn-project/simulator/src/client/view_data_oracle.ts index b8ca0266fa4..3494b8e5747 100644 --- a/yarn-project/simulator/src/client/view_data_oracle.ts +++ b/yarn-project/simulator/src/client/view_data_oracle.ts @@ -296,7 +296,7 @@ export class ViewDataOracle extends TypedOracle { /** * Returns the tagging secret for a given sender and recipient pair, siloed to the current contract address. - * Includes the last known index used for tagging with this secret. + * Includes the last seen index used for tagging with this secret, or 0 if this combination hasn't been seen before. * For this to work, the ivpsk_m of the sender must be known. * @param sender - The address sending the note * @param recipient - The address receiving the note @@ -308,14 +308,4 @@ export class ViewDataOracle extends TypedOracle { ): Promise { return await this.db.getAppTaggingSecret(this.contractAddress, sender, recipient); } - - /** - * Returns the siloed tagging secrets for a given recipient and all the senders in the address book - * @param contractAddress - The contract address to silo the secret for - * @param recipient - The address receiving the notes - * @returns A list of siloed tagging secrets - */ - public override async getAppTaggingSecretsForSenders(recipient: AztecAddress): Promise { - return await this.db.getAppTaggingSecretsForSenders(this.contractAddress, recipient); - } } diff --git a/yarn-project/txe/src/oracle/txe_oracle.ts b/yarn-project/txe/src/oracle/txe_oracle.ts index 01c7dc312bb..6c156858b3f 100644 --- a/yarn-project/txe/src/oracle/txe_oracle.ts +++ b/yarn-project/txe/src/oracle/txe_oracle.ts @@ -778,7 +778,7 @@ export class TXE implements TypedOracle { return directionalSecret; } - async getAppTaggingSecretsForSenders(recipient: AztecAddress): Promise { + async #getAppTaggingSecretsForSenders(recipient: AztecAddress): Promise { const recipientCompleteAddress = await this.getCompleteAddress(recipient); const completeAddresses = await this.txeDatabase.getCompleteAddresses(); // Filter out the addresses corresponding to accounts diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts index 52ceb74f06f..f130f64a01b 100644 --- a/yarn-project/txe/src/txe_service/txe_service.ts +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -608,13 +608,6 @@ export class TXEService { return toForeignCallResult([toArray(secret.toFields())]); } - async getAppTaggingSecretsForSenders(recipient: ForeignCallSingle) { - const secrets = await this.typedOracle.getAppTaggingSecretsForSenders( - AztecAddress.fromField(fromSingle(recipient)), - ); - return toForeignCallResult([toArray(secrets.flatMap(secret => secret.toFields()))]); - } - // AVM opcodes avmOpcodeEmitUnencryptedLog(_message: ForeignCallArray) { From 7e362f3d71ba1d66447f13883c9731b03d5a20a3 Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 5 Nov 2024 06:57:01 +0000 Subject: [PATCH 03/15] fmt --- yarn-project/archiver/src/archiver/archiver_store.ts | 3 +-- .../archiver/kv_archiver_store/kv_archiver_store.ts | 3 +-- .../src/archiver/kv_archiver_store/log_store.ts | 1 - .../memory_archiver_store/memory_archiver_store.ts | 1 - yarn-project/aztec-node/src/aztec-node/server.ts | 3 +-- yarn-project/pxe/src/pxe_service/pxe_service.ts | 4 ---- yarn-project/pxe/src/simulator_oracle/index.ts | 10 +++++----- .../src/simulator_oracle/simulator_oracle.test.ts | 12 ++++++------ yarn-project/simulator/src/client/db_oracle.ts | 2 +- 9 files changed, 15 insertions(+), 24 deletions(-) diff --git a/yarn-project/archiver/src/archiver/archiver_store.ts b/yarn-project/archiver/src/archiver/archiver_store.ts index b1d2c6307ad..5038dd4afeb 100644 --- a/yarn-project/archiver/src/archiver/archiver_store.ts +++ b/yarn-project/archiver/src/archiver/archiver_store.ts @@ -1,5 +1,4 @@ import { - type EncryptedL2NoteLog, type FromLogType, type GetUnencryptedLogsResponse, type InboxLeaf, @@ -10,7 +9,7 @@ import { type TxEffect, type TxHash, type TxReceipt, - TxScopedEncryptedL2NoteLog, + type TxScopedEncryptedL2NoteLog, } from '@aztec/circuit-types'; import { type ContractClassPublic, 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 84430312b02..d7ff5f8c1d5 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 @@ -1,5 +1,4 @@ import { - type EncryptedL2NoteLog, type FromLogType, type GetUnencryptedLogsResponse, type InboxLeaf, @@ -10,7 +9,7 @@ import { type TxEffect, type TxHash, type TxReceipt, - TxScopedEncryptedL2NoteLog, + type TxScopedEncryptedL2NoteLog, } from '@aztec/circuit-types'; import { type ContractClassPublic, 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 3048d3f1284..2cdbb52b34c 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 @@ -1,6 +1,5 @@ import { EncryptedL2BlockL2Logs, - EncryptedL2NoteLog, EncryptedNoteL2BlockL2Logs, ExtendedUnencryptedL2Log, type FromLogType, 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 index c23469fd694..14637a80a10 100644 --- 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 @@ -1,6 +1,5 @@ import { type EncryptedL2BlockL2Logs, - type EncryptedL2NoteLog, type EncryptedNoteL2BlockL2Logs, ExtendedUnencryptedL2Log, type FromLogType, diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index 7818f0984d4..d479c2a83cb 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -3,7 +3,6 @@ import { BBCircuitVerifier, TestCircuitVerifier } from '@aztec/bb-prover'; import { type AztecNode, type ClientProtocolCircuitVerifier, - type EncryptedL2NoteLog, type EpochProofQuote, type FromLogType, type GetUnencryptedLogsResponse, @@ -27,7 +26,7 @@ import { type TxEffect, type TxHash, TxReceipt, - TxScopedEncryptedL2NoteLog, + type TxScopedEncryptedL2NoteLog, TxStatus, type TxValidator, type WorldStateSynchronizer, diff --git a/yarn-project/pxe/src/pxe_service/pxe_service.ts b/yarn-project/pxe/src/pxe_service/pxe_service.ts index e9cd583a719..1e9ed903992 100644 --- a/yarn-project/pxe/src/pxe_service/pxe_service.ts +++ b/yarn-project/pxe/src/pxe_service/pxe_service.ts @@ -28,7 +28,6 @@ import { type TxHash, TxProvingResult, type TxReceipt, - TxScopedEncryptedL2NoteLog, TxSimulationResult, UniqueNote, getNonNullifiedL1ToL2MessageWitness, @@ -71,14 +70,11 @@ import { type AcirSimulator } from '@aztec/simulator'; import { type PXEServiceConfig, getPackageInfo } from '../config/index.js'; import { ContractDataOracle } from '../contract_data_oracle/index.js'; -import { DeferredNoteDao } from '../database/deferred_note_dao.js'; import { IncomingNoteDao } from '../database/incoming_note_dao.js'; import { type PxeDatabase } from '../database/index.js'; -import { OutgoingNoteDao } from '../database/outgoing_note_dao.js'; import { KernelOracle } from '../kernel_oracle/index.js'; import { KernelProver } from '../kernel_prover/kernel_prover.js'; import { TestPrivateKernelProver } from '../kernel_prover/test/test_circuit_prover.js'; -import { produceNoteDaos } from '../note_processor/utils/produce_note_daos.js'; import { getAcirSimulator } from '../simulator/index.js'; import { Synchronizer } from '../synchronizer/index.js'; import { enrichPublicSimulationError, enrichSimulationError } from './error_enriching.js'; diff --git a/yarn-project/pxe/src/simulator_oracle/index.ts b/yarn-project/pxe/src/simulator_oracle/index.ts index 17642e7e26d..aefc0fbaa34 100644 --- a/yarn-project/pxe/src/simulator_oracle/index.ts +++ b/yarn-project/pxe/src/simulator_oracle/index.ts @@ -6,7 +6,7 @@ import { type NoteStatus, type NullifierMembershipWitness, type PublicDataWitness, - TxScopedEncryptedL2NoteLog, + type TxScopedEncryptedL2NoteLog, getNonNullifiedL1ToL2MessageWitness, } from '@aztec/circuit-types'; import { @@ -28,13 +28,13 @@ import { type FunctionArtifact, getFunctionArtifact } from '@aztec/foundation/ab import { poseidon2Hash } from '@aztec/foundation/crypto'; import { createDebugLogger } from '@aztec/foundation/log'; import { type KeyStore } from '@aztec/key-store'; -import { AcirSimulator, type DBOracle, MessageLoadOracleInputs } from '@aztec/simulator'; +import { type AcirSimulator, type DBOracle, MessageLoadOracleInputs } from '@aztec/simulator'; import { type ContractDataOracle } from '../contract_data_oracle/index.js'; -import { DeferredNoteDao } from '../database/deferred_note_dao.js'; -import { IncomingNoteDao } from '../database/incoming_note_dao.js'; +import { type DeferredNoteDao } from '../database/deferred_note_dao.js'; +import { type IncomingNoteDao } from '../database/incoming_note_dao.js'; import { type PxeDatabase } from '../database/index.js'; -import { OutgoingNoteDao } from '../database/outgoing_note_dao.js'; +import { type OutgoingNoteDao } from '../database/outgoing_note_dao.js'; import { produceNoteDaos } from '../note_processor/utils/produce_note_daos.js'; import { getAcirSimulator } from '../simulator/index.js'; diff --git a/yarn-project/pxe/src/simulator_oracle/simulator_oracle.test.ts b/yarn-project/pxe/src/simulator_oracle/simulator_oracle.test.ts index 6c733b8dd1d..a10c21271e0 100644 --- a/yarn-project/pxe/src/simulator_oracle/simulator_oracle.test.ts +++ b/yarn-project/pxe/src/simulator_oracle/simulator_oracle.test.ts @@ -4,7 +4,7 @@ import { EncryptedLogPayload, L1NotePayload, Note, - TxEffect, + type TxEffect, TxHash, TxScopedEncryptedL2NoteLog, } from '@aztec/circuit-types'; @@ -26,16 +26,16 @@ import { import { pedersenHash, poseidon2Hash } from '@aztec/foundation/crypto'; import { KeyStore } from '@aztec/key-store'; import { openTmpStore } from '@aztec/kv-store/utils'; -import { AcirSimulator } from '@aztec/simulator'; +import { type AcirSimulator } from '@aztec/simulator'; import { jest } from '@jest/globals'; import { type MockProxy, mock } from 'jest-mock-extended'; import times from 'lodash.times'; -import { IncomingNoteDao } from '../database/incoming_note_dao.js'; +import { type IncomingNoteDao } from '../database/incoming_note_dao.js'; import { type PxeDatabase } from '../database/index.js'; import { KVPxeDatabase } from '../database/kv_pxe_database.js'; -import { OutgoingNoteDao } from '../database/outgoing_note_dao.js'; +import { type OutgoingNoteDao } from '../database/outgoing_note_dao.js'; import { ContractDataOracle } from '../index.js'; import { type SimulatorOracle } from './index.js'; @@ -280,7 +280,7 @@ describe('Simulator oracle', () => { let addNotesSpy: any; let simulator: MockProxy; - beforeEach(async () => { + beforeEach(() => { addNotesSpy = jest.spyOn(database, 'addNotes'); simulator = mock(); simulator.computeNoteHashAndOptionallyANullifier.mockImplementation((...args: any) => @@ -339,7 +339,7 @@ describe('Simulator oracle', () => { }); }); - aztecNode.getTxEffect.mockImplementation(async txHash => { + aztecNode.getTxEffect.mockImplementation(txHash => { return Promise.resolve(txEffectsMap[txHash.toString()] as TxEffect); }); return taggedLogs; diff --git a/yarn-project/simulator/src/client/db_oracle.ts b/yarn-project/simulator/src/client/db_oracle.ts index b0473c75352..1022579324b 100644 --- a/yarn-project/simulator/src/client/db_oracle.ts +++ b/yarn-project/simulator/src/client/db_oracle.ts @@ -4,7 +4,7 @@ import { type NoteStatus, type NullifierMembershipWitness, type PublicDataWitness, - TxScopedEncryptedL2NoteLog, + type TxScopedEncryptedL2NoteLog, } from '@aztec/circuit-types'; import { type CompleteAddress, From d1f88252dcf8e3f94b60f5c7da43e3ac52dd4157 Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 5 Nov 2024 07:04:57 +0000 Subject: [PATCH 04/15] fixes --- noir-projects/aztec-nr/aztec/src/oracle/notes.nr | 6 +++++- yarn-project/prover-client/src/orchestrator/orchestrator.ts | 1 - yarn-project/pxe/src/simulator_oracle/index.ts | 3 +++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/oracle/notes.nr b/noir-projects/aztec-nr/aztec/src/oracle/notes.nr index df5b743f78d..b57fbddc510 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/notes.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/notes.nr @@ -240,6 +240,7 @@ pub unconstrained fn get_app_tagging_secrets_for_senders( ) -> [IndexedTaggingSecret] { let results = get_app_tagging_secrets_for_senders_oracle(recipient); let mut indexed_tagging_secrets = &[]; + assert(results.len() % 3 == 0, "Invalid result length"); for i in 0..results.len() { if i % 3 != 0 { continue; @@ -260,7 +261,10 @@ pub fn sync_notes(targetContractAddress: AztecAddress, recipient: AztecAddress) } } -unconstrained fn sync_notes_oracle_wrapper(targetContractAddress: AztecAddress, recipient: AztecAddress) { +unconstrained fn sync_notes_oracle_wrapper( + targetContractAddress: AztecAddress, + recipient: AztecAddress, +) { sync_notes_oracle(targetContractAddress, recipient); } diff --git a/yarn-project/prover-client/src/orchestrator/orchestrator.ts b/yarn-project/prover-client/src/orchestrator/orchestrator.ts index 6244b197c43..67bbf75b88d 100644 --- a/yarn-project/prover-client/src/orchestrator/orchestrator.ts +++ b/yarn-project/prover-client/src/orchestrator/orchestrator.ts @@ -26,7 +26,6 @@ import { type Header, L1_TO_L2_MSG_SUBTREE_HEIGHT, L1_TO_L2_MSG_SUBTREE_SIBLING_PATH_LENGTH, - MAX_NOTE_HASHES_PER_TX, type NESTED_RECURSIVE_PROOF_LENGTH, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, NUM_BASE_PARITY_PER_ROOT_PARITY, diff --git a/yarn-project/pxe/src/simulator_oracle/index.ts b/yarn-project/pxe/src/simulator_oracle/index.ts index aefc0fbaa34..8ece0ecc966 100644 --- a/yarn-project/pxe/src/simulator_oracle/index.ts +++ b/yarn-project/pxe/src/simulator_oracle/index.ts @@ -414,6 +414,9 @@ export class SimulatorOracle implements DBOracle { excludedIndices.set(scopedLog.txHash.toString(), new Set()); } const { incomingNote, outgoingNote, incomingDeferredNote, outgoingDeferredNote } = await produceNoteDaos( + // I don't like this at all, but we need a simulator to run `computeNoteHashAndOptionallyANullifier`. This generates + // a chicken-and-egg problem due to this oracle requiring a simulator, which in turn requires this oracle. Furthermore, since jest doesn't allow + // mocking ESM exports, we have to pollute the method even more by providing a simulator parameter so tests can inject a fake one. simulator ?? getAcirSimulator(this.db, this.aztecNode, this.keyStore, this.contractDataOracle), this.db, incomingNotePayload ? computePoint(recipient) : undefined, From 674464ef4c9755821c942aeabd33a40673eba1ce Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 5 Nov 2024 07:22:24 +0000 Subject: [PATCH 05/15] removed unused oracle --- .../aztec-nr/aztec/src/oracle/notes.nr | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/oracle/notes.nr b/noir-projects/aztec-nr/aztec/src/oracle/notes.nr index b57fbddc510..b33bbad66f6 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/notes.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/notes.nr @@ -231,30 +231,6 @@ unconstrained fn increment_app_tagging_secret_oracle( _recipient: AztecAddress, ) {} -/// Returns the tagging secrets for a given recipient and all the senders in PXE's address book, -// siloed for the current contract address. -/// Includes the last known index used for tagging with this secret. -/// For this to work, PXE must know the ivsk_m of the recipient. -pub unconstrained fn get_app_tagging_secrets_for_senders( - recipient: AztecAddress, -) -> [IndexedTaggingSecret] { - let results = get_app_tagging_secrets_for_senders_oracle(recipient); - let mut indexed_tagging_secrets = &[]; - assert(results.len() % 3 == 0, "Invalid result length"); - for i in 0..results.len() { - if i % 3 != 0 { - continue; - } - indexed_tagging_secrets = indexed_tagging_secrets.push_back( - IndexedTaggingSecret::deserialize([results[i], results[i + 1], results[i + 2]]), - ); - } - indexed_tagging_secrets -} - -#[oracle(getAppTaggingSecretsForSenders)] -unconstrained fn get_app_tagging_secrets_for_senders_oracle(_recipient: AztecAddress) -> [Field] {} - pub fn sync_notes(targetContractAddress: AztecAddress, recipient: AztecAddress) { unsafe { sync_notes_oracle_wrapper(targetContractAddress, recipient); From 65611bf7c8509c71bd4b75be86d63b2946581e7d Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 5 Nov 2024 07:24:36 +0000 Subject: [PATCH 06/15] comment --- yarn-project/pxe/src/database/kv_pxe_database.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/yarn-project/pxe/src/database/kv_pxe_database.ts b/yarn-project/pxe/src/database/kv_pxe_database.ts index d44ce292c6d..9c6f25092d0 100644 --- a/yarn-project/pxe/src/database/kv_pxe_database.ts +++ b/yarn-project/pxe/src/database/kv_pxe_database.ts @@ -64,6 +64,7 @@ export class KVPxeDatabase implements PxeDatabase { #notesByTxHashAndScope: Map>; #notesByAddressPointAndScope: Map>; + // Stores the last index used for each tagging secret #taggingSecretIndexes: AztecMap; constructor(private db: AztecKVStore) { From 5d0dc8f04ccef4b197bf41e6fe6808be6e2416ca Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 5 Nov 2024 09:22:46 +0000 Subject: [PATCH 07/15] fmt --- yarn-project/circuit-types/src/interfaces/aztec-node.ts | 1 - yarn-project/circuit-types/src/logs/get_logs_response.ts | 2 +- yarn-project/pxe/src/pxe_service/pxe_service.ts | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/yarn-project/circuit-types/src/interfaces/aztec-node.ts b/yarn-project/circuit-types/src/interfaces/aztec-node.ts index b309a22d646..02ca22bbf72 100644 --- a/yarn-project/circuit-types/src/interfaces/aztec-node.ts +++ b/yarn-project/circuit-types/src/interfaces/aztec-node.ts @@ -16,7 +16,6 @@ import type { Fr } from '@aztec/foundation/fields'; import type { L2Block } from '../l2_block.js'; import type { - EncryptedL2NoteLog, FromLogType, GetUnencryptedLogsResponse, L2BlockL2Logs, diff --git a/yarn-project/circuit-types/src/logs/get_logs_response.ts b/yarn-project/circuit-types/src/logs/get_logs_response.ts index 698369c7e12..0e431472ef5 100644 --- a/yarn-project/circuit-types/src/logs/get_logs_response.ts +++ b/yarn-project/circuit-types/src/logs/get_logs_response.ts @@ -1,7 +1,7 @@ import { Fr } from '@aztec/circuits.js'; import { BufferReader, numToUInt32BE } from '@aztec/foundation/serialize'; -import { EncryptedL2NoteLog, Tx, TxHash } from '../index.js'; +import { EncryptedL2NoteLog, TxHash } from '../index.js'; import { type ExtendedUnencryptedL2Log } from './extended_unencrypted_l2_log.js'; /** diff --git a/yarn-project/pxe/src/pxe_service/pxe_service.ts b/yarn-project/pxe/src/pxe_service/pxe_service.ts index 498e301b318..fec27dda7cc 100644 --- a/yarn-project/pxe/src/pxe_service/pxe_service.ts +++ b/yarn-project/pxe/src/pxe_service/pxe_service.ts @@ -8,7 +8,6 @@ import { type GetUnencryptedLogsResponse, type IncomingNotesFilter, L1EventPayload, - L1NotePayload, type L2Block, type LogFilter, MerkleTreeId, From 7f277c3b6cab2811a7b68114cc4925a026747dcf Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 5 Nov 2024 10:33:09 +0000 Subject: [PATCH 08/15] refining oracles --- noir-projects/aztec-nr/aztec/src/oracle/notes.nr | 13 +++++-------- yarn-project/simulator/src/acvm/oracle/oracle.ts | 4 ++++ .../simulator/src/acvm/oracle/typed_oracle.ts | 4 ++++ .../src/client/client_execution_context.ts | 4 ++-- yarn-project/txe/src/oracle/txe_oracle.ts | 5 +++++ yarn-project/txe/src/txe_service/txe_service.ts | 5 +++++ 6 files changed, 25 insertions(+), 10 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/oracle/notes.nr b/noir-projects/aztec-nr/aztec/src/oracle/notes.nr index b33bbad66f6..cff8a0739b4 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/notes.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/notes.nr @@ -231,18 +231,15 @@ unconstrained fn increment_app_tagging_secret_oracle( _recipient: AztecAddress, ) {} -pub fn sync_notes(targetContractAddress: AztecAddress, recipient: AztecAddress) { +pub fn sync_notes(recipient: AztecAddress) { unsafe { - sync_notes_oracle_wrapper(targetContractAddress, recipient); + sync_notes_oracle_wrapper(recipient); } } -unconstrained fn sync_notes_oracle_wrapper( - targetContractAddress: AztecAddress, - recipient: AztecAddress, -) { - sync_notes_oracle(targetContractAddress, recipient); +unconstrained fn sync_notes_oracle_wrapper(recipient: AztecAddress) { + sync_notes_oracle(recipient); } #[oracle(syncNotes)] -unconstrained fn sync_notes_oracle(_targetContractAddress: AztecAddress, _recipient: AztecAddress) {} +unconstrained fn sync_notes_oracle(_recipient: AztecAddress) {} diff --git a/yarn-project/simulator/src/acvm/oracle/oracle.ts b/yarn-project/simulator/src/acvm/oracle/oracle.ts index d2faa8f1ed6..cb4f063cf1b 100644 --- a/yarn-project/simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/simulator/src/acvm/oracle/oracle.ts @@ -423,4 +423,8 @@ export class Oracle { AztecAddress.fromString(recipient), ); } + + async syncNotes([recipient]: ACVMField[]) { + await this.typedOracle.syncNotes(AztecAddress.fromString(recipient)); + } } diff --git a/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts b/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts index 4b13ee3be2b..76fa4b31f2f 100644 --- a/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts @@ -261,4 +261,8 @@ export abstract class TypedOracle { incrementAppTaggingSecret(_sender: AztecAddress, _recipient: AztecAddress): Promise { throw new OracleMethodNotAvailableError('incrementAppTaggingSecret'); } + + syncNotes(_recipient: AztecAddress): Promise { + throw new OracleMethodNotAvailableError('syncNotes'); + } } diff --git a/yarn-project/simulator/src/client/client_execution_context.ts b/yarn-project/simulator/src/client/client_execution_context.ts index ac4adb49a7c..be7e8bcde19 100644 --- a/yarn-project/simulator/src/client/client_execution_context.ts +++ b/yarn-project/simulator/src/client/client_execution_context.ts @@ -614,8 +614,8 @@ export class ClientExecutionContext extends ViewDataOracle { await this.db.incrementAppTaggingSecret(this.contractAddress, sender, recipient); } - public async syncNotes(targetContractAddress: AztecAddress, recipient: AztecAddress) { - const taggedLogs = await this.db.syncTaggedLogs(targetContractAddress, recipient); + public override async syncNotes(recipient: AztecAddress) { + const taggedLogs = await this.db.syncTaggedLogs(this.contractAddress, recipient); await this.db.processTaggedLogs(taggedLogs, recipient); } } diff --git a/yarn-project/txe/src/oracle/txe_oracle.ts b/yarn-project/txe/src/oracle/txe_oracle.ts index 3fcd5592f1c..d4bd7c0af79 100644 --- a/yarn-project/txe/src/oracle/txe_oracle.ts +++ b/yarn-project/txe/src/oracle/txe_oracle.ts @@ -800,6 +800,11 @@ export class TXE implements TypedOracle { return secrets.map((secret, i) => new IndexedTaggingSecret(secret, recipient, indexes[i])); } + async syncNotes(_recipient: AztecAddress) { + // TODO: Implement + return Promise.resolve(); + } + // AVM oracles async avmOpcodeCall(targetContractAddress: AztecAddress, args: Fr[], isStaticCall: boolean) { diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts index 1b167b318ec..a8433c56061 100644 --- a/yarn-project/txe/src/txe_service/txe_service.ts +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -608,6 +608,11 @@ export class TXEService { return toForeignCallResult([toArray(secret.toFields())]); } + async syncNotes(recipient: ForeignCallSingle) { + await this.typedOracle.syncNotes(AztecAddress.fromField(fromSingle(recipient))); + return toForeignCallResult([]); + } + // AVM opcodes avmOpcodeEmitUnencryptedLog(_message: ForeignCallArray) { From 6ef60522fe6df77b293f37959c747f655464e910 Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 5 Nov 2024 12:02:06 +0000 Subject: [PATCH 09/15] fmt --- yarn-project/txe/src/oracle/txe_oracle.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn-project/txe/src/oracle/txe_oracle.ts b/yarn-project/txe/src/oracle/txe_oracle.ts index d4bd7c0af79..f75ceb5a893 100644 --- a/yarn-project/txe/src/oracle/txe_oracle.ts +++ b/yarn-project/txe/src/oracle/txe_oracle.ts @@ -800,7 +800,7 @@ export class TXE implements TypedOracle { return secrets.map((secret, i) => new IndexedTaggingSecret(secret, recipient, indexes[i])); } - async syncNotes(_recipient: AztecAddress) { + syncNotes(_recipient: AztecAddress) { // TODO: Implement return Promise.resolve(); } From 0216142503de980e7a51daa24466c7160ed1af7b Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 5 Nov 2024 14:23:05 +0000 Subject: [PATCH 10/15] clarified comments --- yarn-project/pxe/src/simulator_oracle/index.ts | 2 +- yarn-project/simulator/src/client/db_oracle.ts | 2 +- yarn-project/simulator/src/client/view_data_oracle.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn-project/pxe/src/simulator_oracle/index.ts b/yarn-project/pxe/src/simulator_oracle/index.ts index 8ece0ecc966..7706dd86fd5 100644 --- a/yarn-project/pxe/src/simulator_oracle/index.ts +++ b/yarn-project/pxe/src/simulator_oracle/index.ts @@ -252,7 +252,7 @@ export class SimulatorOracle implements DBOracle { /** * Returns the tagging secret for a given sender and recipient pair. For this to work, the ivpsk_m of the sender must be known. - * Includes the last seen index used for tagging with this secret, or 0 if this combination hasn't been seen before. + * Includes the next index to be used used for tagging with this secret. * @param contractAddress - The contract address to silo the secret for * @param sender - The address sending the note * @param recipient - The address receiving the note diff --git a/yarn-project/simulator/src/client/db_oracle.ts b/yarn-project/simulator/src/client/db_oracle.ts index 1022579324b..304ea84d76c 100644 --- a/yarn-project/simulator/src/client/db_oracle.ts +++ b/yarn-project/simulator/src/client/db_oracle.ts @@ -198,7 +198,7 @@ export interface DBOracle extends CommitmentsDB { /** * Returns the tagging secret for a given sender and recipient pair. For this to work, the ivpsk_m of the sender must be known. - * Includes the last seen index used for tagging with this secret, or 0 if this combination hasn't been seen before. + * Includes the next index to be used used for tagging with this secret. * @param contractAddress - The contract address to silo the secret for * @param sender - The address sending the note * @param recipient - The address receiving the note diff --git a/yarn-project/simulator/src/client/view_data_oracle.ts b/yarn-project/simulator/src/client/view_data_oracle.ts index 3494b8e5747..57fb6695e23 100644 --- a/yarn-project/simulator/src/client/view_data_oracle.ts +++ b/yarn-project/simulator/src/client/view_data_oracle.ts @@ -296,7 +296,7 @@ export class ViewDataOracle extends TypedOracle { /** * Returns the tagging secret for a given sender and recipient pair, siloed to the current contract address. - * Includes the last seen index used for tagging with this secret, or 0 if this combination hasn't been seen before. + * Includes the next index to be used used for tagging with this secret. * For this to work, the ivpsk_m of the sender must be known. * @param sender - The address sending the note * @param recipient - The address receiving the note From e3fa943341bcdfe81e530b8a7862f89097df821e Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 5 Nov 2024 15:41:13 +0000 Subject: [PATCH 11/15] handle deferred and nullified notes --- .../pxe/src/simulator_oracle/index.ts | 39 ++++++++++++------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/yarn-project/pxe/src/simulator_oracle/index.ts b/yarn-project/pxe/src/simulator_oracle/index.ts index 7706dd86fd5..77433e05a98 100644 --- a/yarn-project/pxe/src/simulator_oracle/index.ts +++ b/yarn-project/pxe/src/simulator_oracle/index.ts @@ -445,21 +445,7 @@ export class SimulatorOracle implements DBOracle { } } if (deferredIncomingNotes.length || deferredOutgoingNotes.length) { - await this.db.addDeferredNotes([...deferredIncomingNotes, ...deferredOutgoingNotes]); - deferredIncomingNotes.forEach(noteDao => { - this.log.verbose( - `Deferred incoming note for contract ${noteDao.payload.contractAddress} at slot ${ - noteDao.payload.storageSlot - } in tx ${noteDao.txHash.toString()}`, - ); - }); - deferredOutgoingNotes.forEach(noteDao => { - this.log.verbose( - `Deferred outgoing note for contract ${noteDao.payload.contractAddress} at slot ${ - noteDao.payload.storageSlot - } in tx ${noteDao.txHash.toString()}`, - ); - }); + throw new Error('Found deferred notes when processing tagged logs. This should not happen.'); } if (incomingNotes.length || outgoingNotes.length) { await this.db.addNotes(incomingNotes, outgoingNotes, recipient); @@ -474,5 +460,28 @@ export class SimulatorOracle implements DBOracle { this.log.verbose(`Added outgoing note for contract ${noteDao.contractAddress} at slot ${noteDao.storageSlot}`); }); } + const nullifiedNotes: IncomingNoteDao[] = []; + for (const incomingNote of incomingNotes) { + // NOTE: this leaks information about the nullifiers I'm interested in to the node. + const found = await this.aztecNode.findLeafIndex( + 'latest', + MerkleTreeId.NULLIFIER_TREE, + incomingNote.siloedNullifier, + ); + if (found) { + nullifiedNotes.push(incomingNote); + } + } + await this.db.removeNullifiedNotes( + nullifiedNotes.map(note => note.siloedNullifier), + computePoint(recipient), + ); + nullifiedNotes.forEach(noteDao => { + this.log.verbose( + `Removed note for contract ${noteDao.contractAddress} at slot ${ + noteDao.storageSlot + } with nullifier ${noteDao.siloedNullifier.toString()}`, + ); + }); } } From 15a0564c913369b3118ac10bdde55266f0f7df7d Mon Sep 17 00:00:00 2001 From: Gregorio Juliana Date: Thu, 7 Nov 2024 06:44:56 +0100 Subject: [PATCH 12/15] Update noir-projects/aztec-nr/aztec/src/oracle/notes.nr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Nicolás Venturo --- noir-projects/aztec-nr/aztec/src/oracle/notes.nr | 1 + 1 file changed, 1 insertion(+) diff --git a/noir-projects/aztec-nr/aztec/src/oracle/notes.nr b/noir-projects/aztec-nr/aztec/src/oracle/notes.nr index cff8a0739b4..9a733d96979 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/notes.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/notes.nr @@ -232,6 +232,7 @@ unconstrained fn increment_app_tagging_secret_oracle( ) {} pub fn sync_notes(recipient: AztecAddress) { + // This oracle call returns nothing: we only call it for its side effects. It is therefore always safe to call. unsafe { sync_notes_oracle_wrapper(recipient); } From 63af392bd3c973750143cf1e5e031a898f7d67aa Mon Sep 17 00:00:00 2001 From: Gregorio Juliana Date: Thu, 7 Nov 2024 06:45:04 +0100 Subject: [PATCH 13/15] Update noir-projects/aztec-nr/aztec/src/oracle/notes.nr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Nicolás Venturo --- noir-projects/aztec-nr/aztec/src/oracle/notes.nr | 2 ++ 1 file changed, 2 insertions(+) diff --git a/noir-projects/aztec-nr/aztec/src/oracle/notes.nr b/noir-projects/aztec-nr/aztec/src/oracle/notes.nr index 9a733d96979..f9ab2dc07fc 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/notes.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/notes.nr @@ -231,6 +231,8 @@ unconstrained fn increment_app_tagging_secret_oracle( _recipient: AztecAddress, ) {} +/// Finds new notes that may have been sent to `recipient` in the current contract and makes them available +/// for later querying via the `get_notes` oracle. pub fn sync_notes(recipient: AztecAddress) { // This oracle call returns nothing: we only call it for its side effects. It is therefore always safe to call. unsafe { From 27b348068467cdd5b2b8dc6d557c5193a877e06b Mon Sep 17 00:00:00 2001 From: Gregorio Juliana Date: Thu, 7 Nov 2024 06:45:15 +0100 Subject: [PATCH 14/15] Update yarn-project/circuit-types/src/interfaces/aztec-node.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Nicolás Venturo --- yarn-project/circuit-types/src/interfaces/aztec-node.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yarn-project/circuit-types/src/interfaces/aztec-node.ts b/yarn-project/circuit-types/src/interfaces/aztec-node.ts index 02ca22bbf72..e2d2ac343bc 100644 --- a/yarn-project/circuit-types/src/interfaces/aztec-node.ts +++ b/yarn-project/circuit-types/src/interfaces/aztec-node.ts @@ -255,8 +255,8 @@ export interface AztecNode extends ProverCoordination { /** * 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. + * @returns For each received tag, an array of matching logs and metadata (e.g. tx hash) is returned. An empty + array implies no logs match that tag. */ getLogsByTags(tags: Fr[]): Promise; From 4de9b3bcd3676f2531aabb64a910e733a22312a1 Mon Sep 17 00:00:00 2001 From: thunkar Date: Thu, 7 Nov 2024 06:10:09 +0000 Subject: [PATCH 15/15] comments from review --- .../src/logs/get_logs_response.ts | 16 ++++++- .../pxe/src/simulator_oracle/index.ts | 45 ++++++++++++++----- 2 files changed, 50 insertions(+), 11 deletions(-) diff --git a/yarn-project/circuit-types/src/logs/get_logs_response.ts b/yarn-project/circuit-types/src/logs/get_logs_response.ts index 0e431472ef5..68b1dbb9068 100644 --- a/yarn-project/circuit-types/src/logs/get_logs_response.ts +++ b/yarn-project/circuit-types/src/logs/get_logs_response.ts @@ -20,7 +20,21 @@ export type GetUnencryptedLogsResponse = { }; export class TxScopedEncryptedL2NoteLog { - constructor(public txHash: TxHash, public dataStartIndexForTx: number, public log: EncryptedL2NoteLog) {} + constructor( + /* + * Hash of the tx where the log is included + */ + public txHash: TxHash, + /* + * The next available leaf index for the note hash tree for this transaction. It is stored + * with the log so the noteHashIndex can be reconstructed after decryption. + */ + public dataStartIndexForTx: number, + /* + * The encrypted note log + */ + public log: EncryptedL2NoteLog, + ) {} toBuffer() { return Buffer.concat([this.txHash.toBuffer(), numToUInt32BE(this.dataStartIndexForTx), this.log.toBuffer()]); diff --git a/yarn-project/pxe/src/simulator_oracle/index.ts b/yarn-project/pxe/src/simulator_oracle/index.ts index 77433e05a98..425603d6516 100644 --- a/yarn-project/pxe/src/simulator_oracle/index.ts +++ b/yarn-project/pxe/src/simulator_oracle/index.ts @@ -296,6 +296,9 @@ export class SimulatorOracle implements DBOracle { /** * Returns the siloed tagging secrets for a given recipient and all the senders in the address book + * This method should be exposed as an oracle call to allow aztec.nr to perform the orchestration + * of the syncTaggedLogs and processTaggedLogs methods. However, it is not possible to do so at the moment, + * so we're keeping it private for now. * @param contractAddress - The contract address to silo the secret for * @param recipient - The address receiving the notes * @returns A list of siloed tagging secrets @@ -367,15 +370,17 @@ export class SimulatorOracle implements DBOracle { } /** - * Processes the tagged logs returned by syncTaggedLogs by decrypting them and storing them in the database. - * @param logs - The logs to process. + * Decrypts logs tagged for a recipient and returns them. + * @param scopedLogs - The logs to decrypt. * @param recipient - The recipient of the logs. + * @param simulator - The simulator to use for decryption. + * @returns The decrypted notes. */ - public async processTaggedLogs( - logs: TxScopedEncryptedL2NoteLog[], + async #decryptTaggedLogs( + scopedLogs: TxScopedEncryptedL2NoteLog[], recipient: AztecAddress, - simulator?: AcirSimulator, - ): Promise { + simulator: AcirSimulator, + ) { const recipientCompleteAddress = await this.getCompleteAddress(recipient); const ivskM = await this.keyStore.getMasterSecretKey( recipientCompleteAddress.publicKeys.masterIncomingViewingPublicKey, @@ -391,13 +396,13 @@ export class SimulatorOracle implements DBOracle { const outgoingNotes: OutgoingNoteDao[] = []; const deferredIncomingNotes: DeferredNoteDao[] = []; const deferredOutgoingNotes: DeferredNoteDao[] = []; - for (const scopedLog of logs) { + for (const scopedLog of scopedLogs) { const incomingNotePayload = L1NotePayload.decryptAsIncoming(scopedLog.log.data, addressSecret); const outgoingNotePayload = L1NotePayload.decryptAsOutgoing(scopedLog.log.data, ovskM); if (incomingNotePayload || outgoingNotePayload) { if (incomingNotePayload && outgoingNotePayload && !incomingNotePayload.equals(outgoingNotePayload)) { - throw new Error( + this.log.warn( `Incoming and outgoing note payloads do not match. Incoming: ${JSON.stringify( incomingNotePayload, )}, Outgoing: ${JSON.stringify(outgoingNotePayload)}`, @@ -408,7 +413,8 @@ export class SimulatorOracle implements DBOracle { const txEffect = await this.aztecNode.getTxEffect(scopedLog.txHash); if (!txEffect) { - throw new Error(`No tx effect found for ${scopedLog.txHash}`); + this.log.warn(`No tx effect found for ${scopedLog.txHash} while decrypting tagged logs`); + continue; } if (!excludedIndices.has(scopedLog.txHash.toString())) { excludedIndices.set(scopedLog.txHash.toString(), new Set()); @@ -445,8 +451,27 @@ export class SimulatorOracle implements DBOracle { } } if (deferredIncomingNotes.length || deferredOutgoingNotes.length) { - throw new Error('Found deferred notes when processing tagged logs. This should not happen.'); + this.log.warn('Found deferred notes when processing tagged logs. This should not happen.'); } + + return { incomingNotes, outgoingNotes }; + } + + /** + * Processes the tagged logs returned by syncTaggedLogs by decrypting them and storing them in the database. + * @param logs - The logs to process. + * @param recipient - The recipient of the logs. + */ + public async processTaggedLogs( + logs: TxScopedEncryptedL2NoteLog[], + recipient: AztecAddress, + simulator?: AcirSimulator, + ): Promise { + const { incomingNotes, outgoingNotes } = await this.#decryptTaggedLogs( + logs, + recipient, + simulator ?? getAcirSimulator(this.db, this.aztecNode, this.keyStore, this.contractDataOracle), + ); if (incomingNotes.length || outgoingNotes.length) { await this.db.addNotes(incomingNotes, outgoingNotes, recipient); incomingNotes.forEach(noteDao => {