Skip to content

Commit

Permalink
feat!: l1 to l2 message api takes sender as arg (#4648)
Browse files Browse the repository at this point in the history
Fixes #4559
  • Loading branch information
benesjan committed Feb 21, 2024
1 parent 91d5379 commit 96f6b2a
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 15 deletions.
6 changes: 2 additions & 4 deletions noir-projects/aztec-nr/aztec/src/context/private_context.nr
Original file line number Diff line number Diff line change
Expand Up @@ -218,16 +218,14 @@ impl PrivateContext {
self.new_l2_to_l1_msgs.push(message);
}

// PrivateContextInputs must be temporarily passed in to prevent too many unknowns
// Note this returns self to get around an issue where mutable structs do not maintain mutations unless reassigned
// docs:start:context_consume_l1_to_l2_message
// docs:start:consume_l1_to_l2_message
pub fn consume_l1_to_l2_message(&mut self, msg_key: Field, content: Field, secret: Field) {
pub fn consume_l1_to_l2_message(&mut self, msg_key: Field, content: Field, secret: Field, sender: EthAddress) {
// docs:end:context_consume_l1_to_l2_message
let nullifier = process_l1_to_l2_message(
self.historical_header.state.l1_to_l2_message_tree.root,
self.this_address(),
self.this_portal_address(),
sender,
self.chain_id(),
self.version(),
msg_key,
Expand Down
6 changes: 2 additions & 4 deletions noir-projects/aztec-nr/aztec/src/context/public_context.nr
Original file line number Diff line number Diff line change
Expand Up @@ -155,14 +155,12 @@ impl PublicContext {
self.new_l2_to_l1_msgs.push(message);
}

// PrivateContextInputs must be temporarily passed in to prevent too many unknowns
// Note this returns self to get around an issue where mutable structs do not maintain mutations unless reassigned
pub fn consume_l1_to_l2_message(&mut self, msg_key: Field, content: Field, secret: Field) {
pub fn consume_l1_to_l2_message(&mut self, msg_key: Field, content: Field, secret: Field, sender: EthAddress) {
let this = (*self).this_address();
let nullifier = process_l1_to_l2_message(
self.historical_header.state.l1_to_l2_message_tree.root,
this,
self.this_portal_address(),
sender,
self.chain_id(),
self.version(),
msg_key,
Expand Down
31 changes: 29 additions & 2 deletions noir-projects/noir-contracts/contracts/test_contract/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ contract Test {
) {
let content_hash = get_mint_public_content_hash(to, amount, canceller);
// Consume message and emit nullifier
context.consume_l1_to_l2_message(msg_key, content_hash, secret);
context.consume_l1_to_l2_message(msg_key, content_hash, secret, context.this_portal_address());
}

#[aztec(private)]
Expand All @@ -280,7 +280,34 @@ contract Test {
) {
// Consume L1 to L2 message and emit nullifier
let content_hash = get_mint_private_content_hash(secret_hash_for_redeeming_minted_notes, amount, canceller);
context.consume_l1_to_l2_message(msg_key, content_hash, secret_for_L1_to_L2_message_consumption);
context.consume_l1_to_l2_message(
msg_key,
content_hash,
secret_for_L1_to_L2_message_consumption,
context.this_portal_address()
);
}

#[aztec(public)]
fn consume_message_from_arbitrary_sender_public(
msg_key: Field,
content: Field,
secret: Field,
sender: EthAddress
) {
// Consume message and emit nullifier
context.consume_l1_to_l2_message(msg_key, content, secret, sender);
}

#[aztec(private)]
fn consume_message_from_arbitrary_sender_private(
msg_key: Field,
content: Field,
secret: Field,
sender: EthAddress
) {
// Consume message and emit nullifier
context.consume_l1_to_l2_message(msg_key, content, secret, sender);
}

#[aztec(private)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ contract TokenBridge {
let content_hash = get_mint_public_content_hash(to, amount, canceller);

// Consume message and emit nullifier
context.consume_l1_to_l2_message(msg_key, content_hash, secret);
context.consume_l1_to_l2_message(msg_key, content_hash, secret, context.this_portal_address());

// Mint tokens
Token::at(storage.token.read()).mint_public(context, to, amount);
Expand Down Expand Up @@ -75,7 +75,12 @@ contract TokenBridge {
) {
// Consume L1 to L2 message and emit nullifier
let content_hash = get_mint_private_content_hash(secret_hash_for_redeeming_minted_notes, amount, canceller);
context.consume_l1_to_l2_message(msg_key, content_hash, secret_for_L1_to_L2_message_consumption);
context.consume_l1_to_l2_message(
msg_key,
content_hash,
secret_for_L1_to_L2_message_consumption,
context.this_portal_address()
);

// Mint tokens on L2
// `mint_private` on token is public. So we call an internal public function
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import {
Fr,
TxStatus,
computeAuthWitMessageHash,
computeMessageSecretHash,
sleep,
} from '@aztec/aztec.js';
import { OutboxAbi } from '@aztec/l1-artifacts';
import { InboxAbi, OutboxAbi } from '@aztec/l1-artifacts';
import { TestContract } from '@aztec/noir-contracts.js';
import { TokenContract } from '@aztec/noir-contracts.js/Token';
import { TokenBridgeContract } from '@aztec/noir-contracts.js/TokenBridge';
Expand All @@ -31,6 +32,7 @@ describe('e2e_public_cross_chain_messaging', () => {
let crossChainTestHarness: CrossChainTestHarness;
let l2Token: TokenContract;
let l2Bridge: TokenBridgeContract;
let inbox: any;
let outbox: any;

beforeEach(async () => {
Expand All @@ -46,6 +48,7 @@ describe('e2e_public_cross_chain_messaging', () => {
l2Bridge = crossChainTestHarness.l2Bridge;
ethAccount = crossChainTestHarness.ethAccount;
ownerAddress = crossChainTestHarness.ownerAddress;
inbox = crossChainTestHarness.inbox;
outbox = crossChainTestHarness.outbox;
teardown = teardown_;
user1Wallet = wallets[0];
Expand Down Expand Up @@ -75,15 +78,15 @@ describe('e2e_public_cross_chain_messaging', () => {
expect(await crossChainTestHarness.getL1BalanceOf(ethAccount)).toBe(l1TokenBalance - bridgeAmount);

// Wait for the archiver to process the message
await sleep(5000); /// waiting 5 seconds.
await sleep(5000); // waiting 5 seconds.

// Perform an unrelated transaction on L2 to progress the rollup. Here we mint public tokens.
const unrelatedMintAmount = 99n;
await crossChainTestHarness.mintTokensPublicOnL2(unrelatedMintAmount);
await crossChainTestHarness.expectPublicBalanceOnL2(ownerAddress, unrelatedMintAmount);
const balanceBefore = unrelatedMintAmount;

// 3. Consume L1-> L2 message and mint public tokens on L2
// 3. Consume L1 -> L2 message and mint public tokens on L2
await crossChainTestHarness.consumeMessageOnAztecAndMintPublicly(bridgeAmount, messageKey, secret);
await crossChainTestHarness.expectPublicBalanceOnL2(ownerAddress, balanceBefore + bridgeAmount);
const afterBalance = balanceBefore + bridgeAmount;
Expand Down Expand Up @@ -244,4 +247,81 @@ describe('e2e_public_cross_chain_messaging', () => {
},
60_000,
);

// Note: We register one portal address when deploying contract but that address is no-longer the only address
// allowed to send messages to the given contract. In the following test we'll test that it's really the case.
it.each([true, false])(
'can send an L1 -> L2 message from a non-registered portal address consumed from private or public',
async (isPrivate: boolean) => {
const testContract = await TestContract.deploy(user1Wallet).send().deployed();

const sender = crossChainTestHarness.ethAccount;
const recipient = testContract.address.toString();

const secret = Fr.random();
const secretHash = computeMessageSecretHash(secret);

// The following are arbitrary test values
const content = Fr.random();
const fee = 100_0000n;
const deadline = 4294967295n;

// We inject the message to Inbox
const txHash = await inbox.write.sendL2Message(
[
{ actor: recipient as Hex, version: 1n },
deadline,
content.toString() as Hex,
secretHash.toString() as Hex,
] as const,
{ value: fee } as any,
);

// We check that the message was correctly injected by checking the emitted event and we store the message key
// for later use
let msgKey!: Fr;
{
const abiItem = getAbiItem({
abi: InboxAbi,
name: 'MessageAdded',
});

const events = await crossChainTestHarness.publicClient.getLogs<typeof abiItem>({
address: getAddress(inbox.address.toString()),
event: abiItem,
fromBlock: 0n,
});

// We get the event just for the relevant transaction
const txEvents = events.filter(event => event.transactionHash === txHash);

// We check that exactly 1 MessageAdded event was emitted with the expected recipient
expect(txEvents.length).toBe(1);
expect(txEvents[0].args.recipient).toBe(recipient);

// TODO(#4678): Unify naming of message key/entry key
msgKey = Fr.fromString(txEvents[0].args.entryKey!);
}

// We wait for the archiver to process the message and we push a block for the message to be confirmed
{
await sleep(5000); // waiting 5 seconds.
await testContract.methods.get_this_portal_address().send().wait();
}

// Finally, e consume the L1 -> L2 message using the test contract either from private or public
if (isPrivate) {
await testContract.methods
.consume_message_from_arbitrary_sender_private(msgKey, content, secret, sender)
.send()
.wait();
} else {
await testContract.methods
.consume_message_from_arbitrary_sender_public(msgKey, content, secret, sender)
.send()
.wait();
}
},
60_000,
);
});
11 changes: 11 additions & 0 deletions yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
sha256,
} from '@aztec/aztec.js';
import {
InboxAbi,
OutboxAbi,
PortalERC20Abi,
PortalERC20Bytecode,
Expand Down Expand Up @@ -134,6 +135,13 @@ export class CrossChainTestHarness {
const owner = wallet.getCompleteAddress();
const l1ContractAddresses = (await pxeService.getNodeInfo()).l1ContractAddresses;

const inbox = getContract({
address: l1ContractAddresses.inboxAddress.toString(),
abi: InboxAbi,
walletClient,
publicClient,
});

const outbox = getContract({
address: l1ContractAddresses.outboxAddress.toString(),
abi: OutboxAbi,
Expand Down Expand Up @@ -163,6 +171,7 @@ export class CrossChainTestHarness {
tokenPortalAddress,
tokenPortal,
underlyingERC20,
inbox,
outbox,
publicClient,
walletClient,
Expand Down Expand Up @@ -190,6 +199,8 @@ export class CrossChainTestHarness {
public tokenPortal: any,
/** Underlying token for portal tests. */
public underlyingERC20: any,
/** Message Bridge Inbox. */
public inbox: any,
/** Message Bridge Outbox. */
public outbox: any,
/** Viem Public client instance. */
Expand Down

0 comments on commit 96f6b2a

Please sign in to comment.