From f35bac5d35d22e269d8bf2ffccc4484a715d70ae Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Tue, 30 Jul 2024 07:44:23 -0300 Subject: [PATCH] feat: Handle L1toL2 msgs in prover-node (#7654) 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. --- .../composed/integration_l1_publisher.test.ts | 44 ++---------- .../end-to-end/src/e2e_prover_node.test.ts | 71 ++++++++++++++----- .../l1_to_l2.test.ts | 41 +---------- ...lic_cross_chain_messaging_contract_test.ts | 1 + .../src/fixtures/l1_to_l2_messaging.ts | 58 +++++++++++++++ .../src/shared/cross_chain_test_harness.ts | 5 ++ yarn-project/prover-node/src/factory.ts | 2 +- .../prover-node/src/job/block-proving-job.ts | 9 ++- yarn-project/prover-node/src/prover-node.ts | 4 +- 9 files changed, 137 insertions(+), 98 deletions(-) create mode 100644 yarn-project/end-to-end/src/fixtures/l1_to_l2_messaging.ts diff --git a/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts b/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts index a71ff1b65ba..2d6127b601d 100644 --- a/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts +++ b/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts @@ -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, @@ -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' @@ -188,35 +180,11 @@ describe('L1Publisher integration', () => { return processedTx; }; - const sendToL2 = async (content: Fr, recipientAddress: AztecAddress): Promise => { - // @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 => { + 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); }; /** diff --git a/yarn-project/end-to-end/src/e2e_prover_node.test.ts b/yarn-project/end-to-end/src/e2e_prover_node.test.ts index af0bf72045a..e2e5f6941c7 100644 --- a/yarn-project/end-to-end/src/e2e_prover_node.test.ts +++ b/yarn-project/end-to-end/src/e2e_prover_node.test.ts @@ -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, @@ -30,17 +35,36 @@ describe('e2e_prover_node', () => { let wallet: AccountWalletWithSecretKey; let recipient: AztecAddress; let contract: StatefulTestContract; + let msgTestContract: TestContract; let txReceipts: FieldsOf[]; 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 = { 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())); @@ -64,13 +88,18 @@ 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(); }, ); @@ -78,11 +107,21 @@ describe('e2e_prover_node', () => { 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); @@ -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); }); }); diff --git a/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging/l1_to_l2.test.ts b/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging/l1_to_l2.test.ts index bc31966bfe6..615ea99c97b 100644 --- a/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging/l1_to_l2.test.ts +++ b/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging/l1_to_l2.test.ts @@ -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(); @@ -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 () => { @@ -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); }; }); diff --git a/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging/public_cross_chain_messaging_contract_test.ts b/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging/public_cross_chain_messaging_contract_test.ts index b747d543e91..f344d479131 100644 --- a/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging/public_cross_chain_messaging_contract_test.ts +++ b/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging/public_cross_chain_messaging_contract_test.ts @@ -166,6 +166,7 @@ export class PublicCrossChainMessagingContractTest { publicClient, walletClient, this.ownerAddress, + this.aztecNodeConfig.l1Contracts, ); this.publicClient = publicClient; diff --git a/yarn-project/end-to-end/src/fixtures/l1_to_l2_messaging.ts b/yarn-project/end-to-end/src/fixtures/l1_to_l2_messaging.ts new file mode 100644 index 00000000000..ff7ab861e2f --- /dev/null +++ b/yarn-project/end-to-end/src/fixtures/l1_to_l2_messaging.ts @@ -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; + }, +) { + 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); +} 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 7adf0e29ac4..b6f43b98435 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 @@ -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, @@ -185,6 +186,7 @@ export class CrossChainTestHarness { publicClient, walletClient, owner.address, + l1ContractAddresses, ); } @@ -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, ) {} /** diff --git a/yarn-project/prover-node/src/factory.ts b/yarn-project/prover-node/src/factory.ts index 6b5c65d20f2..d2e18610b14 100644 --- a/yarn-project/prover-node/src/factory.ts +++ b/yarn-project/prover-node/src/factory.ts @@ -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); } diff --git a/yarn-project/prover-node/src/job/block-proving-job.ts b/yarn-project/prover-node/src/job/block-proving-job.ts index 1857a26bd71..e0d5c76e92f 100644 --- a/yarn-project/prover-node/src/job/block-proving-job.ts +++ b/yarn-project/prover-node/src/job/block-proving-job.ts @@ -1,6 +1,7 @@ import { type BlockProver, EmptyTxValidator, + type L1ToL2MessageSource, type L2Block, type L2BlockSource, PROVING_STATUS, @@ -9,7 +10,6 @@ import { type TxHash, type TxProvider, } from '@aztec/circuit-types'; -import { type Fr } from '@aztec/circuits.js'; import { createDebugLogger } from '@aztec/foundation/log'; import { type L1Publisher } from '@aztec/sequencer-client'; import { type PublicProcessor, type PublicProcessorFactory } from '@aztec/simulator'; @@ -28,6 +28,7 @@ export class BlockProvingJob { private publicProcessorFactory: PublicProcessorFactory, private publisher: L1Publisher, private l2BlockSource: L2BlockSource, + private l1ToL2MessageSource: L1ToL2MessageSource, private txProvider: TxProvider, ) {} @@ -53,7 +54,7 @@ export class BlockProvingJob { const globalVariables = block.header.globalVariables; const txHashes = block.body.txEffects.map(tx => tx.txHash); const txCount = block.body.numberOfTxsIncludingPadded; - const l1ToL2Messages: Fr[] = []; // TODO: grab L1 to L2 messages for this block + const l1ToL2Messages = await this.getL1ToL2Messages(block); this.log.verbose(`Starting block processing`, { number: block.number, @@ -116,6 +117,10 @@ export class BlockProvingJob { return txs.map(([_, tx]) => tx!); } + private getL1ToL2Messages(block: L2Block) { + return this.l1ToL2MessageSource.getL1ToL2Messages(BigInt(block.number)); + } + private async processTxs( publicProcessor: PublicProcessor, txs: Tx[], diff --git a/yarn-project/prover-node/src/prover-node.ts b/yarn-project/prover-node/src/prover-node.ts index bd41429fb27..1c435e3cfa3 100644 --- a/yarn-project/prover-node/src/prover-node.ts +++ b/yarn-project/prover-node/src/prover-node.ts @@ -1,4 +1,4 @@ -import { type L2BlockSource, type ProverClient, type TxProvider } from '@aztec/circuit-types'; +import { type L1ToL2MessageSource, type L2BlockSource, type ProverClient, type TxProvider } from '@aztec/circuit-types'; import { createDebugLogger } from '@aztec/foundation/log'; import { RunningPromise } from '@aztec/foundation/running-promise'; import { type L1Publisher } from '@aztec/sequencer-client'; @@ -20,6 +20,7 @@ export class ProverNode { private publicProcessorFactory: PublicProcessorFactory, private publisher: L1Publisher, private l2BlockSource: L2BlockSource, + private l1ToL2MessageSource: L1ToL2MessageSource, private txProvider: TxProvider, private options: { pollingIntervalMs: number; disableAutomaticProving: boolean } = { pollingIntervalMs: 1_000, @@ -102,6 +103,7 @@ export class ProverNode { this.publicProcessorFactory, this.publisher, this.l2BlockSource, + this.l1ToL2MessageSource, this.txProvider, ); }