We have a lingering issue with L1 to L2 messages: if a tx consumes an L1 to L2 message on the same block it is seen by the archiver, then that block is not provable. This is because of a mismatch between how the sequencer and the prover node work.
The sequencer uses the block_builder, which when starting a new block, first inserts all L1 to L2 messages into the tree to make them available, so when txs are processed by the public_processor, they can access the messages.
|
await blockFactory.startNewBlock(newGlobalVariables, l1ToL2Messages); |
|
|
|
const [publicProcessorDuration, [processedTxs, failedTxs, usedTxs]] = await elapsed(() => |
|
processor.process(pendingTxs, opts, validator), |
|
); |
|
|
|
// All real transactions have been added, set the block as full and pad if needed |
|
await blockFactory.addTxs(processedTxs); |
|
const block = await blockFactory.setBlockCompleted(); |
However, the prover node doesn't. It forks world-state immediately before the block it wants to prove, and then calls the public_processor without first adding the L1 to L2 messages.
|
// Process public fns |
|
const db = await this.dbProvider.fork(block.number - 1); |
|
const publicProcessor = this.publicProcessorFactory.create(db, globalVariables, true); |
|
const processed = await this.processTxs(publicProcessor, txs); |
|
await this.prover.addTxs(processed); |
|
await db.close(); |
So, in #16375 we changed it so that we do add the L1 to L2 messages before we start processing. But then we started getting this error:
[17:19:39.610] ERROR: prover-client:broker-circuit-prover-facade Job errored with 'Circuit execution failed: last_l1_to_l2 in constants does not match the value in the previous block header' id=1:BLOCK_ROOT_ROLLUP:fa05417b0e9ee6e429e870e21962107691067177f857f0b33fb3869c7399bb2a type=SINGLE_TX_BLOCK_ROOT_ROLLUP
This error comes from a mismatch between the previous_rollup_data[0].base_or_merge_rollup_public_inputs.constants.last_l1_to_l2 and data.previous_block_header.state.l1_to_l2_message_tree from the SingleTxBlockRootRollupInputs (if I managed to follow the data flow correctly).
|
// Validate last_l1_to_l2. |
|
assert_eq( |
|
constants.last_l1_to_l2, |
|
previous_block_header.state.l1_to_l2_message_tree, |
|
"last_l1_to_l2 in constants does not match the value in the previous block header", |
|
); |
Now, the constants.last_l1_to_l2 in the public base rollup gets populated from the AVM start_tree_snapshots in the public base rollup:
|
last_l1_to_l2: avm_data.start_tree_snapshots.l1_to_l2_message_tree, |
So, if I understand correctly, this means that circuits are expecting that the L1 to L2 message tree does not change between the end of the previous block and the start of AVM execution, which would contradict what the sequencer is doing by inserting messages before tx execution starts.
So my question is: what is the expected behaviour? Should we be injecting the L1 to L2 messages for a given block into their tree before we start tx public execution, or at the end?
We have a lingering issue with L1 to L2 messages: if a tx consumes an L1 to L2 message on the same block it is seen by the archiver, then that block is not provable. This is because of a mismatch between how the sequencer and the prover node work.
The sequencer uses the
block_builder, which when starting a new block, first inserts all L1 to L2 messages into the tree to make them available, so when txs are processed by thepublic_processor, they can access the messages.aztec-packages/yarn-project/sequencer-client/src/sequencer/block_builder.ts
Lines 65 to 73 in 45e1fad
However, the prover node doesn't. It forks world-state immediately before the block it wants to prove, and then calls the public_processor without first adding the L1 to L2 messages.
aztec-packages/yarn-project/prover-node/src/job/epoch-proving-job.ts
Lines 153 to 158 in 45e1fad
So, in #16375 we changed it so that we do add the L1 to L2 messages before we start processing. But then we started getting this error:
This error comes from a mismatch between the
previous_rollup_data[0].base_or_merge_rollup_public_inputs.constants.last_l1_to_l2anddata.previous_block_header.state.l1_to_l2_message_treefrom theSingleTxBlockRootRollupInputs(if I managed to follow the data flow correctly).aztec-packages/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/block_root/components/block_root_rollup_inputs_validator.nr
Lines 111 to 116 in 45e1fad
Now, the
constants.last_l1_to_l2in the public base rollup gets populated from the AVMstart_tree_snapshotsin the public base rollup:aztec-packages/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr
Line 92 in 45e1fad
So, if I understand correctly, this means that circuits are expecting that the L1 to L2 message tree does not change between the end of the previous block and the start of AVM execution, which would contradict what the sequencer is doing by inserting messages before tx execution starts.
So my question is: what is the expected behaviour? Should we be injecting the L1 to L2 messages for a given block into their tree before we start tx public execution, or at the end?