Skip to content

Commit

Permalink
feat: Handle L1toL2 msgs in prover-node (#7654)
Browse files Browse the repository at this point in the history
Prover node was not collecting l1 to l2 messages for proofs. This PR
adds a call to the archiver to retrieve them for the block being proven,
and extends the e2e_prover_node test to include a tx with a msg.
  • Loading branch information
spalladino authored Jul 30, 2024
1 parent 45e7867 commit f35bac5
Show file tree
Hide file tree
Showing 9 changed files with 137 additions and 98 deletions.
Original file line number Diff line number Diff line change
@@ -1,15 +1,6 @@
import { type ArchiveSource } from '@aztec/archiver';
import { getConfigEnvVars } from '@aztec/aztec-node';
import {
AztecAddress,
Body,
Fr,
GlobalVariables,
L2Actor,
type L2Block,
createDebugLogger,
mockTx,
} from '@aztec/aztec.js';
import { AztecAddress, Body, Fr, GlobalVariables, type L2Block, createDebugLogger, mockTx } from '@aztec/aztec.js';
// eslint-disable-next-line no-restricted-imports
import {
PROVING_STATUS,
Expand Down Expand Up @@ -60,6 +51,7 @@ import {
} from 'viem';
import { type PrivateKeyAccount, privateKeyToAccount } from 'viem/accounts';

import { sendL1ToL2Message } from '../fixtures/l1_to_l2_messaging.js';
import { setupL1Contracts } from '../fixtures/utils.js';

// Accounts 4 and 5 of Anvil default startup with mnemonic: 'test test test test test test test test test test test junk'
Expand Down Expand Up @@ -188,35 +180,11 @@ describe('L1Publisher integration', () => {
return processedTx;
};

const sendToL2 = async (content: Fr, recipientAddress: AztecAddress): Promise<Fr> => {
// @todo @LHerskind version hardcoded here (update to bigint or field)
const recipient = new L2Actor(recipientAddress, 1);
// getting the 32 byte hex string representation of the content
const contentString = content.toString();
// Using the 0 value for the secretHash.
const emptySecretHash = Fr.ZERO.toString();

const txHash = await inbox.write.sendL2Message(
[{ actor: recipient.recipient.toString(), version: BigInt(recipient.version) }, contentString, emptySecretHash],
{} as any,
const sendToL2 = (content: Fr, recipient: AztecAddress): Promise<Fr> => {
return sendL1ToL2Message(
{ content, secretHash: Fr.ZERO, recipient },
{ publicClient, walletClient, l1ContractAddresses },
);

const txReceipt = await publicClient.waitForTransactionReceipt({
hash: txHash,
});

// Exactly 1 event should be emitted in the transaction
expect(txReceipt.logs.length).toBe(1);

// We decode the event log before checking it
const txLog = txReceipt.logs[0];
const topics = decodeEventLog({
abi: InboxAbi,
data: txLog.data,
topics: txLog.topics,
});

return Fr.fromString(topics.args.hash);
};

/**
Expand Down
71 changes: 53 additions & 18 deletions yarn-project/end-to-end/src/e2e_prover_node.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,21 @@ import {
type AccountWalletWithSecretKey,
type AztecAddress,
type DebugLogger,
EthAddress,
type FieldsOf,
Fr,
SignerlessWallet,
type TxReceipt,
computeSecretHash,
createDebugLogger,
retryUntil,
sleep,
} from '@aztec/aztec.js';
import { StatefulTestContract } from '@aztec/noir-contracts.js';
import { createProverNode } from '@aztec/prover-node';
import { StatefulTestContract, TestContract } from '@aztec/noir-contracts.js';
import { type ProverNode, createProverNode } from '@aztec/prover-node';
import { type SequencerClientConfig } from '@aztec/sequencer-client';

import { sendL1ToL2Message } from './fixtures/l1_to_l2_messaging.js';
import {
type ISnapshotManager,
type SubsystemsContext,
Expand All @@ -30,17 +35,36 @@ describe('e2e_prover_node', () => {
let wallet: AccountWalletWithSecretKey;
let recipient: AztecAddress;
let contract: StatefulTestContract;
let msgTestContract: TestContract;
let txReceipts: FieldsOf<TxReceipt>[];

let logger: DebugLogger;

let snapshotManager: ISnapshotManager;

const msgContent: Fr = Fr.fromString('0xcafe');
const msgSecret: Fr = Fr.fromString('0xfeca');

beforeAll(async () => {
logger = createDebugLogger('aztec:e2e_prover_node');
const config: Partial<SequencerClientConfig> = { sequencerSkipSubmitProofs: true };
snapshotManager = createSnapshotManager(`e2e_prover_node`, process.env.E2E_DATA_PATH, config);

const testContractOpts = { contractAddressSalt: Fr.ONE, universalDeploy: true };
await snapshotManager.snapshot(
'send-l1-to-l2-msg',
async ctx => {
const testContract = TestContract.deploy(new SignerlessWallet(ctx.pxe)).getInstance(testContractOpts);
const msgHash = await sendL1ToL2Message(
{ recipient: testContract.address, content: msgContent, secretHash: computeSecretHash(msgSecret) },
ctx.deployL1ContractsValues,
);
return { msgHash };
},
async (_data, ctx) => {
msgTestContract = await TestContract.deploy(new SignerlessWallet(ctx.pxe)).register(testContractOpts);
},
);

await snapshotManager.snapshot('setup', addAccounts(2, logger), async ({ accountKeys }, ctx) => {
const accountManagers = accountKeys.map(ak => getSchnorrAccount(ctx.pxe, ak[0], ak[1], 1));
await Promise.all(accountManagers.map(a => a.register()));
Expand All @@ -64,25 +88,40 @@ describe('e2e_prover_node', () => {

await snapshotManager.snapshot(
'create-blocks',
async () => {
const txReceipt1 = await contract.methods.create_note(recipient, recipient, 10).send().wait();
const txReceipt2 = await contract.methods.increment_public_value(recipient, 20).send().wait();
return { txReceipt1, txReceipt2 };
async ctx => {
const msgSender = ctx.deployL1ContractsValues.walletClient.account.address;
const txReceipt1 = await msgTestContract.methods
.consume_message_from_arbitrary_sender_private(msgContent, msgSecret, EthAddress.fromString(msgSender))
.send()
.wait();
const txReceipt2 = await contract.methods.create_note(recipient, recipient, 10).send().wait();
const txReceipt3 = await contract.methods.increment_public_value(recipient, 20).send().wait();
return { txReceipts: [txReceipt1, txReceipt2, txReceipt3] };
},
({ txReceipt1, txReceipt2 }) => {
txReceipts = [txReceipt1, txReceipt2];
data => {
txReceipts = data.txReceipts;
return Promise.resolve();
},
);

ctx = await snapshotManager.setup();
});

it('submits two blocks, then prover proves the first one', async () => {
const prove = async (proverNode: ProverNode, blockNumber: number) => {
logger.info(`Proving block ${blockNumber}`);
await proverNode.prove(blockNumber, blockNumber);

logger.info(`Proof submitted. Awaiting aztec node to sync...`);
await retryUntil(async () => (await ctx.aztecNode.getProvenBlockNumber()) === blockNumber, 'block-1', 10, 1);
expect(await ctx.aztecNode.getProvenBlockNumber()).toEqual(blockNumber);
};

it('submits three blocks, then prover proves the first two', async () => {
// Check everything went well during setup and txs were mined in two different blocks
const [txReceipt1, txReceipt2] = txReceipts;
const [txReceipt1, txReceipt2, txReceipt3] = txReceipts;
const firstBlock = txReceipt1.blockNumber!;
expect(txReceipt2.blockNumber).toEqual(firstBlock + 1);
expect(txReceipt3.blockNumber).toEqual(firstBlock + 2);
expect(await contract.methods.get_public_value(recipient).simulate()).toEqual(20n);
expect(await contract.methods.summed_values(recipient).simulate()).toEqual(10n);
expect(await ctx.aztecNode.getProvenBlockNumber()).toEqual(0);
Expand All @@ -101,12 +140,8 @@ describe('e2e_prover_node', () => {
const archiver = ctx.aztecNode.getBlockSource() as Archiver;
const proverNode = await createProverNode(proverConfig, { aztecNodeTxProvider: ctx.aztecNode, archiver });

// Prove block from first tx and block until it is proven
logger.info(`Proving block ${firstBlock}`);
await proverNode.prove(firstBlock, firstBlock);

logger.info(`Proof submitted. Awaiting aztec node to sync...`);
await retryUntil(async () => (await ctx.aztecNode.getProvenBlockNumber()) === firstBlock, 'proven-block', 10, 1);
expect(await ctx.aztecNode.getProvenBlockNumber()).toEqual(firstBlock);
// Prove the first two blocks
await prove(proverNode, firstBlock);
await prove(proverNode, firstBlock + 1);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,15 @@ import {
L2Actor,
computeSecretHash,
} from '@aztec/aztec.js';
import { InboxAbi } from '@aztec/l1-artifacts';
import { TestContract } from '@aztec/noir-contracts.js';

import { type Hex, decodeEventLog } from 'viem';

import { sendL1ToL2Message } from '../fixtures/l1_to_l2_messaging.js';
import { PublicCrossChainMessagingContractTest } from './public_cross_chain_messaging_contract_test.js';

describe('e2e_public_cross_chain_messaging l1_to_l2', () => {
const t = new PublicCrossChainMessagingContractTest('l1_to_l2');

let { crossChainTestHarness, aztecNode, user1Wallet, inbox } = t;
let { crossChainTestHarness, aztecNode, user1Wallet } = t;

beforeAll(async () => {
await t.applyBaseSnapshots();
Expand All @@ -26,7 +24,6 @@ describe('e2e_public_cross_chain_messaging l1_to_l2', () => {
({ crossChainTestHarness, user1Wallet } = t);

aztecNode = crossChainTestHarness.aztecNode;
inbox = crossChainTestHarness.inbox;
}, 300_000);

afterAll(async () => {
Expand Down Expand Up @@ -84,39 +81,7 @@ describe('e2e_public_cross_chain_messaging l1_to_l2', () => {
);

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

// We check that the message was correctly injected by checking the emitted event
const msgHash = message.hash();
{
const txReceipt = await crossChainTestHarness.publicClient.waitForTransactionReceipt({
hash: txHash,
});

// Exactly 1 event should be emitted in the transaction
expect(txReceipt.logs.length).toBe(1);

// We decode the event and get leaf out of it
const txLog = txReceipt.logs[0];
const topics = decodeEventLog({
abi: InboxAbi,
data: txLog.data,
topics: txLog.topics,
});
const receivedMsgHash = topics.args.hash;

// We check that the leaf inserted into the subtree matches the expected message hash
expect(receivedMsgHash).toBe(msgHash.toString());
}

const msgHash = await sendL1ToL2Message(message, crossChainTestHarness);
await crossChainTestHarness.makeMessageConsumable(msgHash);
};
});
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ export class PublicCrossChainMessagingContractTest {
publicClient,
walletClient,
this.ownerAddress,
this.aztecNodeConfig.l1Contracts,
);

this.publicClient = publicClient;
Expand Down
58 changes: 58 additions & 0 deletions yarn-project/end-to-end/src/fixtures/l1_to_l2_messaging.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { type L1ToL2Message } from '@aztec/aztec.js';
import { type AztecAddress, Fr } from '@aztec/circuits.js';
import { type L1ContractAddresses } from '@aztec/ethereum';
import { InboxAbi } from '@aztec/l1-artifacts';

import { expect } from '@jest/globals';
import { type Hex, type PublicClient, type WalletClient, decodeEventLog, getContract } from 'viem';

export async function sendL1ToL2Message(
message: L1ToL2Message | { recipient: AztecAddress; content: Fr; secretHash: Fr },
ctx: {
walletClient: WalletClient;
publicClient: PublicClient;
l1ContractAddresses: Pick<L1ContractAddresses, 'inboxAddress'>;
},
) {
const inbox = getContract({
address: ctx.l1ContractAddresses.inboxAddress.toString(),
abi: InboxAbi,
client: ctx.walletClient,
});

const recipient = 'recipient' in message.recipient ? message.recipient.recipient : message.recipient;
const version = 'version' in message.recipient ? message.recipient.version : 1;

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

// We check that the message was correctly injected by checking the emitted event
const txReceipt = await ctx.publicClient.waitForTransactionReceipt({ hash: txHash });

// Exactly 1 event should be emitted in the transaction
expect(txReceipt.logs.length).toBe(1);

// We decode the event and get leaf out of it
const txLog = txReceipt.logs[0];
const topics = decodeEventLog({
abi: InboxAbi,
data: txLog.data,
topics: txLog.topics,
});
const receivedMsgHash = topics.args.hash;

// We check that the leaf inserted into the subtree matches the expected message hash
if ('hash' in message) {
const msgHash = message.hash();
expect(receivedMsgHash).toBe(msgHash.toString());
}

return Fr.fromString(receivedMsgHash);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
deployL1Contract,
retryUntil,
} from '@aztec/aztec.js';
import { type L1ContractAddresses } from '@aztec/ethereum';
import { sha256ToField } from '@aztec/foundation/crypto';
import {
InboxAbi,
Expand Down Expand Up @@ -185,6 +186,7 @@ export class CrossChainTestHarness {
publicClient,
walletClient,
owner.address,
l1ContractAddresses,
);
}

Expand Down Expand Up @@ -221,6 +223,9 @@ export class CrossChainTestHarness {

/** Aztec address to use in tests. */
public ownerAddress: AztecAddress,

/** Deployment addresses for all L1 contracts */
public readonly l1ContractAddresses: L1ContractAddresses,
) {}

/**
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/prover-node/src/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,5 @@ export async function createProverNode(
? new AztecNodeTxProvider(deps.aztecNodeTxProvider)
: createTxProvider(config);

return new ProverNode(prover!, publicProcessorFactory, publisher, archiver, txProvider);
return new ProverNode(prover!, publicProcessorFactory, publisher, archiver, archiver, txProvider);
}
Loading

0 comments on commit f35bac5

Please sign in to comment.