diff --git a/noir-projects/aztec-nr/aztec/src/history/note/test.nr b/noir-projects/aztec-nr/aztec/src/history/note/test.nr index 783959e9f8f5..b41f31f7490e 100644 --- a/noir-projects/aztec-nr/aztec/src/history/note/test.nr +++ b/noir-projects/aztec-nr/aztec/src/history/note/test.nr @@ -42,7 +42,7 @@ unconstrained fn succeeds_on_blocks_after_creation_and_before_nullification() { }); } -#[test(should_fail_with = "Proving nullifier non-inclusion failed")] +#[test(should_fail_with = "Cannot prove nullifier non-inclusion")] unconstrained fn fails_on_blocks_after_note_nullification() { let (env, hinted_note) = test::create_note_and_nullify_it(); diff --git a/noir-projects/aztec-nr/aztec/src/history/nullifier/test.nr b/noir-projects/aztec-nr/aztec/src/history/nullifier/test.nr index 8b754f67759d..a83e216854b3 100644 --- a/noir-projects/aztec-nr/aztec/src/history/nullifier/test.nr +++ b/noir-projects/aztec-nr/aztec/src/history/nullifier/test.nr @@ -79,7 +79,7 @@ unconstrained fn note_not_nullified_succeeds_in_blocks_before_note_nullification ); } -#[test(should_fail_with = "Proving nullifier non-inclusion failed")] +#[test(should_fail_with = "Cannot prove nullifier non-inclusion")] unconstrained fn note_not_nullified_fails_in_blocks_after_note_nullification_fails() { let (env, hinted_note) = test::create_note_and_nullify_it(); @@ -100,7 +100,7 @@ unconstrained fn nullifier_non_inclusion_succeeds_in_blocks_before_nullifier_cre }); } -#[test(should_fail_with = "Proving nullifier non-inclusion failed")] +#[test(should_fail_with = "Cannot prove nullifier non-inclusion")] unconstrained fn nullifier_non_inclusion_fails_in_blocks_after_nullifier_creation() { let env = TestEnvironment::new(); diff --git a/noir-projects/noir-contracts/contracts/test/test_contract/src/test/deployment_proofs.nr b/noir-projects/noir-contracts/contracts/test/test_contract/src/test/deployment_proofs.nr index 03adb43fcb81..c285062e440a 100644 --- a/noir-projects/noir-contracts/contracts/test/test_contract/src/test/deployment_proofs.nr +++ b/noir-projects/noir-contracts/contracts/test/test_contract/src/test/deployment_proofs.nr @@ -103,7 +103,7 @@ unconstrained fn assert_contract_was_initialized_by_before_initialization_fails( ); } -#[test(should_fail_with = "Proving nullifier non-inclusion failed")] +#[test(should_fail_with = "Cannot prove nullifier non-inclusion")] unconstrained fn assert_contract_bytecode_was_not_published_by_of_deployed_fails() { let (env, contract_address, _owner) = setup(); @@ -119,7 +119,7 @@ unconstrained fn assert_contract_bytecode_was_not_published_by_of_deployed_fails ); } -#[test(should_fail_with = "Proving nullifier non-inclusion failed")] +#[test(should_fail_with = "Cannot prove nullifier non-inclusion")] unconstrained fn assert_contract_was_not_initialized_by_of_initialized_fails() { let (env, contract_address, _owner) = setup(); diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/delayed_public_mutable/delayed_public_mutable_values/test.nr b/noir-projects/noir-protocol-circuits/crates/types/src/delayed_public_mutable/delayed_public_mutable_values/test.nr index acfb537c9869..c4d409d11f0b 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/delayed_public_mutable/delayed_public_mutable_values/test.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/delayed_public_mutable/delayed_public_mutable_values/test.nr @@ -74,7 +74,7 @@ unconstrained fn packed_delayed_public_mutable_values_match_typescript() { let pre_value = MockStruct { a: 1, b: 2 }; let post_value = MockStruct { a: 3, b: 4 }; - let sdc = ScheduledDelayChange::<0u64>::new(Option::some(1), Option::some(50), 2); + let sdc = ScheduledDelayChange::<0_u64>::new(Option::some(1), Option::some(50), 2); let svc = ScheduledValueChange::new(pre_value, post_value, 50); let dpmv = DelayedPublicMutableValues::new(svc, sdc); diff --git a/yarn-project/archiver/src/store/kv_archiver_store.test.ts b/yarn-project/archiver/src/store/kv_archiver_store.test.ts index 413513dba5de..4e70ce10aa1c 100644 --- a/yarn-project/archiver/src/store/kv_archiver_store.test.ts +++ b/yarn-project/archiver/src/store/kv_archiver_store.test.ts @@ -2997,6 +2997,24 @@ describe('KVArchiverDataStore', () => { } }); + it('"tag" filter param is respected', async () => { + // Get a random tag from the logs + const targetBlockIndex = randomInt(numBlocksForPublicLogs); + const targetBlock = publishedCheckpoints[targetBlockIndex].checkpoint.blocks[0]; + const targetTxIndex = randomInt(getTxsPerBlock(targetBlock)); + const targetLogIndex = randomInt(getPublicLogsPerTx(targetBlock, targetTxIndex)); + const targetTag = targetBlock.body.txEffects[targetTxIndex].publicLogs[targetLogIndex].fields[0]; + + const response = await store.getPublicLogs({ tag: targetTag }); + + expect(response.maxLogsHit).toBeFalsy(); + expect(response.logs.length).toBeGreaterThan(0); + + for (const extendedLog of response.logs) { + expect(extendedLog.log.fields[0].equals(targetTag)).toBeTruthy(); + } + }); + it('"afterLog" filter param is respected', async () => { // Get a random log as reference const targetBlockIndex = randomInt(numBlocksForPublicLogs); @@ -3032,13 +3050,13 @@ describe('KVArchiverDataStore', () => { } }); - it('"txHash" filter param is ignored when "afterLog" is set', async () => { - // Get random txHash + it('"txHash" filter param is respected when "afterLog" is set', async () => { + // A random txHash should match nothing, even with afterLog set const txHash = TxHash.random(); const afterLog = new LogId(BlockNumber(1), BlockHash.random(), TxHash.random(), 0, 0); const response = await store.getPublicLogs({ txHash, afterLog }); - expect(response.logs.length).toBeGreaterThan(1); + expect(response.logs.length).toBe(0); }); it('intersecting works', async () => { diff --git a/yarn-project/archiver/src/store/log_store.ts b/yarn-project/archiver/src/store/log_store.ts index a591964b248d..5ef8656acc4d 100644 --- a/yarn-project/archiver/src/store/log_store.ts +++ b/yarn-project/archiver/src/store/log_store.ts @@ -633,11 +633,24 @@ export class LogStore { txLogs: PublicLog[], filter: LogFilter = {}, ): boolean { + if (filter.fromBlock && blockNumber < filter.fromBlock) { + return false; + } + if (filter.toBlock && blockNumber >= filter.toBlock) { + return false; + } + if (filter.txHash && !txHash.equals(filter.txHash)) { + return false; + } + let maxLogsHit = false; let logIndex = typeof filter.afterLog?.logIndex === 'number' ? filter.afterLog.logIndex + 1 : 0; for (; logIndex < txLogs.length; logIndex++) { const log = txLogs[logIndex]; - if (!filter.contractAddress || log.contractAddress.equals(filter.contractAddress)) { + if ( + (!filter.contractAddress || log.contractAddress.equals(filter.contractAddress)) && + (!filter.tag || log.fields[0]?.equals(filter.tag)) + ) { results.push( new ExtendedPublicLog(new LogId(BlockNumber(blockNumber), blockHash, txHash, txIndex, logIndex), log), ); @@ -661,6 +674,16 @@ export class LogStore { txLogs: ContractClassLog[], filter: LogFilter = {}, ): boolean { + if (filter.fromBlock && blockNumber < filter.fromBlock) { + return false; + } + if (filter.toBlock && blockNumber >= filter.toBlock) { + return false; + } + if (filter.txHash && !txHash.equals(filter.txHash)) { + return false; + } + let maxLogsHit = false; let logIndex = typeof filter.afterLog?.logIndex === 'number' ? filter.afterLog.logIndex + 1 : 0; for (; logIndex < txLogs.length; logIndex++) { diff --git a/yarn-project/aztec-node/src/aztec-node/server.test.ts b/yarn-project/aztec-node/src/aztec-node/server.test.ts index cb63b42ea898..37f3adca164f 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.test.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.test.ts @@ -408,6 +408,33 @@ describe('aztec node', () => { }); }); + describe('getLowNullifierMembershipWitness', () => { + beforeEach(() => { + lastBlockNumber = BlockNumber(1); + }); + + it('throws when nullifier already exists in the tree', async () => { + const nullifier = Fr.random(); + merkleTreeOps.getPreviousValueIndex.mockImplementation((treeId: MerkleTreeId, value: bigint) => { + if (treeId === MerkleTreeId.NULLIFIER_TREE && value === nullifier.toBigInt()) { + return Promise.resolve({ index: 42n, alreadyPresent: true }); + } + return Promise.resolve(undefined); + }); + + await expect(node.getLowNullifierMembershipWitness('latest', nullifier)).rejects.toThrow( + /Cannot prove nullifier non-inclusion/, + ); + }); + + it('returns undefined when nullifier not found', async () => { + merkleTreeOps.getPreviousValueIndex.mockResolvedValue(undefined); + + const result = await node.getLowNullifierMembershipWitness('latest', Fr.random()); + expect(result).toBeUndefined(); + }); + }); + describe('findLeavesIndexes', () => { const blockHash1 = Fr.random(); const blockHash2 = Fr.random(); diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index e6c2108e985d..172286005cbb 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -1131,21 +1131,6 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable { return new NullifierMembershipWitness(index, leafPreimage as NullifierLeafPreimage, path); } - /** - * Returns a low nullifier membership witness for a given nullifier at a given block. - * @param referenceBlock - The block parameter (block number, block hash, or 'latest') at which to get the data - * (which contains the root of the nullifier tree in which we are searching for the nullifier). - * @param nullifier - Nullifier we try to find the low nullifier witness for. - * @returns The low nullifier membership witness (if found). - * @remarks Low nullifier witness can be used to perform a nullifier non-inclusion proof by leveraging the "linked - * list structure" of leaves and proving that a lower nullifier is pointing to a bigger next value than the nullifier - * we are trying to prove non-inclusion for. - * - * Note: This function returns the membership witness of the nullifier itself and not the low nullifier when - * the nullifier already exists in the tree. This is because the `getPreviousValueIndex` function returns the - * index of the nullifier itself when it already exists in the tree. - * TODO: This is a confusing behavior and we should eventually address that. - */ public async getLowNullifierMembershipWitness( referenceBlock: BlockParameter, nullifier: Fr, @@ -1157,7 +1142,9 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable { } const { index, alreadyPresent } = findResult; if (alreadyPresent) { - this.log.warn(`Nullifier ${nullifier.toBigInt()} already exists in the tree`); + throw new Error( + `Cannot prove nullifier non-inclusion: nullifier ${nullifier.toBigInt()} already exists in the tree`, + ); } const preimageData = (await committedDb.getLeafPreimage(MerkleTreeId.NULLIFIER_TREE, index))!; diff --git a/yarn-project/pxe/src/contract_function_simulator/contract_function_simulator.ts b/yarn-project/pxe/src/contract_function_simulator/contract_function_simulator.ts index a81e986f162a..6b1bd80e6baf 100644 --- a/yarn-project/pxe/src/contract_function_simulator/contract_function_simulator.ts +++ b/yarn-project/pxe/src/contract_function_simulator/contract_function_simulator.ts @@ -17,6 +17,7 @@ import { MAX_NULLIFIERS_PER_TX, MAX_NULLIFIER_READ_REQUESTS_PER_TX, MAX_PRIVATE_LOGS_PER_TX, + MAX_TX_LIFETIME, PRIVATE_TX_L2_GAS_OVERHEAD, PUBLIC_TX_L2_GAS_OVERHEAD, TX_DA_GAS_OVERHEAD, @@ -442,12 +443,23 @@ export async function generateSimulatedProvingResult( let publicTeardownCallRequest; + // We set expiration timestamp to anchor_block_timestamp + MAX_TX_LIFETIME (24h) just like kernels do + let expirationTimestamp = + privateExecutionResult.entrypoint.publicInputs.anchorBlockHeader.globalVariables.timestamp + + BigInt(MAX_TX_LIFETIME); + const executions = [privateExecutionResult.entrypoint]; while (executions.length !== 0) { const execution = executions.shift()!; executions.unshift(...execution!.nestedExecutionResults); + // Just like kernels we overwrite the default value if the call sets it. + const callExpirationTimestamp = execution.publicInputs.expirationTimestamp; + if (callExpirationTimestamp !== 0n && callExpirationTimestamp < expirationTimestamp) { + expirationTimestamp = callExpirationTimestamp; + } + const { contractAddress } = execution.publicInputs.callContext; scopedNoteHashes.push( @@ -671,7 +683,7 @@ export async function generateSimulatedProvingResult( }), ), /*feePayer=*/ AztecAddress.zero(), - /*expirationTimestamp=*/ 0n, + /*expirationTimestamp=*/ expirationTimestamp, hasPublicCalls ? inputsForPublic : undefined, !hasPublicCalls ? inputsForRollup : undefined, ); diff --git a/yarn-project/pxe/src/contract_function_simulator/oracle/legacy_oracle_mappings.ts b/yarn-project/pxe/src/contract_function_simulator/oracle/legacy_oracle_mappings.ts index ee95befc339a..d8c7297824db 100644 --- a/yarn-project/pxe/src/contract_function_simulator/oracle/legacy_oracle_mappings.ts +++ b/yarn-project/pxe/src/contract_function_simulator/oracle/legacy_oracle_mappings.ts @@ -11,39 +11,108 @@ import type { Oracle } from './oracle.js'; export function buildLegacyOracleCallbacks(oracle: Oracle): ACIRCallback { return { // Simple prefix renames (privateXxx/utilityXxx → aztec_prv_/aztec_utl_) - utilityLog: (...args: ACVMField[][]) => oracle.aztec_utl_log(args[0], args[1], args[2], args[3]), - utilityAssertCompatibleOracleVersion: (...args: ACVMField[][]) => - oracle.aztec_utl_assertCompatibleOracleVersion(args[0]), - utilityLoadCapsule: (...args: ACVMField[][]) => oracle.aztec_utl_loadCapsule(args[0], args[1], args[2]), - privateStoreInExecutionCache: (...args: ACVMField[][]) => oracle.aztec_prv_storeInExecutionCache(args[0], args[1]), - privateLoadFromExecutionCache: (...args: ACVMField[][]) => oracle.aztec_prv_loadFromExecutionCache(args[0]), - privateCallPrivateFunction: (...args: ACVMField[][]) => - oracle.aztec_prv_callPrivateFunction(args[0], args[1], args[2], args[3], args[4]), - privateIsNullifierPending: (...args: ACVMField[][]) => oracle.aztec_prv_isNullifierPending(args[0], args[1]), - privateNotifyCreatedNullifier: (...args: ACVMField[][]) => oracle.aztec_prv_notifyCreatedNullifier(args[0]), - privateNotifyCreatedContractClassLog: (...args: ACVMField[][]) => - oracle.aztec_prv_notifyCreatedContractClassLog(args[0], args[1], args[2], args[3]), - privateGetNextAppTagAsSender: (...args: ACVMField[][]) => oracle.aztec_prv_getNextAppTagAsSender(args[0], args[1]), - privateGetSenderForTags: () => oracle.aztec_prv_getSenderForTags(), - privateSetSenderForTags: (...args: ACVMField[][]) => oracle.aztec_prv_setSenderForTags(args[0]), - utilityGetUtilityContext: () => oracle.aztec_utl_getUtilityContext(), - utilityStorageRead: (...args: ACVMField[][]) => oracle.aztec_utl_storageRead(args[0], args[1], args[2], args[3]), - utilityStoreCapsule: (...args: ACVMField[][]) => oracle.aztec_utl_storeCapsule(args[0], args[1], args[2]), - utilityCopyCapsule: (...args: ACVMField[][]) => oracle.aztec_utl_copyCapsule(args[0], args[1], args[2], args[3]), - utilityDeleteCapsule: (...args: ACVMField[][]) => oracle.aztec_utl_deleteCapsule(args[0], args[1]), - utilityAes128Decrypt: (...args: ACVMField[][]) => - oracle.aztec_utl_aes128Decrypt(args[0], args[1], args[2], args[3]), - utilityGetSharedSecret: (...args: ACVMField[][]) => - oracle.aztec_utl_getSharedSecret(args[0], args[1], args[2], args[3]), - utilityFetchTaggedLogs: (...args: ACVMField[][]) => oracle.aztec_utl_fetchTaggedLogs(args[0]), - utilityBulkRetrieveLogs: (...args: ACVMField[][]) => oracle.aztec_utl_bulkRetrieveLogs(args[0], args[1], args[2]), + utilityLog: ( + level: ACVMField[], + message: ACVMField[], + _ignoredFieldsSize: ACVMField[], + fields: ACVMField[], + ): Promise => oracle.aztec_utl_log(level, message, _ignoredFieldsSize, fields), + utilityAssertCompatibleOracleVersion: (version: ACVMField[]): Promise => + oracle.aztec_utl_assertCompatibleOracleVersion(version), + utilityLoadCapsule: ( + contractAddress: ACVMField[], + slot: ACVMField[], + tSize: ACVMField[], + ): Promise<(ACVMField | ACVMField[])[]> => oracle.aztec_utl_loadCapsule(contractAddress, slot, tSize), + privateStoreInExecutionCache: (values: ACVMField[], hash: ACVMField[]): Promise => + oracle.aztec_prv_storeInExecutionCache(values, hash), + privateLoadFromExecutionCache: (returnsHash: ACVMField[]): Promise => + oracle.aztec_prv_loadFromExecutionCache(returnsHash), + privateCallPrivateFunction: ( + contractAddress: ACVMField[], + functionSelector: ACVMField[], + argsHash: ACVMField[], + sideEffectCounter: ACVMField[], + isStaticCall: ACVMField[], + ): Promise => + oracle.aztec_prv_callPrivateFunction( + contractAddress, + functionSelector, + argsHash, + sideEffectCounter, + isStaticCall, + ), + privateIsNullifierPending: (innerNullifier: ACVMField[], contractAddress: ACVMField[]): Promise => + oracle.aztec_prv_isNullifierPending(innerNullifier, contractAddress), + privateNotifyCreatedNullifier: (innerNullifier: ACVMField[]): Promise => + oracle.aztec_prv_notifyCreatedNullifier(innerNullifier), + privateNotifyCreatedContractClassLog: ( + contractAddress: ACVMField[], + message: ACVMField[], + length: ACVMField[], + counter: ACVMField[], + ): Promise => + oracle.aztec_prv_notifyCreatedContractClassLog(contractAddress, message, length, counter), + utilityGetUtilityContext: (): Promise<(ACVMField | ACVMField[])[]> => oracle.aztec_utl_getUtilityContext(), + utilityStorageRead: ( + blockHash: ACVMField[], + contractAddress: ACVMField[], + startStorageSlot: ACVMField[], + numberOfElements: ACVMField[], + ): Promise => + oracle.aztec_utl_storageRead(blockHash, contractAddress, startStorageSlot, numberOfElements), + utilityStoreCapsule: ( + contractAddress: ACVMField[], + slot: ACVMField[], + capsule: ACVMField[], + ): Promise => oracle.aztec_utl_storeCapsule(contractAddress, slot, capsule), + utilityCopyCapsule: ( + contractAddress: ACVMField[], + srcSlot: ACVMField[], + dstSlot: ACVMField[], + numEntries: ACVMField[], + ): Promise => oracle.aztec_utl_copyCapsule(contractAddress, srcSlot, dstSlot, numEntries), + utilityDeleteCapsule: (contractAddress: ACVMField[], slot: ACVMField[]): Promise => + oracle.aztec_utl_deleteCapsule(contractAddress, slot), + utilityAes128Decrypt: ( + ciphertextBVecStorage: ACVMField[], + ciphertextLength: ACVMField[], + iv: ACVMField[], + symKey: ACVMField[], + ): Promise<(ACVMField | ACVMField[])[]> => + oracle.aztec_utl_aes128Decrypt(ciphertextBVecStorage, ciphertextLength, iv, symKey), + utilityGetSharedSecret: ( + address: ACVMField[], + ephPKField0: ACVMField[], + ephPKField1: ACVMField[], + ephPKField2: ACVMField[], + ): Promise => oracle.aztec_utl_getSharedSecret(address, ephPKField0, ephPKField1, ephPKField2), + utilityFetchTaggedLogs: (pendingTaggedLogArrayBaseSlot: ACVMField[]): Promise => + oracle.aztec_utl_fetchTaggedLogs(pendingTaggedLogArrayBaseSlot), + utilityBulkRetrieveLogs: ( + contractAddress: ACVMField[], + logRetrievalRequestsArrayBaseSlot: ACVMField[], + logRetrievalResponsesArrayBaseSlot: ACVMField[], + ): Promise => + oracle.aztec_utl_bulkRetrieveLogs( + contractAddress, + logRetrievalRequestsArrayBaseSlot, + logRetrievalResponsesArrayBaseSlot, + ), + utilityGetL1ToL2MembershipWitness: ( + contractAddress: ACVMField[], + messageHash: ACVMField[], + secret: ACVMField[], + ): Promise<(ACVMField | ACVMField[])[]> => + oracle.aztec_utl_getL1ToL2MembershipWitness(contractAddress, messageHash, secret), + utilityEmitOffchainEffect: (data: ACVMField[]): Promise => oracle.aztec_utl_emitOffchainEffect(data), // Adapter: old 3-param signature → new 5-param with injected constants. // Values derived from: MAX_MESSAGE_CONTENT_LEN(11) - RESERVED_FIELDS (3 for notes, 1 for events). utilityValidateAndStoreEnqueuedNotesAndEvents: ( contractAddress: ACVMField[], noteValidationRequestsArrayBaseSlot: ACVMField[], eventValidationRequestsArrayBaseSlot: ACVMField[], - ) => + ): Promise => oracle.aztec_utl_validateAndStoreEnqueuedNotesAndEvents( contractAddress, noteValidationRequestsArrayBaseSlot, @@ -51,27 +120,23 @@ export function buildLegacyOracleCallbacks(oracle: Oracle): ACIRCallback { [new Fr(8).toString()], [new Fr(10).toString()], ), - utilityGetL1ToL2MembershipWitness: (...args: ACVMField[][]) => - oracle.aztec_utl_getL1ToL2MembershipWitness(args[0], args[1], args[2]), - utilityCheckNullifierExists: (...args: ACVMField[][]) => oracle.aztec_utl_checkNullifierExists(args[0]), - utilityGetRandomField: () => oracle.aztec_utl_getRandomField(), - utilityEmitOffchainEffect: (...args: ACVMField[][]) => oracle.aztec_utl_emitOffchainEffect(args[0]), // Renames (same signature, different oracle name) - privateNotifySetMinRevertibleSideEffectCounter: (...args: ACVMField[][]) => - oracle.aztec_prv_notifyRevertiblePhaseStart(args[0]), - privateIsSideEffectCounterRevertible: (...args: ACVMField[][]) => oracle.aztec_prv_inRevertiblePhase(args[0]), + privateNotifySetMinRevertibleSideEffectCounter: (counter: ACVMField[]): Promise => + oracle.aztec_prv_notifyRevertiblePhaseStart(counter), + privateIsSideEffectCounterRevertible: (sideEffectCounter: ACVMField[]): Promise => + oracle.aztec_prv_inRevertiblePhase(sideEffectCounter), // Signature changes: old 4-param oracles → new 1-param validatePublicCalldata privateNotifyEnqueuedPublicFunctionCall: ( - [_contractAddress]: ACVMField[], - [calldataHash]: ACVMField[], - [_sideEffectCounter]: ACVMField[], - [_isStaticCall]: ACVMField[], - ) => oracle.aztec_prv_validatePublicCalldata([calldataHash]), + _contractAddress: ACVMField[], + calldataHash: ACVMField[], + _sideEffectCounter: ACVMField[], + _isStaticCall: ACVMField[], + ): Promise => oracle.aztec_prv_validatePublicCalldata(calldataHash), privateNotifySetPublicTeardownFunctionCall: ( - [_contractAddress]: ACVMField[], - [calldataHash]: ACVMField[], - [_sideEffectCounter]: ACVMField[], - [_isStaticCall]: ACVMField[], - ) => oracle.aztec_prv_validatePublicCalldata([calldataHash]), + _contractAddress: ACVMField[], + calldataHash: ACVMField[], + _sideEffectCounter: ACVMField[], + _isStaticCall: ACVMField[], + ): Promise => oracle.aztec_prv_validatePublicCalldata(calldataHash), }; } diff --git a/yarn-project/pxe/src/pxe.ts b/yarn-project/pxe/src/pxe.ts index 449e051e7f03..2688ead9d442 100644 --- a/yarn-project/pxe/src/pxe.ts +++ b/yarn-project/pxe/src/pxe.ts @@ -107,7 +107,9 @@ export type SimulateTxOpts = { skipTxValidation?: boolean; /** If false, fees are enforced. */ skipFeeEnforcement?: boolean; - /** State overrides for the simulation, such as contract instances and artifacts. */ + /** If true, kernel logic is emulated in TS for simulation */ + skipKernels?: boolean; + /** State overrides for the simulation, such as contract instances and artifacts. Requires skipKernels: true */ overrides?: SimulationOverrides; /** Addresses whose private state and keys are accessible during private execution */ scopes: AccessScopes; @@ -896,7 +898,14 @@ export class PXE { */ public simulateTx( txRequest: TxExecutionRequest, - { simulatePublic, skipTxValidation = false, skipFeeEnforcement = false, overrides, scopes }: SimulateTxOpts, + { + simulatePublic, + skipTxValidation = false, + skipFeeEnforcement = false, + skipKernels = true, + overrides, + scopes, + }: SimulateTxOpts, ): Promise { // We disable concurrent simulations since those might execute oracles which read and write to the PXE stores (e.g. // to the capsules), and we need to prevent concurrent runs from interfering with one another (e.g. attempting to @@ -920,13 +929,15 @@ export class PXE { await this.blockStateSynchronizer.sync(); const syncTime = syncTimer.ms(); - const contractFunctionSimulator = this.#getSimulatorForTx(overrides); - // Temporary: in case there are overrides, we have to skip the kernels or validations - // will fail. Consider handing control to the user/wallet on whether they want to run them - // or not. const overriddenContracts = overrides?.contracts ? new Set(Object.keys(overrides.contracts)) : undefined; const hasOverriddenContracts = overriddenContracts !== undefined && overriddenContracts.size > 0; - const skipKernels = hasOverriddenContracts; + + if (hasOverriddenContracts && !skipKernels) { + throw new Error( + 'Simulating with overridden contracts is not compatible with kernel execution. Please set skipKernels to true when simulating with overridden contracts.', + ); + } + const contractFunctionSimulator = this.#getSimulatorForTx(overrides); // Set overridden contracts on the sync service so it knows to skip syncing them if (hasOverriddenContracts) { diff --git a/yarn-project/stdlib/src/interfaces/aztec-node.ts b/yarn-project/stdlib/src/interfaces/aztec-node.ts index 94ec70a55654..183354098fd5 100644 --- a/yarn-project/stdlib/src/interfaces/aztec-node.ts +++ b/yarn-project/stdlib/src/interfaces/aztec-node.ts @@ -122,6 +122,7 @@ export interface AztecNode * @param referenceBlock - The block parameter (block number, block hash, or 'latest') at which to get the data. * @param nullifier - Nullifier we try to find the low nullifier witness for. * @returns The low nullifier membership witness (if found). + * @throws If the nullifier already exists in the tree, since non-inclusion cannot be proven. * @remarks Low nullifier witness can be used to perform a nullifier non-inclusion proof by leveraging the "linked * list structure" of leaves and proving that a lower nullifier is pointing to a bigger next value than the nullifier * we are trying to prove non-inclusion for. diff --git a/yarn-project/stdlib/src/logs/log_filter.ts b/yarn-project/stdlib/src/logs/log_filter.ts index d2c191a803a9..75f97cd1ee20 100644 --- a/yarn-project/stdlib/src/logs/log_filter.ts +++ b/yarn-project/stdlib/src/logs/log_filter.ts @@ -1,3 +1,5 @@ +import type { Fr } from '@aztec/foundation/curves/bn254'; + import { z } from 'zod'; import type { AztecAddress } from '../aztec-address/index.js'; @@ -20,6 +22,8 @@ export type LogFilter = { afterLog?: LogId; /** The contract address to filter logs by. */ contractAddress?: AztecAddress; + /** The tag (first field of the log) to filter logs by. */ + tag?: Fr; }; export const LogFilterSchema: ZodFor = z.object({ @@ -28,4 +32,5 @@ export const LogFilterSchema: ZodFor = z.object({ toBlock: schemas.Integer.optional(), afterLog: LogId.schema.optional(), contractAddress: schemas.AztecAddress.optional(), + tag: schemas.Fr.optional(), }); diff --git a/yarn-project/wallets/src/embedded/embedded_wallet.ts b/yarn-project/wallets/src/embedded/embedded_wallet.ts index e1f69cf6ad09..df2a73b9ecf1 100644 --- a/yarn-project/wallets/src/embedded/embedded_wallet.ts +++ b/yarn-project/wallets/src/embedded/embedded_wallet.ts @@ -10,7 +10,12 @@ import { AztecAddress } from '@aztec/stdlib/aztec-address'; import { getContractInstanceFromInstantiationParams } from '@aztec/stdlib/contract'; import type { AztecNode } from '@aztec/stdlib/interfaces/client'; import { deriveSigningKey } from '@aztec/stdlib/keys'; -import { ExecutionPayload, type TxSimulationResult, mergeExecutionPayloads } from '@aztec/stdlib/tx'; +import { + ExecutionPayload, + SimulationOverrides, + type TxSimulationResult, + mergeExecutionPayloads, +} from '@aztec/stdlib/tx'; import { BaseWallet, type FeeOptions } from '@aztec/wallet-sdk/base-wallet'; import type { AccountContractsProvider } from './account-contract-providers/types.js'; @@ -84,10 +89,20 @@ export class EmbeddedWallet extends BaseWallet { from: AztecAddress, feeOptions: FeeOptions, scopes: AccessScopes, - _skipTxValidation?: boolean, - _skipFeeEnforcement?: boolean, + skipTxValidation?: boolean, + skipFeeEnforcement?: boolean, ): Promise { - const { account: fromAccount, instance, artifact } = await this.getFakeAccountDataFor(from); + let overrides: SimulationOverrides | undefined; + let fromAccount: Account; + if (!from.equals(AztecAddress.ZERO)) { + const { account, instance, artifact } = await this.getFakeAccountDataFor(from); + fromAccount = account; + overrides = { + contracts: { [from.toString()]: { instance, artifact } }, + }; + } else { + fromAccount = await this.getAccountFromAddress(from); + } const feeExecutionPayload = await feeOptions.walletFeePaymentMethod?.getExecutionPayload(); const executionOptions: DefaultAccountEntrypointOptions = { @@ -107,49 +122,33 @@ export class EmbeddedWallet extends BaseWallet { ); return this.pxe.simulateTx(txRequest, { simulatePublic: true, - skipFeeEnforcement: true, - skipTxValidation: true, - overrides: { - contracts: { [from.toString()]: { instance, artifact } }, - }, + skipFeeEnforcement, + skipTxValidation, + overrides, scopes, }); } private async getFakeAccountDataFor(address: AztecAddress) { - // While we have the convention of "Zero address means no auth", and also - // we don't have a way to trigger kernelless simulations without overrides, - // we need to explicitly handle the zero address case here by - // returning the actual multicall contract instead of trying to create a stub account for it. - if (!address.equals(AztecAddress.ZERO)) { - const originalAccount = await this.getAccountFromAddress(address); - if (originalAccount instanceof SignerlessAccount) { - throw new Error(`Cannot create fake account data for SignerlessAccount at address: ${address}`); - } - const originalAddress = (originalAccount as Account).getCompleteAddress(); - const contractInstance = await this.pxe.getContractInstance(originalAddress.address); - if (!contractInstance) { - throw new Error(`No contract instance found for address: ${originalAddress.address}`); - } - const stubAccount = await this.accountContracts.createStubAccount(originalAddress); - const stubArtifact = await this.accountContracts.getStubAccountContractArtifact(); - const instance = await getContractInstanceFromInstantiationParams(stubArtifact, { - salt: Fr.random(), - }); - return { - account: stubAccount, - instance, - artifact: stubArtifact, - }; - } else { - const { instance, artifact } = await this.accountContracts.getMulticallContract(); - const account = new SignerlessAccount(); - return { - instance, - account, - artifact, - }; + const originalAccount = await this.getAccountFromAddress(address); + if (originalAccount instanceof SignerlessAccount) { + throw new Error(`Cannot create fake account data for SignerlessAccount at address: ${address}`); } + const originalAddress = (originalAccount as Account).getCompleteAddress(); + const contractInstance = await this.pxe.getContractInstance(originalAddress.address); + if (!contractInstance) { + throw new Error(`No contract instance found for address: ${originalAddress.address}`); + } + const stubAccount = await this.accountContracts.createStubAccount(originalAddress); + const stubArtifact = await this.accountContracts.getStubAccountContractArtifact(); + const instance = await getContractInstanceFromInstantiationParams(stubArtifact, { + salt: Fr.random(), + }); + return { + account: stubAccount, + instance, + artifact: stubArtifact, + }; } protected async createAccountInternal(