Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 49 additions & 94 deletions yarn-project/end-to-end/src/e2e_cross_chain_messaging/l2_to_l1.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,33 +74,19 @@ describe('e2e_cross_chain_messaging l2_to_l1', () => {
.wait();
}

const l2ToL1Message = {
sender: { actor: contract.address.toString() as Hex, version: BigInt(version) },
recipient: {
actor: recipient.toString() as Hex,
chainId: BigInt(crossChainTestHarness.l1Client.chain.id),
},
content: content.toString() as Hex,
};

const leaf = sha256ToField([
contract.address,
new Fr(version), // aztec version
recipient.toBuffer32(),
new Fr(crossChainTestHarness.l1Client.chain.id), // chain id
content,
]);
const message = makeL2ToL1Message(recipient, content);
const messageLeaf = computeMessageLeaf(message);

const [l2MessageIndex, siblingPath] = await aztecNode.getL2ToL1MessageMembershipWitness(
l2TxReceipt.blockNumber!,
leaf,
messageLeaf,
);

await t.assumeProven();

const txHash = await outbox.write.consume(
[
l2ToL1Message,
message,
BigInt(l2TxReceipt.blockNumber!),
BigInt(l2MessageIndex),
siblingPath.toBufferArray().map((buf: Buffer) => `0x${buf.toString('hex')}`) as readonly `0x${string}`[],
Expand Down Expand Up @@ -132,7 +118,7 @@ describe('e2e_cross_chain_messaging l2_to_l1', () => {
};

// We check that MessageConsumed event was emitted with the expected message hash and leaf index
expect(topics.args.messageHash).toStrictEqual(leaf.toString());
expect(topics.args.messageHash).toStrictEqual(messageLeaf.toString());
expect(topics.args.leafIndex).toStrictEqual(BigInt(0));
},
60_000,
Expand Down Expand Up @@ -162,33 +148,19 @@ describe('e2e_cross_chain_messaging l2_to_l1', () => {
await aztecNodeAdmin.setConfig({ minTxsPerBlock: 1 });
}

const l2ToL1Message = {
sender: { actor: contract.address.toString() as Hex, version: BigInt(version) },
recipient: {
actor: recipient.toString() as Hex,
chainId: BigInt(crossChainTestHarness.l1Client.chain.id),
},
content: content.toString() as Hex,
};

const leaf = sha256ToField([
contract.address,
new Fr(version),
recipient.toBuffer32(),
new Fr(crossChainTestHarness.l1Client.chain.id),
content,
]);
const message = makeL2ToL1Message(recipient, content);
const messageLeaf = computeMessageLeaf(message);

const [l2MessageIndex, siblingPath] = await aztecNode.getL2ToL1MessageMembershipWitness(
receiptOfTxWithTheMessage.blockNumber!,
leaf,
messageLeaf,
);

await t.assumeProven();

const l1TxHash = await outbox.write.consume(
[
l2ToL1Message,
message,
BigInt(receiptOfTxWithTheMessage.blockNumber!),
BigInt(l2MessageIndex),
siblingPath.toBufferArray().map((buf: Buffer) => `0x${buf.toString('hex')}`) as readonly `0x${string}`[],
Expand Down Expand Up @@ -220,7 +192,7 @@ describe('e2e_cross_chain_messaging l2_to_l1', () => {
};

// We check that MessageConsumed event was emitted with the expected message hash and leaf index
expect(topics.args.messageHash).toStrictEqual(leaf.toString());
expect(topics.args.messageHash).toStrictEqual(messageLeaf.toString());
expect(topics.args.leafIndex).toStrictEqual(l2MessageIndex);
}, 60_000);

Expand All @@ -245,11 +217,10 @@ describe('e2e_cross_chain_messaging l2_to_l1', () => {

const l2ToL1Messages = block?.body.txEffects.flatMap(txEffect => txEffect.l2ToL1Msgs);

expect(l2ToL1Messages?.map(l2ToL1Message => l2ToL1Message.toString())).toStrictEqual(
[makeL2ToL1Message(recipient1, content1), makeL2ToL1Message(recipient2, content2)].map(expectedL2ToL1Message =>
expectedL2ToL1Message.toString(),
),
);
expect(l2ToL1Messages).toStrictEqual([
computeMessageLeaf(makeL2ToL1Message(recipient1, content1)),
computeMessageLeaf(makeL2ToL1Message(recipient2, content2)),
]);

// For each individual message, we are using our node API to grab the index and sibling path. We expect
// the index to match the order of the block we obtained earlier. We also then use this sibling path to hash up to the root,
Expand Down Expand Up @@ -285,15 +256,7 @@ describe('e2e_cross_chain_messaging l2_to_l1', () => {
expect(l1MinHeight).toEqual(BigInt(siblingPath.pathSize));

// Consume msg 2
// Taken from l2_to_l1.test
const msg2 = {
sender: { actor: contract.address.toString() as `0x${string}`, version: BigInt(version) },
recipient: {
actor: recipient2.toString() as `0x${string}`,
chainId: BigInt(t.deployL1ContractsValues.l1Client.chain.id),
},
content: content2.toString() as `0x${string}`,
};
const msg2 = makeL2ToL1Message(recipient2, content2);

const txHash = await outbox.write.consume(
[
Expand Down Expand Up @@ -341,9 +304,10 @@ describe('e2e_cross_chain_messaging l2_to_l1', () => {
await expect(consumeAgain).rejects.toThrow();
});

it('Inserts two transactions with total four out messages, and verifies sibling paths of two new messages', async () => {
it('Inserts two transactions with total four out messages, and verifies sibling paths of both the new messages', async () => {
// Force txs to be in the same block
await aztecNodeAdmin!.setConfig({ minTxsPerBlock: 2 });
// recipient2 = msg.sender, so we can consume it later
const [[recipient1, content1], [recipient2, content2], [recipient3, content3], [recipient4, content4]] = [
[EthAddress.random(), Fr.random()],
[EthAddress.fromString(t.deployL1ContractsValues.l1Client.account.address), Fr.random()],
Expand Down Expand Up @@ -372,21 +336,21 @@ describe('e2e_cross_chain_messaging l2_to_l1', () => {
// the index to match the order of the block we obtained earlier. We also then use this sibling path to hash up to the root,
// verifying that the expected root obtained through the message and the sibling path match the actual root
// that was returned by the circuits in the header as out_hash.
const singleMessage = makeL2ToL1Message(recipient4, content4);
const singleMessageLeaf = computeMessageLeaf(makeL2ToL1Message(recipient4, content4));
const [index, siblingPath] = await aztecNode.getL2ToL1MessageMembershipWitness(
l2TxReceipt0.blockNumber!,
singleMessage,
singleMessageLeaf,
);
// The solo message is the only one in the tx, so it only requires a subtree of height 1
// +1 for being rolled up
expect(siblingPath.pathSize).toBe(2);
const expectedRoot = calculateExpectedRoot(singleMessage, siblingPath as SiblingPath<2>, index);
const expectedRoot = calculateExpectedRoot(singleMessageLeaf, siblingPath as SiblingPath<2>, index);
expect(expectedRoot.toString('hex')).toEqual(block?.header.contentCommitment.outHash.toString('hex'));

const messageToConsume = makeL2ToL1Message(recipient2, content2);
const leafOfMessageToConsume = computeMessageLeaf(makeL2ToL1Message(recipient2, content2));
const [index2, siblingPath2] = await aztecNode.getL2ToL1MessageMembershipWitness(
l2TxReceipt0.blockNumber!,
messageToConsume,
leafOfMessageToConsume,
);
// This message is in a group of 3, => it needs a subtree of height 2
// +1 for being rolled up
Expand All @@ -404,15 +368,7 @@ describe('e2e_cross_chain_messaging l2_to_l1', () => {
expect(l1MinHeight).toEqual(BigInt(siblingPath.pathSize));

// Consume msg 2
// Taken from l2_to_l1.test
const msg2 = {
sender: { actor: contract.address.toString() as `0x${string}`, version: BigInt(version) },
recipient: {
actor: recipient2.toString() as `0x${string}`,
chainId: BigInt(t.deployL1ContractsValues.l1Client.chain.id),
},
content: content2.toString() as `0x${string}`,
};
const msg2 = makeL2ToL1Message(recipient2, content2);

const txHash = await outbox.write.consume(
[
Expand Down Expand Up @@ -444,7 +400,7 @@ describe('e2e_cross_chain_messaging l2_to_l1', () => {
};
};
// Consumed the expected message
expect(topics.args.messageHash).toStrictEqual(messageToConsume.toString());
expect(topics.args.messageHash).toStrictEqual(leafOfMessageToConsume.toString());
expect(topics.args.leafIndex).toStrictEqual(BigInt(index2));

const consumeAgain = outbox.write.consume(
Expand Down Expand Up @@ -480,14 +436,13 @@ describe('e2e_cross_chain_messaging l2_to_l1', () => {

const l2ToL1Messages = block?.body.txEffects.flatMap(txEffect => txEffect.l2ToL1Msgs);
const messageToConsume = makeL2ToL1Message(recipient2, content2);
const leafOfMessageToConsume = computeMessageLeaf(messageToConsume);

// We cannot guarantee the order of txs in blocks
expect(
l2ToL1Messages?.map(l2ToL1Message =>
l2ToL1Message.toString().includes(makeL2ToL1Message(recipient1, content1).toString()),
),
expect(l2ToL1Messages?.some(msg => msg.equals(computeMessageLeaf(makeL2ToL1Message(recipient1, content1))))).toBe(
true,
);
expect(l2ToL1Messages?.map(l2ToL1Message => l2ToL1Message.toString().includes(messageToConsume.toString())));
expect(l2ToL1Messages?.some(msg => msg.equals(leafOfMessageToConsume))).toBe(true);

// For each individual message, we are using our node API to grab the index and sibling path. We expect
// the index to match the order of the block we obtained earlier. We also then use this sibling path to hash up to the root,
Expand Down Expand Up @@ -521,21 +476,12 @@ describe('e2e_cross_chain_messaging l2_to_l1', () => {
expect(l1MinHeight).toEqual(BigInt(siblingPath.pathSize));

// Consume msg 2
// Taken from l2_to_l1.test
const msg2 = {
sender: { actor: contract.address.toString() as `0x${string}`, version: BigInt(version) },
recipient: {
actor: recipient2.toString() as `0x${string}`,
chainId: BigInt(t.deployL1ContractsValues.l1Client.chain.id),
},
content: content2.toString() as `0x${string}`,
};
const [inputIndex, inputPath] = messageToConsume.equals(l2ToL1Messages![0])
const [inputIndex, inputPath] = leafOfMessageToConsume.equals(l2ToL1Messages![0])
? [index, siblingPath]
: [index2, siblingPath2];
const txHash = await outbox.write.consume(
[
msg2,
messageToConsume,
BigInt(l2TxReceipt0.blockNumber!),
BigInt(inputIndex),
inputPath.toBufferArray().map((buf: Buffer) => `0x${buf.toString('hex')}`) as readonly `0x${string}`[],
Expand Down Expand Up @@ -563,12 +509,12 @@ describe('e2e_cross_chain_messaging l2_to_l1', () => {
};
};
// Consumed the expected message
expect(topics.args.messageHash).toStrictEqual(messageToConsume.toString());
expect(topics.args.messageHash).toStrictEqual(leafOfMessageToConsume.toString());
expect(topics.args.leafIndex).toStrictEqual(BigInt(inputIndex));

const consumeAgain = outbox.write.consume(
[
msg2,
messageToConsume,
BigInt(l2TxReceipt0.blockNumber!),
BigInt(index2),
siblingPath2.toBufferArray().map((buf: Buffer) => `0x${buf.toString('hex')}`) as readonly `0x${string}`[],
Expand Down Expand Up @@ -602,15 +548,24 @@ describe('e2e_cross_chain_messaging l2_to_l1', () => {
return truncateAndPad(merkleSha256.hash(...secondLayerInput));
}

function makeL2ToL1Message(recipient: EthAddress, content: Fr = Fr.ZERO): Fr {
const leaf = sha256ToField([
function makeL2ToL1Message(recipient: EthAddress, content: Fr = Fr.ZERO) {
return {
sender: { actor: contract.address.toString() as Hex, version: BigInt(version) },
recipient: {
actor: recipient.toString() as Hex,
chainId: BigInt(crossChainTestHarness.l1Client.chain.id),
},
content: content.toString() as Hex,
};
}

function computeMessageLeaf(message: ReturnType<typeof makeL2ToL1Message>) {
return sha256ToField([
contract.address,
new Fr(version),
recipient.toBuffer32(),
new Fr(t.deployL1ContractsValues.l1Client.chain.id), // chain id
content,
new Fr(message.sender.version),
EthAddress.fromString(message.recipient.actor).toBuffer32(),
new Fr(message.recipient.chainId),
Fr.fromString(message.content),
]);

return leaf;
}
});