From 302e3bbb2d7a7d54f362026edb314f3d3596b6d6 Mon Sep 17 00:00:00 2001 From: Facundo Date: Wed, 17 Apr 2024 18:25:07 +0100 Subject: [PATCH] feat: make public l1tol2 message consumption take leafIndex (#5805) --- .../aztec-nr/aztec/src/context/avm_context.nr | 20 ++++++++++-- .../aztec-nr/aztec/src/context/interface.nr | 2 +- .../aztec/src/context/public_context.nr | 3 +- .../contracts/gas_token_contract/src/main.nr | 4 +-- .../contracts/test_contract/src/main.nr | 19 ++++++++--- .../token_bridge_contract/src/main.nr | 9 ++++-- .../src/e2e_cross_chain_messaging.test.ts | 7 +++- yarn-project/end-to-end/src/e2e_fees.test.ts | 1 + .../e2e_public_cross_chain_messaging.test.ts | 32 +++++++++++++++---- .../e2e_public_to_private_messaging.test.ts | 25 +++++++++++---- .../src/flakey_e2e_account_init_fees.test.ts | 1 + .../src/shared/cross_chain_test_harness.ts | 4 +-- .../src/shared/gas_portal_test_harness.ts | 21 +++++++++--- .../end-to-end/src/shared/uniswap_l1_l2.ts | 31 ++++++++++++++++-- .../simulator/src/public/index.test.ts | 6 ++-- 15 files changed, 148 insertions(+), 37 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/context/avm_context.nr b/noir-projects/aztec-nr/aztec/src/context/avm_context.nr index 4e80ea93f46..c4a6350af39 100644 --- a/noir-projects/aztec-nr/aztec/src/context/avm_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/avm_context.nr @@ -1,3 +1,4 @@ +use crate::hash::{compute_secret_hash, compute_message_hash, compute_message_nullifier}; use dep::protocol_types::{ address::{AztecAddress, EthAddress}, constants::{L1_TO_L2_MESSAGE_LENGTH, NESTED_CALL_L2_GAS_BUFFER}, header::Header @@ -128,8 +129,23 @@ impl PublicContextInterface for AvmContext { assert(false, "'push_unencrypted_log' not required for avm - use emit_unencrypted_log!"); } - fn consume_l1_to_l2_message(&mut self, content: Field, secret: Field, sender: EthAddress) { - assert(false, "'consume_l1_to_l2_message' not implemented!"); + fn consume_l1_to_l2_message(&mut self, content: Field, secret: Field, sender: EthAddress, leaf_index: Field) { + let secret_hash = compute_secret_hash(secret); + let message_hash = compute_message_hash( + sender, + self.chain_id(), + /*recipient=*/self.this_address(), + self.version(), + content, + secret_hash + ); + let nullifier = compute_message_nullifier(message_hash, secret, leaf_index); + + assert(!self.nullifier_exists(nullifier, self.this_address()), "L1-to-L2 message is already nullified"); + assert(self.l1_to_l2_msg_exists(message_hash, leaf_index), "Tried to consume nonexistent L1-to-L2 message"); + + // Push nullifier (and the "commitment" corresponding to this can be "empty") + self.push_new_nullifier(nullifier, 0); } fn message_portal(&mut self, recipient: EthAddress, content: Field) { diff --git a/noir-projects/aztec-nr/aztec/src/context/interface.nr b/noir-projects/aztec-nr/aztec/src/context/interface.nr index 724ec73c09d..a076158d71d 100644 --- a/noir-projects/aztec-nr/aztec/src/context/interface.nr +++ b/noir-projects/aztec-nr/aztec/src/context/interface.nr @@ -33,7 +33,7 @@ trait PublicContextInterface { fn fee_per_l1_gas(self) -> Field; fn fee_per_l2_gas(self) -> Field; fn message_portal(&mut self, recipient: EthAddress, content: Field); - fn consume_l1_to_l2_message(&mut self, content: Field, secret: Field, sender: EthAddress); + fn consume_l1_to_l2_message(&mut self, content: Field, secret: Field, sender: EthAddress, leaf_index: Field); fn emit_unencrypted_log(&mut self, log: T); // TODO(1165) Merge push_unencrypted_log into emit_unencrypted_log, since oracle call // in PublicContext will no longer be needed for extracting log hash diff --git a/noir-projects/aztec-nr/aztec/src/context/public_context.nr b/noir-projects/aztec-nr/aztec/src/context/public_context.nr index ad3aa93d879..ba70631a79f 100644 --- a/noir-projects/aztec-nr/aztec/src/context/public_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/public_context.nr @@ -263,7 +263,8 @@ impl PublicContextInterface for PublicContext { // We can consume message with a secret in public context because the message cannot be modified and therefore // there is no front-running risk (e.g. somebody could front run you to claim your tokens to your address). - fn consume_l1_to_l2_message(&mut self, content: Field, secret: Field, sender: EthAddress) { + // Leaf index is not used in public context, but it is used in the AVMContext which will replace it. + fn consume_l1_to_l2_message(&mut self, content: Field, secret: Field, sender: EthAddress, _leaf_index: Field) { let this = (*self).this_address(); let nullifier = process_l1_to_l2_message( self.historical_header.state.l1_to_l2_message_tree.root, diff --git a/noir-projects/noir-contracts/contracts/gas_token_contract/src/main.nr b/noir-projects/noir-contracts/contracts/gas_token_contract/src/main.nr index ba8ecf6c608..ec1adf32116 100644 --- a/noir-projects/noir-contracts/contracts/gas_token_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/gas_token_contract/src/main.nr @@ -12,11 +12,11 @@ contract GasToken { } #[aztec(public)] - fn claim_public(to: AztecAddress, amount: Field, secret: Field) { + fn claim_public(to: AztecAddress, amount: Field, secret: Field, leaf_index: Field) { let content_hash = get_bridge_gas_msg_hash(to, amount); // Consume message and emit nullifier - context.consume_l1_to_l2_message(content_hash, secret, context.this_portal_address()); + context.consume_l1_to_l2_message(content_hash, secret, context.this_portal_address(), leaf_index); let new_balance = storage.balances.at(to).read() + U128::from_integer(amount); storage.balances.at(to).write(new_balance); diff --git a/noir-projects/noir-contracts/contracts/test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test_contract/src/main.nr index 535e96dc43a..c8ea069d730 100644 --- a/noir-projects/noir-contracts/contracts/test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test_contract/src/main.nr @@ -268,10 +268,20 @@ contract Test { } #[aztec(public)] - fn consume_mint_public_message(to: AztecAddress, amount: Field, secret: Field) { + fn consume_mint_public_message( + to: AztecAddress, + amount: Field, + secret: Field, + message_leaf_index: Field + ) { let content_hash = get_mint_public_content_hash(to, amount); // Consume message and emit nullifier - context.consume_l1_to_l2_message(content_hash, secret, context.this_portal_address()); + context.consume_l1_to_l2_message( + content_hash, + secret, + context.this_portal_address(), + message_leaf_index + ); } #[aztec(private)] @@ -293,10 +303,11 @@ contract Test { fn consume_message_from_arbitrary_sender_public( content: Field, secret: Field, - sender: EthAddress + sender: EthAddress, + message_leaf_index: Field ) { // Consume message and emit nullifier - context.consume_l1_to_l2_message(content, secret, sender); + context.consume_l1_to_l2_message(content, secret, sender, message_leaf_index); } #[aztec(private)] diff --git a/noir-projects/noir-contracts/contracts/token_bridge_contract/src/main.nr b/noir-projects/noir-contracts/contracts/token_bridge_contract/src/main.nr index c5814ed81ce..32f9653915b 100644 --- a/noir-projects/noir-contracts/contracts/token_bridge_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/token_bridge_contract/src/main.nr @@ -34,11 +34,16 @@ contract TokenBridge { // docs:start:claim_public // Consumes a L1->L2 message and calls the token contract to mint the appropriate amount publicly #[aztec(public)] - fn claim_public(to: AztecAddress, amount: Field, secret: Field) { + fn claim_public(to: AztecAddress, amount: Field, secret: Field, message_leaf_index: Field) { let content_hash = get_mint_public_content_hash(to, amount); // Consume message and emit nullifier - context.consume_l1_to_l2_message(content_hash, secret, context.this_portal_address()); + context.consume_l1_to_l2_message( + content_hash, + secret, + context.this_portal_address(), + message_leaf_index + ); // Mint tokens Token::at(storage.token.read()).mint_public(to, amount).call(&mut context); diff --git a/yarn-project/end-to-end/src/e2e_cross_chain_messaging.test.ts b/yarn-project/end-to-end/src/e2e_cross_chain_messaging.test.ts index e7d3b42101f..862c4a1ac09 100644 --- a/yarn-project/end-to-end/src/e2e_cross_chain_messaging.test.ts +++ b/yarn-project/end-to-end/src/e2e_cross_chain_messaging.test.ts @@ -245,11 +245,16 @@ describe('e2e_cross_chain_messaging', () => { secretHashForL2MessageConsumption, ); + // get message leaf index, needed for claiming in public + const maybeIndexAndPath = await aztecNode.getL1ToL2MessageMembershipWitness('latest', msgHash, 0n); + expect(maybeIndexAndPath).toBeDefined(); + const messageLeafIndex = maybeIndexAndPath![0]; + // 3. Consume L1 -> L2 message and try to mint publicly on L2 - should fail await expect( l2Bridge .withWallet(user2Wallet) - .methods.claim_public(ownerAddress, bridgeAmount, secretForL2MessageConsumption) + .methods.claim_public(ownerAddress, bridgeAmount, secretForL2MessageConsumption, messageLeafIndex) .prove(), ).rejects.toThrow(`No non-nullified L1 to L2 message found for message hash ${wrongMessage.hash().toString()}`); }, 120_000); diff --git a/yarn-project/end-to-end/src/e2e_fees.test.ts b/yarn-project/end-to-end/src/e2e_fees.test.ts index a13b7830b90..d6301790f78 100644 --- a/yarn-project/end-to-end/src/e2e_fees.test.ts +++ b/yarn-project/end-to-end/src/e2e_fees.test.ts @@ -81,6 +81,7 @@ describe('e2e_fees', () => { sequencerAddress = wallets[2].getAddress(); gasBridgeTestHarness = await GasPortalTestingHarnessFactory.create({ + aztecNode: aztecNode, pxeService: pxe, publicClient: deployL1ContractsValues.publicClient, walletClient: deployL1ContractsValues.walletClient, diff --git a/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging.test.ts b/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging.test.ts index b26721bd672..e8aa0bacee3 100644 --- a/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging.test.ts +++ b/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging.test.ts @@ -5,6 +5,8 @@ import { type DebugLogger, type DeployL1Contracts, EthAddress, + type EthAddressLike, + type FieldLike, Fr, L1Actor, L1ToL2Message, @@ -92,8 +94,13 @@ describe('e2e_public_cross_chain_messaging', () => { // Wait for the message to be available for consumption await crossChainTestHarness.makeMessageConsumable(msgHash); + // Get message leaf index, needed for claiming in public + const maybeIndexAndPath = await aztecNode.getL1ToL2MessageMembershipWitness('latest', msgHash, 0n); + expect(maybeIndexAndPath).toBeDefined(); + const messageLeafIndex = maybeIndexAndPath![0]; + // 3. Consume L1 -> L2 message and mint public tokens on L2 - await crossChainTestHarness.consumeMessageOnAztecAndMintPublicly(bridgeAmount, secret); + await crossChainTestHarness.consumeMessageOnAztecAndMintPublicly(bridgeAmount, secret, messageLeafIndex); await crossChainTestHarness.expectPublicBalanceOnL2(ownerAddress, bridgeAmount); const afterBalance = bridgeAmount; @@ -161,14 +168,26 @@ describe('e2e_public_cross_chain_messaging', () => { secretHash, ); + // get message leaf index, needed for claiming in public + const maybeIndexAndPath = await aztecNode.getL1ToL2MessageMembershipWitness('latest', msgHash, 0n); + expect(maybeIndexAndPath).toBeDefined(); + const messageLeafIndex = maybeIndexAndPath![0]; + // user2 tries to consume this message and minting to itself -> should fail since the message is intended to be consumed only by owner. await expect( - l2Bridge.withWallet(user2Wallet).methods.claim_public(user2Wallet.getAddress(), bridgeAmount, secret).prove(), + l2Bridge + .withWallet(user2Wallet) + .methods.claim_public(user2Wallet.getAddress(), bridgeAmount, secret, messageLeafIndex) + .prove(), ).rejects.toThrow(`No non-nullified L1 to L2 message found for message hash ${wrongMessage.hash().toString()}`); // user2 consumes owner's L1-> L2 message on bridge contract and mints public tokens on L2 logger.info("user2 consumes owner's message on L2 Publicly"); - await l2Bridge.withWallet(user2Wallet).methods.claim_public(ownerAddress, bridgeAmount, secret).send().wait(); + await l2Bridge + .withWallet(user2Wallet) + .methods.claim_public(ownerAddress, bridgeAmount, secret, messageLeafIndex) + .send() + .wait(); // ensure funds are gone to owner and not user2. await crossChainTestHarness.expectPublicBalanceOnL2(ownerAddress, bridgeAmount); await crossChainTestHarness.expectPublicBalanceOnL2(user2Wallet.getAddress(), 0n); @@ -312,7 +331,8 @@ describe('e2e_public_cross_chain_messaging', () => { const testContract = await TestContract.deploy(user1Wallet).send().deployed(); const consumeMethod = isPrivate - ? testContract.methods.consume_message_from_arbitrary_sender_private + ? (content: FieldLike, secret: FieldLike, sender: EthAddressLike, _leafIndex: FieldLike) => + testContract.methods.consume_message_from_arbitrary_sender_private(content, secret, sender) : testContract.methods.consume_message_from_arbitrary_sender_public; const secret = Fr.random(); @@ -329,7 +349,7 @@ describe('e2e_public_cross_chain_messaging', () => { const [message1Index, _1] = (await aztecNode.getL1ToL2MessageMembershipWitness('latest', message.hash(), 0n))!; // Finally, we consume the L1 -> L2 message using the test contract either from private or public - await consumeMethod(message.content, secret, message.sender.sender).send().wait(); + await consumeMethod(message.content, secret, message.sender.sender, message1Index).send().wait(); // We send and consume the exact same message the second time to test that oracles correctly return the new // non-nullified message @@ -348,7 +368,7 @@ describe('e2e_public_cross_chain_messaging', () => { // Now we consume the message again. Everything should pass because oracle should return the duplicate message // which is not nullified - await consumeMethod(message.content, secret, message.sender.sender).send().wait(); + await consumeMethod(message.content, secret, message.sender.sender, message2Index).send().wait(); }, 120_000, ); diff --git a/yarn-project/end-to-end/src/e2e_public_to_private_messaging.test.ts b/yarn-project/end-to-end/src/e2e_public_to_private_messaging.test.ts index 805628e74a8..51077dc9de8 100644 --- a/yarn-project/end-to-end/src/e2e_public_to_private_messaging.test.ts +++ b/yarn-project/end-to-end/src/e2e_public_to_private_messaging.test.ts @@ -1,4 +1,4 @@ -import { type AztecAddress, type DebugLogger, type EthAddress } from '@aztec/aztec.js'; +import { type AztecAddress, type AztecNode, type DebugLogger, type EthAddress } from '@aztec/aztec.js'; import { setup } from './fixtures/utils.js'; import { CrossChainTestHarness } from './shared/cross_chain_test_harness.js'; @@ -7,18 +7,23 @@ describe('e2e_public_to_private_messaging', () => { let logger: DebugLogger; let teardown: () => Promise; + let aztecNode: AztecNode; let ethAccount: EthAddress; - let underlyingERC20: any; - let ownerAddress: AztecAddress; - let crossChainTestHarness: CrossChainTestHarness; beforeEach(async () => { - const { aztecNode, pxe, deployL1ContractsValues, wallet, logger: logger_, teardown: teardown_ } = await setup(2); + const { + aztecNode: aztecNode_, + pxe, + deployL1ContractsValues, + wallet, + logger: logger_, + teardown: teardown_, + } = await setup(2); crossChainTestHarness = await CrossChainTestHarness.new( - aztecNode, + aztecNode_, pxe, deployL1ContractsValues.publicClient, deployL1ContractsValues.walletClient, @@ -26,6 +31,7 @@ describe('e2e_public_to_private_messaging', () => { logger_, ); + aztecNode = crossChainTestHarness.aztecNode; ethAccount = crossChainTestHarness.ethAccount; ownerAddress = crossChainTestHarness.ownerAddress; underlyingERC20 = crossChainTestHarness.underlyingERC20; @@ -53,7 +59,12 @@ describe('e2e_public_to_private_messaging', () => { await crossChainTestHarness.makeMessageConsumable(msgHash); - await crossChainTestHarness.consumeMessageOnAztecAndMintPublicly(bridgeAmount, secret); + // get message leaf index, needed for claiming in public + const maybeIndexAndPath = await aztecNode.getL1ToL2MessageMembershipWitness('latest', msgHash, 0n); + expect(maybeIndexAndPath).toBeDefined(); + const messageLeafIndex = maybeIndexAndPath![0]; + + await crossChainTestHarness.consumeMessageOnAztecAndMintPublicly(bridgeAmount, secret, messageLeafIndex); await crossChainTestHarness.expectPublicBalanceOnL2(ownerAddress, bridgeAmount); // Create the commitment to be spent in the private domain diff --git a/yarn-project/end-to-end/src/flakey_e2e_account_init_fees.test.ts b/yarn-project/end-to-end/src/flakey_e2e_account_init_fees.test.ts index 1cb3ab1ba33..9f0d16ed810 100644 --- a/yarn-project/end-to-end/src/flakey_e2e_account_init_fees.test.ts +++ b/yarn-project/end-to-end/src/flakey_e2e_account_init_fees.test.ts @@ -94,6 +94,7 @@ describe('e2e_fees_account_init', () => { }); gasBridgeTestHarness = await GasPortalTestingHarnessFactory.create({ + aztecNode: ctx.aztecNode, pxeService: ctx.pxe, publicClient: ctx.deployL1ContractsValues.publicClient, walletClient: ctx.deployL1ContractsValues.walletClient, diff --git a/yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts b/yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts index 342e0ce917f..63975935c91 100644 --- a/yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts +++ b/yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts @@ -319,10 +319,10 @@ export class CrossChainTestHarness { await this.addPendingShieldNoteToPXE(bridgeAmount, secretHashForRedeemingMintedNotes, consumptionReceipt.txHash); } - async consumeMessageOnAztecAndMintPublicly(bridgeAmount: bigint, secret: Fr) { + async consumeMessageOnAztecAndMintPublicly(bridgeAmount: bigint, secret: Fr, leafIndex: bigint) { this.logger.info('Consuming messages on L2 Publicly'); // Call the mint tokens function on the Aztec.nr contract - await this.l2Bridge.methods.claim_public(this.ownerAddress, bridgeAmount, secret).send().wait(); + await this.l2Bridge.methods.claim_public(this.ownerAddress, bridgeAmount, secret, leafIndex).send().wait(); } async withdrawPrivateFromAztecToL1(withdrawAmount: bigint, nonce: Fr = Fr.ZERO): Promise> { diff --git a/yarn-project/end-to-end/src/shared/gas_portal_test_harness.ts b/yarn-project/end-to-end/src/shared/gas_portal_test_harness.ts index 1897042ab05..7a0c7625302 100644 --- a/yarn-project/end-to-end/src/shared/gas_portal_test_harness.ts +++ b/yarn-project/end-to-end/src/shared/gas_portal_test_harness.ts @@ -1,5 +1,6 @@ import { type AztecAddress, + type AztecNode, type DebugLogger, EthAddress, Fr, @@ -27,6 +28,7 @@ export interface IGasBridgingTestHarness { } export interface GasPortalTestingHarnessFactoryConfig { + aztecNode: AztecNode; pxeService: PXE; publicClient: PublicClient; walletClient: WalletClient; @@ -34,6 +36,7 @@ export interface GasPortalTestingHarnessFactoryConfig { logger: DebugLogger; mockL1?: boolean; } + export class GasPortalTestingHarnessFactory { private constructor(private config: GasPortalTestingHarnessFactoryConfig) {} @@ -49,7 +52,7 @@ export class GasPortalTestingHarnessFactory { } private async createReal() { - const { pxeService, publicClient, walletClient, wallet, logger } = this.config; + const { aztecNode, pxeService, publicClient, walletClient, wallet, logger } = this.config; const ethAccount = EthAddress.fromString((await walletClient.getAddresses())[0]); const l1ContractAddresses = (await pxeService.getNodeInfo()).l1ContractAddresses; @@ -82,6 +85,7 @@ export class GasPortalTestingHarnessFactory { const gasL2 = await GasTokenContract.at(getCanonicalGasTokenAddress(gasPortalAddress), wallet); return new GasBridgingTestHarness( + aztecNode, pxeService, logger, gasL2, @@ -118,6 +122,8 @@ class MockGasBridgingTestHarness implements IGasBridgingTestHarness { */ class GasBridgingTestHarness implements IGasBridgingTestHarness { constructor( + /** Aztec node */ + public aztecNode: AztecNode, /** Private eXecution Environment (PXE). */ public pxeService: PXE, /** Logger. */ @@ -201,10 +207,10 @@ class GasBridgingTestHarness implements IGasBridgingTestHarness { return Fr.fromString(messageHash); } - async consumeMessageOnAztecAndMintPublicly(bridgeAmount: bigint, owner: AztecAddress, secret: Fr) { + async consumeMessageOnAztecAndMintPublicly(bridgeAmount: bigint, owner: AztecAddress, secret: Fr, leafIndex: bigint) { this.logger.info('Consuming messages on L2 Publicly'); // Call the mint tokens function on the Aztec.nr contract - await this.l2Token.methods.claim_public(owner, bridgeAmount, secret).send().wait(); + await this.l2Token.methods.claim_public(owner, bridgeAmount, secret, leafIndex).send().wait(); } async getL2PublicBalanceOf(owner: AztecAddress) { @@ -223,15 +229,20 @@ class GasBridgingTestHarness implements IGasBridgingTestHarness { await this.mintTokensOnL1(l1TokenBalance); // 2. Deposit tokens to the TokenPortal - await this.sendTokensToPortalPublic(bridgeAmount, owner, secretHash); + const msgHash = await this.sendTokensToPortalPublic(bridgeAmount, owner, secretHash); expect(await this.getL1BalanceOf(this.ethAccount)).toBe(l1TokenBalance - bridgeAmount); // Perform an unrelated transactions on L2 to progress the rollup by 2 blocks. await this.l2Token.methods.check_balance(0).send().wait(); await this.l2Token.methods.check_balance(0).send().wait(); + // Get message leaf index, needed for claiming in public + const maybeIndexAndPath = await this.aztecNode.getL1ToL2MessageMembershipWitness('latest', msgHash, 0n); + expect(maybeIndexAndPath).toBeDefined(); + const messageLeafIndex = maybeIndexAndPath![0]; + // 3. Consume L1-> L2 message and mint public tokens on L2 - await this.consumeMessageOnAztecAndMintPublicly(bridgeAmount, owner, secret); + await this.consumeMessageOnAztecAndMintPublicly(bridgeAmount, owner, secret, messageLeafIndex); await this.expectPublicBalanceOnL2(owner, bridgeAmount); } } diff --git a/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts b/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts index 81dc6b3cb4c..f23c35ea442 100644 --- a/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts +++ b/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts @@ -14,6 +14,7 @@ import { InboxAbi, UniswapPortalAbi, UniswapPortalBytecode } from '@aztec/l1-art import { UniswapContract } from '@aztec/noir-contracts.js/Uniswap'; import { jest } from '@jest/globals'; +import { strict as assert } from 'assert'; import { type Account, type Chain, @@ -409,9 +410,22 @@ export const uniswapL1L2TestSuite = ( // Wait for the message to be available for consumption await wethCrossChainHarness.makeMessageConsumable(wethDepositMsgHash); + // Get message leaf index, needed for claiming in public + const wethDepositMaybeIndexAndPath = await aztecNode.getL1ToL2MessageMembershipWitness( + 'latest', + wethDepositMsgHash, + 0n, + ); + assert(wethDepositMaybeIndexAndPath !== undefined, 'Message not found in tree'); + const wethDepositMessageLeafIndex = wethDepositMaybeIndexAndPath[0]; + // 2. Claim WETH on L2 logger.info('Minting weth on L2'); - await wethCrossChainHarness.consumeMessageOnAztecAndMintPublicly(wethAmountToBridge, secretForMintingWeth); + await wethCrossChainHarness.consumeMessageOnAztecAndMintPublicly( + wethAmountToBridge, + secretForMintingWeth, + wethDepositMessageLeafIndex, + ); await wethCrossChainHarness.expectPublicBalanceOnL2(ownerAddress, wethAmountToBridge); // Store balances @@ -585,9 +599,22 @@ export const uniswapL1L2TestSuite = ( // Wait for the message to be available for consumption await daiCrossChainHarness.makeMessageConsumable(outTokenDepositMsgHash); + // Get message leaf index, needed for claiming in public + const outTokenDepositMaybeIndexAndPath = await aztecNode.getL1ToL2MessageMembershipWitness( + 'latest', + outTokenDepositMsgHash, + 0n, + ); + assert(outTokenDepositMaybeIndexAndPath !== undefined, 'Message not found in tree'); + const outTokenDepositMessageLeafIndex = outTokenDepositMaybeIndexAndPath[0]; + // 6. claim dai on L2 logger.info('Consuming messages to mint dai on L2'); - await daiCrossChainHarness.consumeMessageOnAztecAndMintPublicly(daiAmountToBridge, secretForDepositingSwappedDai); + await daiCrossChainHarness.consumeMessageOnAztecAndMintPublicly( + daiAmountToBridge, + secretForDepositingSwappedDai, + outTokenDepositMessageLeafIndex, + ); await daiCrossChainHarness.expectPublicBalanceOnL2(ownerAddress, daiL2BalanceBeforeSwap + daiAmountToBridge); const wethL2BalanceAfterSwap = await wethCrossChainHarness.getL2PublicBalanceOf(ownerAddress); diff --git a/yarn-project/simulator/src/public/index.test.ts b/yarn-project/simulator/src/public/index.test.ts index 7cd22cf4df1..6f74908fadd 100644 --- a/yarn-project/simulator/src/public/index.test.ts +++ b/yarn-project/simulator/src/public/index.test.ts @@ -397,6 +397,7 @@ describe('ACIR public execution simulator', () => { const tokenRecipient = AztecAddress.random(); let bridgedAmount = 20n; let secret = new Fr(1); + let leafIndex: bigint; let crossChainMsgRecipient: AztecAddress | undefined; let crossChainMsgSender: EthAddress | undefined; @@ -410,6 +411,7 @@ describe('ACIR public execution simulator', () => { beforeEach(() => { bridgedAmount = 20n; secret = new Fr(1); + leafIndex = 0n; crossChainMsgRecipient = undefined; crossChainMsgSender = undefined; @@ -423,7 +425,7 @@ describe('ACIR public execution simulator', () => { secret, ); - const computeArgs = () => encodeArguments(mintPublicArtifact, [tokenRecipient, bridgedAmount, secret]); + const computeArgs = () => encodeArguments(mintPublicArtifact, [tokenRecipient, bridgedAmount, secret, leafIndex]); const computeCallContext = () => makeCallContext(contractAddress, { @@ -455,7 +457,7 @@ describe('ACIR public execution simulator', () => { root = pedersenHash([root, sibling]); } commitmentsDb.getL1ToL2MembershipWitness.mockImplementation(() => { - return Promise.resolve(new MessageLoadOracleInputs(0n, siblingPath)); + return Promise.resolve(new MessageLoadOracleInputs(leafIndex, siblingPath)); }); if (updateState) {