Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Handle L1toL2 msgs in prover-node #7654

Merged
merged 1 commit into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
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
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
Loading