diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-block-root-single-tx/Prover.toml b/noir-projects/noir-protocol-circuits/crates/rollup-block-root-single-tx/Prover.toml index ac726d40c170..6ff6bca8bbe4 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-block-root-single-tx/Prover.toml +++ b/noir-projects/noir-protocol-circuits/crates/rollup-block-root-single-tx/Prover.toml @@ -39,7 +39,7 @@ l1_to_l2_message_frontier_hint = [ ] new_archive_sibling_path = [ "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x2fdbd1b87ca82784e3c3d2871efa66b2a61337fcb13897a786c67fbafb377149", + "0x2f559d93de6d3e8a6289d45995351f749463bcee0ee677456f8ee19347c27114", "0x14e4b977b2203b70e6ee1c2456eb7114d090fe4b907f631eecd0919fed432e7d", "0x30105bad22ddcc508b739b7c9ad87a561c569ff5cb0098a853c1c4ac21b7a037", "0x1e20ad4181460cbfdc74ca773502c59b890f184efe300ebad895956d318422da", @@ -561,12 +561,12 @@ new_archive_sibling_path = [ accumulated_mana_used = "0x0000000000000000000000000000000000000000000000000000000000000000" [inputs.previous_rollup.public_inputs.constants] - vk_tree_root = "0x0a178ec6fe216bfa6e2d25089ce97f9ff6b5c5701d2bce2a1fdf11e6dc351e3c" + vk_tree_root = "0x1d51e7bb66dda198c072b182a8e4809fc6614d677c914af2afa9c5376f16f06d" protocol_contracts_hash = "0x0727efc9473643b7abbe3c57df72d68e86b244b99cae71b17553c0f937ea433b" prover_id = "0x0000000000000000000000000000000000000000000000000000000000000000" [inputs.previous_rollup.public_inputs.constants.last_archive] - root = "0x2d84822e545f3f6e16012e854f9327cee518281ec5f6925ae461d64328e692ee" + root = "0x0c2eb70ed9c405d7895991207fcf58411017ab8481fba43c23b8d01f9ee6d826" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000002" [inputs.previous_rollup.public_inputs.constants.l1_to_l2_tree_snapshot] @@ -624,10 +624,10 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x18d6aae3ab4a271abd590cb98267825b70de1e9e5c339356e94ea5fc7feba64b" ] state = [ - "0x2f7854ba50d6143cd7c2d0aab9074594e7443021752b672f532fb28036bdf464", - "0x2d6155d605fc85dbd2c44a9dc2d61848c1a1abe0536b44e4482c9ac6024a77d5", - "0x236c352e3ae68c93a62504965bc9e8778e07c0cd338fe798a2c88b9fdb4ceae7", - "0x032fc1b246541ca6be41be6375543d448d6b21e06b2cb16d26c42bc97deafdfc" + "0x1e20e5a8cee556f890f804b4c34dfa9d0c0691184ef311d07a3360c7a3ed0abb", + "0x2f58f461552b8e4a5eb0e2390a113ced8ed4fbe200eae42042548cc471aa8302", + "0x0f289d4b3c928a76f3d6b02eb37d55a0bc9f5efe83f048e9bb6087c97ecd2629", + "0x030aab4986f4fd9cf6184c20046212db4094a9884e02d5fd5cb39b8c3fa73119" ] cache_size = "0x0000000000000000000000000000000000000000000000000000000000000003" squeeze_mode = false @@ -642,10 +642,10 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x00000000000000000000000000000000000000000000000000000000b7e5c34c" ] state = [ - "0x2dc74882aa099ff2d05d3ee7a9f8bfdddbbdedba13a6a544b7a97b6f834bd1d6", - "0x2002908e197ef2363618c1ada3e9533d21bc4f30c720c5d5957a90ddbbf42748", - "0x1bc50bd10705cd3e35a0ddf12d2d20342247e2d9f66bd2ef3f8c87afab432a9f", - "0x099fa9fa9487c303774e42ca2c7897603abf9ca25584364229ef9593af312d96" + "0x03a9c278ef420de34c98d3ed9353652c050aa787564e29869ba130c93aeeb58c", + "0x04840897fb96b980247599b309da2659be7d79a529b8e120a3de7d0ce81a18c4", + "0x261c3adfec1a9c39f94e54687fc959511aa357d07907d7eb60b6c1ede39dc9f8", + "0x22aeae6b14550f3d8954f9f9098c4255a25eabe1d0e5e6c662a8917c3d1a0e00" ] cache_size = "0x0000000000000000000000000000000000000000000000000000000000000002" squeeze_mode = false @@ -655,7 +655,7 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 sibling_path = [ "0x10b6730f1d1e9c6bf8d7c4b42b64b40d2603e3ae6ddbd464c3d8fcfb9e06e6d4", "0x014ffec160e37b6cb713c1a4e6e7058964dde1733e6734520ed72f72fd262919", - "0x196cbe2980734bc5d21b53464a1d00c06dad0febdec75822d79d6933dff40579", + "0x14bb1983f28c88ab603e0fe7e46b743829816df364ec385e2245072c3dad3e1e", "0x0787c8cc4cfb80390c27cdc17cb24ae198faad7989508690070b3cf40a2ae4fd", "0x134e9973b03d62389ee6021d35e61158807c7da3c3e6a052d365f53ab1ac214d", "0x118d25fdd2c4cc96d5af69bd85930dd49d101d463e3f5ee9f2cc9236384b5d41", @@ -1812,6 +1812,10 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 num_msgs = "0x0000000000000000000000000000000000000000000000000000000000000000" num_real_msgs = "0x0000000000000000000000000000000000000000000000000000000000000000" + [inputs.previous_l1_to_l2] + root = "0x18d6aae3ab4a271abd590cb98267825b70de1e9e5c339356e94ea5fc7feba64b" + next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000400" + [inputs.start_msg_sponge] num_absorbed = "0x0000000000000000000000000000000000000000000000000000000000000400" diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-block-root/Prover.toml b/noir-projects/noir-protocol-circuits/crates/rollup-block-root/Prover.toml index fbf715d2ec31..4d66dba58d6a 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-block-root/Prover.toml +++ b/noir-projects/noir-protocol-circuits/crates/rollup-block-root/Prover.toml @@ -39,7 +39,7 @@ l1_to_l2_message_frontier_hint = [ ] new_archive_sibling_path = [ "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x111f90dfe6a42b72a65046b7a1203d1249f522b498f11feda2da45ec757fed25", + "0x284d753011a1544ea66ccc1024bcc2aef83468522f0151d20454f31a06424e77", "0x14e4b977b2203b70e6ee1c2456eb7114d090fe4b907f631eecd0919fed432e7d", "0x30105bad22ddcc508b739b7c9ad87a561c569ff5cb0098a853c1c4ac21b7a037", "0x1e20ad4181460cbfdc74ca773502c59b890f184efe300ebad895956d318422da", @@ -561,12 +561,12 @@ new_archive_sibling_path = [ accumulated_mana_used = "0x000000000000000000000000000000000000000000000000000000000006b6c0" [inputs.previous_rollups.public_inputs.constants] - vk_tree_root = "0x0a178ec6fe216bfa6e2d25089ce97f9ff6b5c5701d2bce2a1fdf11e6dc351e3c" + vk_tree_root = "0x1d51e7bb66dda198c072b182a8e4809fc6614d677c914af2afa9c5376f16f06d" protocol_contracts_hash = "0x0727efc9473643b7abbe3c57df72d68e86b244b99cae71b17553c0f937ea433b" prover_id = "0x0000000000000000000000000000000000000000000000000000000000000000" [inputs.previous_rollups.public_inputs.constants.last_archive] - root = "0x09eab2d41c2ecafb0668fbada15342c670b7102c777e8fe01688205370be35dd" + root = "0x22fb435f1b17b06f994e96f8b9092935bb52d7ff089f9da911deb76fb11eafd0" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000002" [inputs.previous_rollups.public_inputs.constants.l1_to_l2_tree_snapshot] @@ -624,10 +624,10 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x1e1c597744057b88e39a9780ed087c39b1fc42864e05ef03a59ebd9e96b70b00" ] state = [ - "0x1340f2f29cb8306ff70bcc4ae858857833524119183778ecc6ad42461cffd2ec", - "0x0ded042d3f2742a39c12f49fea7578be6ca12907c913496a96aac8a4beab7da7", - "0x07fce0817fe38521b58a9910cc882430a755bfe4141eb684f9e13bb55ea08d59", - "0x2c426510a61e082128e8b428a5af495ed7351f768d421f3af48442dde4997151" + "0x2a5cf7199bc1a5f8871b4fea5c6f99f5dba43db93b819ca26d2fa8dbbdc1f82c", + "0x011cf649f43e718babd7d68caa9d36ccebf49e536e764b6674fc8ca18e8a4ada", + "0x17d68b99d3347b9908ca791c2a098777db49c111ac24b850a48e36fd0329b4e4", + "0x038347b13467b09e427b9f4154c2d36b1cd02bd1b08d8ea406687512f0b16a79" ] cache_size = "0x0000000000000000000000000000000000000000000000000000000000000002" squeeze_mode = false @@ -642,10 +642,10 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x00000000000000000000000000000000000000000000000000000000b7f9d44d" ] state = [ - "0x1f28dcf22870bc323df6d045183ab4e620f63c728f91adb34989c7f0fa15014e", - "0x11f9c62ca437d779c42395acdfa9aa365198c2c31c659401d9f436e898895308", - "0x22b47289bb74cee917c189d8d045521b296102ee2cd45d4a78df74c2d5fa0f7b", - "0x234bafa32c222e8d0eb62eaede11e3bfa20b4287f68b880c744abe51ac88ab6d" + "0x2d2cd78abcdb723be782846e1ad64ae3ab9aa2260689da6ae7475677befc11c2", + "0x0250e73c2089d502af086b4d31fc90a61b62f71901e93588a19421c1e54417db", + "0x1225c74c7caef02691c6f3a18a18ed4ce10675ec8286339594bef2cee3bcdf01", + "0x0b9145803dfcf3b0958502feb867fdbb62eb5726361b0971f704ffe19719c0af" ] cache_size = "0x0000000000000000000000000000000000000000000000000000000000000001" squeeze_mode = false @@ -656,7 +656,7 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x1fc39d0a428c8536dbca551ea79848acf67d89d090fd8c643c7d90f2e8f32340", "0x12ce5a49a1ceca53ada7bee003f929bbd65abaa74e8072a81f304c2c96c44e31", "0x2dd71474f7775d87b6c2986ace5f654686583f0970d7400b1ccf8096dad131b5", - "0x0aca02f69b05a42958d30ced7da19a9e135e0c83b75e72ae5f7e2bec714a418f", + "0x1bd9ea476112f37bb5ce44ce3c3f0d2e62e20993253269166a016eda81ac1007", "0x134e9973b03d62389ee6021d35e61158807c7da3c3e6a052d365f53ab1ac214d", "0x118d25fdd2c4cc96d5af69bd85930dd49d101d463e3f5ee9f2cc9236384b5d41", "0x19dc50e83dfaeddfc1eb7b19cfcf39ef4b5608eecfaf54180697ea982b7bebbd" @@ -1273,12 +1273,12 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 accumulated_mana_used = "0x0000000000000000000000000000000000000000000000000000000000000000" [inputs.previous_rollups.public_inputs.constants] - vk_tree_root = "0x0a178ec6fe216bfa6e2d25089ce97f9ff6b5c5701d2bce2a1fdf11e6dc351e3c" + vk_tree_root = "0x1d51e7bb66dda198c072b182a8e4809fc6614d677c914af2afa9c5376f16f06d" protocol_contracts_hash = "0x0727efc9473643b7abbe3c57df72d68e86b244b99cae71b17553c0f937ea433b" prover_id = "0x0000000000000000000000000000000000000000000000000000000000000000" [inputs.previous_rollups.public_inputs.constants.last_archive] - root = "0x09eab2d41c2ecafb0668fbada15342c670b7102c777e8fe01688205370be35dd" + root = "0x22fb435f1b17b06f994e96f8b9092935bb52d7ff089f9da911deb76fb11eafd0" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000002" [inputs.previous_rollups.public_inputs.constants.l1_to_l2_tree_snapshot] @@ -1336,10 +1336,10 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x00000000000000000000000000000000000000000000000000000000b7f9d44d" ] state = [ - "0x1f28dcf22870bc323df6d045183ab4e620f63c728f91adb34989c7f0fa15014e", - "0x11f9c62ca437d779c42395acdfa9aa365198c2c31c659401d9f436e898895308", - "0x22b47289bb74cee917c189d8d045521b296102ee2cd45d4a78df74c2d5fa0f7b", - "0x234bafa32c222e8d0eb62eaede11e3bfa20b4287f68b880c744abe51ac88ab6d" + "0x2d2cd78abcdb723be782846e1ad64ae3ab9aa2260689da6ae7475677befc11c2", + "0x0250e73c2089d502af086b4d31fc90a61b62f71901e93588a19421c1e54417db", + "0x1225c74c7caef02691c6f3a18a18ed4ce10675ec8286339594bef2cee3bcdf01", + "0x0b9145803dfcf3b0958502feb867fdbb62eb5726361b0971f704ffe19719c0af" ] cache_size = "0x0000000000000000000000000000000000000000000000000000000000000001" squeeze_mode = false @@ -1354,10 +1354,10 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x00000000000000000000000000000000000000000000000000000000b80de34e" ] state = [ - "0x134946855417bcc442f889686f1d56a235ef003975c044d0247a83233b410758", - "0x1b72a1748313dbe8d809b3fcbafc90c42245820ec62b0dad170882743ff5229f", - "0x0c028e173991130ade8d45c7ca581aba2786fafd52c6204542bedbd83554d15e", - "0x1d53b7e7cf226e419d853dc8cfc46ecd424838a883d07356d2a1fa811e38c34e" + "0x081ffd41f516611101fabed22dbbba6e6bf5ac0eff43aa020fb8407fe66471be", + "0x07e1027927852cc3291b4d5b979823dfacab903c13b5bd3f2590388210b608ed", + "0x17c059bfe7dca4a328bf27482e1c7d33887c0ff69b69f2be89eb754266e263ed", + "0x1e85bde3c04a4baa1d2d6e1237d10ced927e777bbd7fa7c31fce28cdd79b92ca" ] cache_size = "0x0000000000000000000000000000000000000000000000000000000000000003" squeeze_mode = false @@ -1367,7 +1367,7 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 sibling_path = [ "0x10b6730f1d1e9c6bf8d7c4b42b64b40d2603e3ae6ddbd464c3d8fcfb9e06e6d4", "0x014ffec160e37b6cb713c1a4e6e7058964dde1733e6734520ed72f72fd262919", - "0x196cbe2980734bc5d21b53464a1d00c06dad0febdec75822d79d6933dff40579", + "0x14bb1983f28c88ab603e0fe7e46b743829816df364ec385e2245072c3dad3e1e", "0x0787c8cc4cfb80390c27cdc17cb24ae198faad7989508690070b3cf40a2ae4fd", "0x134e9973b03d62389ee6021d35e61158807c7da3c3e6a052d365f53ab1ac214d", "0x118d25fdd2c4cc96d5af69bd85930dd49d101d463e3f5ee9f2cc9236384b5d41", @@ -2524,6 +2524,10 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 num_msgs = "0x0000000000000000000000000000000000000000000000000000000000000000" num_real_msgs = "0x0000000000000000000000000000000000000000000000000000000000000000" + [inputs.previous_l1_to_l2] + root = "0x18d6aae3ab4a271abd590cb98267825b70de1e9e5c339356e94ea5fc7feba64b" + next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000400" + [inputs.start_msg_sponge] num_absorbed = "0x0000000000000000000000000000000000000000000000000000000000000400" diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/block_root/block_root_rollup.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/block_root/block_root_rollup.nr index f396ff931b05..b0a3119e00a1 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/block_root/block_root_rollup.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/block_root/block_root_rollup.nr @@ -1,8 +1,12 @@ use crate::{ abis::{BlockRollupPublicInputs, L1ToL2MessageBundle, L1ToL2MessageSponge, TxRollupPublicInputs}, - block_root::components::{BlockRollupPublicInputsComposer, validate_previous_rollups}, + block_root::components::{ + BlockRollupPublicInputsComposer, validate_l1_to_l2_tree_snapshot_in_constants, + validate_previous_rollups, + }, }; use types::{ + abis::append_only_tree_snapshot::AppendOnlyTreeSnapshot, constants::{ ARCHIVE_HEIGHT, L1_TO_L2_MSG_TREE_HEIGHT, PRIVATE_TX_BASE_ROLLUP_VK_INDEX, PUBLIC_TX_BASE_ROLLUP_VK_INDEX, TX_MERGE_ROLLUP_VK_INDEX, @@ -17,11 +21,15 @@ pub struct BlockRootRollupPrivateInputs { pub(crate) previous_rollups: [RollupHonkProofData; 2], // L1-to-L2 message bundle inserted by this block. pub(crate) message_bundle: L1ToL2MessageBundle, + // The l1-to-l2 tree snapshot this block builds on (the previous block's post-insertion snapshot). It is a witnessed + // value, pinned by block-merge continuity (`right.start_state == left.end_state`) to the previous block's end + // state; since the checkpoint root forces the leftmost block to be a first-block variant, every non-first block + // has a left neighbour that pins it. + pub(crate) previous_l1_to_l2: AppendOnlyTreeSnapshot, // Message sponge inherited from the previous block. Checked against the previous block's `end_msg_sponge` in the // block merge or checkpoint root circuit. pub(crate) start_msg_sponge: L1ToL2MessageSponge, - // Frontier hint for appending the bundle to the l1-to-l2 message tree (validated against the tree snapshot in the - // constants, which is where the l1-to-l2 tree stands for this non-first block). + // Frontier hint for appending the bundle to the l1-to-l2 message tree (validated against `previous_l1_to_l2`). pub(crate) l1_to_l2_message_frontier_hint: [Field; L1_TO_L2_MSG_TREE_HEIGHT], // Hint for inserting the new block hash to the last archive. pub(crate) new_archive_sibling_path: [Field; ARCHIVE_HEIGHT], @@ -43,18 +51,29 @@ pub struct BlockRootRollupPrivateInputs { /// /// VkIndex: BLOCK_ROOT_ROLLUP_VK_INDEX pub fn execute(inputs: BlockRootRollupPrivateInputs) -> BlockRollupPublicInputs { - validate_previous_rollups(inputs.previous_rollups, ALLOWED_PREVIOUS_VK_INDICES); - let previous_rollups = inputs.previous_rollups.map(|rollup| rollup.public_inputs); - // Non-first blocks build on the l1-to-l2 tree snapshot carried in the checkpoint constants. - let previous_l1_to_l2 = previous_rollups[0].constants.l1_to_l2_tree_snapshot; - BlockRollupPublicInputsComposer::new_from_two_rollups(previous_rollups) + // Non-first blocks build on a witnessed start snapshot (pinned by block-merge continuity to the previous block's + // end state) rather than the constants value, so this block can append its own bundle and still assert the tx + // constants carry the post-bundle snapshot. + let mut composer = BlockRollupPublicInputsComposer::new_from_two_rollups(previous_rollups); + let new_l1_to_l2 = composer .with_message_bundle( false, - previous_l1_to_l2, + inputs.previous_l1_to_l2, inputs.start_msg_sponge, inputs.message_bundle, inputs.l1_to_l2_message_frontier_hint, ) - .finish(inputs.new_archive_sibling_path) + .get_new_l1_to_l2(); + + validate_previous_rollups(inputs.previous_rollups, ALLOWED_PREVIOUS_VK_INDICES); + + // The tx constants pin the l1-to-l2 snapshot the AVM validates message reads against; asserting it equals this + // block's post-bundle root lets this block's txs read the messages this block inserts (same-block consumption). + validate_l1_to_l2_tree_snapshot_in_constants( + inputs.previous_rollups[0].public_inputs.constants, + new_l1_to_l2, + ); + + composer.finish(inputs.new_archive_sibling_path) } diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/block_root/block_root_single_tx_rollup.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/block_root/block_root_single_tx_rollup.nr index d76c91d397bf..3c3cacbdaea8 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/block_root/block_root_single_tx_rollup.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/block_root/block_root_single_tx_rollup.nr @@ -1,8 +1,12 @@ use crate::{ abis::{BlockRollupPublicInputs, L1ToL2MessageBundle, L1ToL2MessageSponge, TxRollupPublicInputs}, - block_root::components::{BlockRollupPublicInputsComposer, validate_previous_rollups}, + block_root::components::{ + BlockRollupPublicInputsComposer, validate_l1_to_l2_tree_snapshot_in_constants, + validate_previous_rollups, + }, }; use types::{ + abis::append_only_tree_snapshot::AppendOnlyTreeSnapshot, constants::{ ARCHIVE_HEIGHT, L1_TO_L2_MSG_TREE_HEIGHT, PRIVATE_TX_BASE_ROLLUP_VK_INDEX, PUBLIC_TX_BASE_ROLLUP_VK_INDEX, @@ -18,11 +22,15 @@ pub struct BlockRootSingleTxRollupPrivateInputs { pub(crate) previous_rollup: RollupHonkProofData, // L1-to-L2 message bundle inserted by this block. pub(crate) message_bundle: L1ToL2MessageBundle, + // The l1-to-l2 tree snapshot this block builds on (the previous block's post-insertion snapshot). It is a witnessed + // value, pinned by block-merge continuity (`right.start_state == left.end_state`) to the previous block's end + // state; since the checkpoint root forces the leftmost block to be a first-block variant, every non-first block + // has a left neighbour that pins it. + pub(crate) previous_l1_to_l2: AppendOnlyTreeSnapshot, // Message sponge inherited from the previous block. Checked against the previous block's `end_msg_sponge` in the // block merge or checkpoint root circuit. pub(crate) start_msg_sponge: L1ToL2MessageSponge, - // Frontier hint for appending the bundle to the l1-to-l2 message tree (validated against the tree snapshot in the - // constants, which is where the l1-to-l2 tree stands for this non-first block). + // Frontier hint for appending the bundle to the l1-to-l2 message tree (validated against `previous_l1_to_l2`). pub(crate) l1_to_l2_message_frontier_hint: [Field; L1_TO_L2_MSG_TREE_HEIGHT], // Hint for inserting the new block hash to the last archive. pub(crate) new_archive_sibling_path: [Field; ARCHIVE_HEIGHT], @@ -43,17 +51,30 @@ pub struct BlockRootSingleTxRollupPrivateInputs { /// /// VkIndex: BLOCK_ROOT_SINGLE_TX_ROLLUP_VK_INDEX pub fn execute(inputs: BlockRootSingleTxRollupPrivateInputs) -> BlockRollupPublicInputs { - validate_previous_rollups([inputs.previous_rollup], ALLOWED_PREVIOUS_VK_INDICES); - - // Non-first blocks build on the l1-to-l2 tree snapshot carried in the checkpoint constants. - let previous_l1_to_l2 = inputs.previous_rollup.public_inputs.constants.l1_to_l2_tree_snapshot; - BlockRollupPublicInputsComposer::new_from_single_rollup(inputs.previous_rollup.public_inputs) + // Non-first blocks build on a witnessed start snapshot (pinned by block-merge continuity to the previous block's + // end state) rather than the constants value, so this block can append its own bundle and still assert the tx + // constants carry the post-bundle snapshot. + let mut composer = BlockRollupPublicInputsComposer::new_from_single_rollup( + inputs.previous_rollup.public_inputs, + ); + let new_l1_to_l2 = composer .with_message_bundle( false, - previous_l1_to_l2, + inputs.previous_l1_to_l2, inputs.start_msg_sponge, inputs.message_bundle, inputs.l1_to_l2_message_frontier_hint, ) - .finish(inputs.new_archive_sibling_path) + .get_new_l1_to_l2(); + + validate_previous_rollups([inputs.previous_rollup], ALLOWED_PREVIOUS_VK_INDICES); + + // The tx constants pin the l1-to-l2 snapshot the AVM validates message reads against; asserting it equals this + // block's post-bundle root lets this block's txs read the messages this block inserts (same-block consumption). + validate_l1_to_l2_tree_snapshot_in_constants( + inputs.previous_rollup.public_inputs.constants, + new_l1_to_l2, + ); + + composer.finish(inputs.new_archive_sibling_path) } diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/block_root/components/block_rollup_public_inputs_composer.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/block_root/components/block_rollup_public_inputs_composer.nr index 42ee59e74eb1..e4d6d7fb48c8 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/block_root/components/block_rollup_public_inputs_composer.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/block_root/components/block_rollup_public_inputs_composer.nr @@ -133,9 +133,10 @@ impl BlockRollupPublicInputsComposer { /// Appends this block's L1-to-L2 message bundle to the tree and absorbs its real messages into the message sponge. /// /// `previous_l1_to_l2` is the tree snapshot this block builds on: for the first block it is the pre-insertion - /// snapshot (hinted, and later checked against the previous checkpoint), for subsequent blocks it is the snapshot - /// carried in the constants. `start_msg_sponge` is the sponge inherited from the previous block (empty for the - /// first block). + /// snapshot (hinted, and later checked against the previous checkpoint), for subsequent blocks it is a witnessed + /// snapshot pinned by block-merge continuity to the previous block's end state. Either way, the caller asserts the + /// tx constants carry the resulting post-bundle snapshot. `start_msg_sponge` is the sponge inherited from the + /// previous block (empty for the first block). /// /// The tree append and the sponge absorb use different counts transitionally (see `L1ToL2MessageBundle`): the tree /// inserts `bundle.num_msgs` leaves (a full padded subtree for a message-bearing block), while the sponge absorbs @@ -182,8 +183,8 @@ impl BlockRollupPublicInputsComposer { *self } - /// The l1-to-l2 tree snapshot after appending this block's bundle. First-block variants check this against the - /// snapshot in the tx constants (consumed by the AVM to read new messages). + /// The l1-to-l2 tree snapshot after appending this block's bundle. Tx-carrying variants (first and non-first) + /// check this against the snapshot in the tx constants (consumed by the AVM to read new messages). pub fn get_new_l1_to_l2(self) -> AppendOnlyTreeSnapshot { self.new_l1_to_l2 } diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/block_root/components/block_root_rollup_inputs_validator.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/block_root/components/block_root_rollup_inputs_validator.nr index c969052ab79f..35abb99345e0 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/block_root/components/block_root_rollup_inputs_validator.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/block_root/components/block_root_rollup_inputs_validator.nr @@ -50,11 +50,11 @@ pub fn validate_previous_rollups = { isProving?: boolean; }; +/** + * The block-root rollup flavor a block proves with and its private inputs, discriminated by circuit name so callers + * can dispatch to the matching prover entrypoint with the correctly-typed inputs. + */ +export type BlockRootRollupTypeAndInputs = + | { rollupType: 'rollup-block-root-first'; inputs: BlockRootFirstRollupPrivateInputs } + | { rollupType: 'rollup-block-root-first-single-tx'; inputs: BlockRootSingleTxFirstRollupPrivateInputs } + | { rollupType: 'rollup-block-root-first-empty-tx'; inputs: BlockRootEmptyTxFirstRollupPrivateInputs } + | { rollupType: 'rollup-block-root-single-tx'; inputs: BlockRootSingleTxRollupPrivateInputs } + | { rollupType: 'rollup-block-root'; inputs: BlockRootRollupPrivateInputs }; + /** * The current state of the proving schedule for a given block. Managed by ProvingState. * Contains the raw inputs and intermediate state to generate every constituent proof in the tree. @@ -280,7 +291,7 @@ export class BlockProvingState { return new TxMergeRollupPrivateInputs([toProofData(left), toProofData(right)]); } - public getBlockRootRollupTypeAndInputs() { + public getBlockRootRollupTypeAndInputs(): BlockRootRollupTypeAndInputs { const provingOutputs = this.#getChildProvingOutputsForBlockRoot(); if (!provingOutputs.every(p => !!p)) { throw new Error('At least one child is not ready for the block root rollup.'); @@ -303,6 +314,7 @@ export class BlockProvingState { inputs: new BlockRootSingleTxRollupPrivateInputs( leftRollup, messageBundle, + this.lastL1ToL2MessageTreeSnapshot, startMsgSponge, frontierHint, this.lastArchiveSiblingPath, @@ -314,6 +326,7 @@ export class BlockProvingState { inputs: new BlockRootRollupPrivateInputs( [leftRollup, rightRollup], messageBundle, + this.lastL1ToL2MessageTreeSnapshot, startMsgSponge, frontierHint, this.lastArchiveSiblingPath, @@ -322,7 +335,10 @@ export class BlockProvingState { } } - #getFirstBlockRootRollupTypeAndInputs([leftRollup, rightRollup]: RollupHonkProofData[]) { + #getFirstBlockRootRollupTypeAndInputs([ + leftRollup, + rightRollup, + ]: RollupHonkProofData[]): BlockRootRollupTypeAndInputs { const messageBundle = this.#getMessageBundle(); const frontierHint = this.#getFrontierHint(); diff --git a/yarn-project/prover-client/src/orchestrator/checkpoint-sub-tree-orchestrator.ts b/yarn-project/prover-client/src/orchestrator/checkpoint-sub-tree-orchestrator.ts index 0535ee3b609d..ad155053c2a9 100644 --- a/yarn-project/prover-client/src/orchestrator/checkpoint-sub-tree-orchestrator.ts +++ b/yarn-project/prover-client/src/orchestrator/checkpoint-sub-tree-orchestrator.ts @@ -28,10 +28,6 @@ import type { ParityPublicInputs } from '@aztec/stdlib/parity'; import { type BaseRollupHints, type BlockRollupPublicInputs, - BlockRootEmptyTxFirstRollupPrivateInputs, - BlockRootFirstRollupPrivateInputs, - BlockRootSingleTxFirstRollupPrivateInputs, - BlockRootSingleTxRollupPrivateInputs, CheckpointConstantData, PrivateTxBaseRollupPrivateInputs, type PublicChonkVerifierPublicInputs, @@ -754,7 +750,9 @@ export class CheckpointSubTreeOrchestrator extends ProvingScheduler { return; } - const { rollupType, inputs } = provingState.getBlockRootRollupTypeAndInputs(); + // Kept whole (not destructured) so the switch on the `rollupType` discriminant narrows `inputs` per case. + const rollup = provingState.getBlockRootRollupTypeAndInputs(); + const rollupType = rollup.rollupType; this.logger.debug(`Enqueuing ${rollupType} for block ${provingState.blockNumber}.`); @@ -763,16 +761,17 @@ export class CheckpointSubTreeOrchestrator extends ProvingScheduler { this.wrapCircuitCall( 'getBlockRootRollupProof', signal => { - if (inputs instanceof BlockRootFirstRollupPrivateInputs) { - return this.prover.getBlockRootFirstRollupProof(inputs, signal, provingState.epochNumber); - } else if (inputs instanceof BlockRootSingleTxFirstRollupPrivateInputs) { - return this.prover.getBlockRootSingleTxFirstRollupProof(inputs, signal, provingState.epochNumber); - } else if (inputs instanceof BlockRootEmptyTxFirstRollupPrivateInputs) { - return this.prover.getBlockRootEmptyTxFirstRollupProof(inputs, signal, provingState.epochNumber); - } else if (inputs instanceof BlockRootSingleTxRollupPrivateInputs) { - return this.prover.getBlockRootSingleTxRollupProof(inputs, signal, provingState.epochNumber); - } else { - return this.prover.getBlockRootRollupProof(inputs, signal, provingState.epochNumber); + switch (rollup.rollupType) { + case 'rollup-block-root-first': + return this.prover.getBlockRootFirstRollupProof(rollup.inputs, signal, provingState.epochNumber); + case 'rollup-block-root-first-single-tx': + return this.prover.getBlockRootSingleTxFirstRollupProof(rollup.inputs, signal, provingState.epochNumber); + case 'rollup-block-root-first-empty-tx': + return this.prover.getBlockRootEmptyTxFirstRollupProof(rollup.inputs, signal, provingState.epochNumber); + case 'rollup-block-root-single-tx': + return this.prover.getBlockRootSingleTxRollupProof(rollup.inputs, signal, provingState.epochNumber); + case 'rollup-block-root': + return this.prover.getBlockRootRollupProof(rollup.inputs, signal, provingState.epochNumber); } }, { [Attributes.PROTOCOL_CIRCUIT_NAME]: rollupType }, diff --git a/yarn-project/stdlib/src/rollup/block_constant_data.ts b/yarn-project/stdlib/src/rollup/block_constant_data.ts index 60979da2a73f..fd7e7f5c5f20 100644 --- a/yarn-project/stdlib/src/rollup/block_constant_data.ts +++ b/yarn-project/stdlib/src/rollup/block_constant_data.ts @@ -13,10 +13,10 @@ export class BlockConstantData { /** Archive tree snapshot at the very beginning of the entire rollup. */ public lastArchive: AppendOnlyTreeSnapshot, /** - * L1-to-L2 message tree snapshot after this block lands. - * For the first block in a checkpoint, this should be the snapshot after inserting the new l1-to-l2 message subtree - * into the last l1-to-l2 tree snapshot in `last_archive`. - * For subsequent blocks, this should match the snapshot of the previous block. + * L1-to-L2 message tree snapshot after this block's own message bundle has been inserted. The AVM validates this + * block's l1-to-l2 message read requests against it, so a tx in this block can read the messages this block + * inserts (same-block consumption). Every block-root variant that carries txs asserts this equals its computed + * post-bundle root, whether or not the block is the first in its checkpoint. */ public l1ToL2TreeSnapshot: AppendOnlyTreeSnapshot, /** Root of the verification key tree. */ diff --git a/yarn-project/stdlib/src/rollup/block_root_rollup_private_inputs.ts b/yarn-project/stdlib/src/rollup/block_root_rollup_private_inputs.ts index fc75394f4959..206572d4a7c6 100644 --- a/yarn-project/stdlib/src/rollup/block_root_rollup_private_inputs.ts +++ b/yarn-project/stdlib/src/rollup/block_root_rollup_private_inputs.ts @@ -340,12 +340,18 @@ export class BlockRootRollupPrivateInputs { * L1-to-L2 message bundle inserted by this block. */ public messageBundle: L1ToL2MessageBundle, + /** + * The l1 to l2 message tree snapshot this block builds on (the previous block's post-insertion snapshot). Pinned by + * block-merge continuity to the previous block's end state; the circuit appends this block's bundle on top and + * asserts the tx constants carry the resulting post-bundle snapshot. + */ + public previousL1ToL2: AppendOnlyTreeSnapshot, /** * Message sponge inherited from the previous block (checked against its `endMsgSponge` in the merge/checkpoint root). */ public startMsgSponge: L1ToL2MessageSponge, /** - * Frontier hint for appending the message bundle to the l1 to l2 tree snapshot carried in the constants. + * Frontier hint for appending the message bundle to `previousL1ToL2`. */ public l1ToL2MessageFrontierHint: Tuple, /** @@ -362,6 +368,7 @@ export class BlockRootRollupPrivateInputs { return [ fields.previousRollups, fields.messageBundle, + fields.previousL1ToL2, fields.startMsgSponge, fields.l1ToL2MessageFrontierHint, fields.newArchiveSiblingPath, @@ -372,6 +379,7 @@ export class BlockRootRollupPrivateInputs { return serializeToBuffer( this.previousRollups, this.messageBundle, + this.previousL1ToL2, this.startMsgSponge, this.l1ToL2MessageFrontierHint, this.newArchiveSiblingPath, @@ -383,6 +391,7 @@ export class BlockRootRollupPrivateInputs { return new BlockRootRollupPrivateInputs( [ProofData.fromBuffer(reader, TxRollupPublicInputs), ProofData.fromBuffer(reader, TxRollupPublicInputs)], reader.readObject(L1ToL2MessageBundle), + AppendOnlyTreeSnapshot.fromBuffer(reader), reader.readObject(L1ToL2MessageSponge), reader.readArray(L1_TO_L2_MSG_TREE_HEIGHT, Fr), reader.readArray(ARCHIVE_HEIGHT, Fr), @@ -408,12 +417,18 @@ export class BlockRootSingleTxRollupPrivateInputs { * L1-to-L2 message bundle inserted by this block. */ public messageBundle: L1ToL2MessageBundle, + /** + * The l1 to l2 message tree snapshot this block builds on (the previous block's post-insertion snapshot). Pinned by + * block-merge continuity to the previous block's end state; the circuit appends this block's bundle on top and + * asserts the tx constants carry the resulting post-bundle snapshot. + */ + public previousL1ToL2: AppendOnlyTreeSnapshot, /** * Message sponge inherited from the previous block (checked against its `endMsgSponge` in the merge/checkpoint root). */ public startMsgSponge: L1ToL2MessageSponge, /** - * Frontier hint for appending the message bundle to the l1 to l2 tree snapshot carried in the constants. + * Frontier hint for appending the message bundle to `previousL1ToL2`. */ public l1ToL2MessageFrontierHint: Tuple, /** @@ -430,6 +445,7 @@ export class BlockRootSingleTxRollupPrivateInputs { return [ fields.previousRollup, fields.messageBundle, + fields.previousL1ToL2, fields.startMsgSponge, fields.l1ToL2MessageFrontierHint, fields.newArchiveSiblingPath, @@ -440,6 +456,7 @@ export class BlockRootSingleTxRollupPrivateInputs { return serializeToBuffer( this.previousRollup, this.messageBundle, + this.previousL1ToL2, this.startMsgSponge, this.l1ToL2MessageFrontierHint, this.newArchiveSiblingPath, @@ -451,6 +468,7 @@ export class BlockRootSingleTxRollupPrivateInputs { return new BlockRootSingleTxRollupPrivateInputs( ProofData.fromBuffer(reader, TxRollupPublicInputs), reader.readObject(L1ToL2MessageBundle), + AppendOnlyTreeSnapshot.fromBuffer(reader), reader.readObject(L1ToL2MessageSponge), reader.readArray(L1_TO_L2_MSG_TREE_HEIGHT, Fr), reader.readArray(ARCHIVE_HEIGHT, Fr), diff --git a/yarn-project/stdlib/src/tests/factories.ts b/yarn-project/stdlib/src/tests/factories.ts index bf854379ad9c..58374d0ae2d5 100644 --- a/yarn-project/stdlib/src/tests/factories.ts +++ b/yarn-project/stdlib/src/tests/factories.ts @@ -981,6 +981,7 @@ export function makeBlockRootSingleTxRollupPrivateInputs(seed = 0) { return new BlockRootSingleTxRollupPrivateInputs( makeProofData(seed + 0x1000, makeTxRollupPublicInputs), new L1ToL2MessageBundle(makeArray(MAX_L1_TO_L2_MSGS_PER_BLOCK, fr, seed + 0x2500), 0, 0), + makeAppendOnlyTreeSnapshot(seed + 0x2800), makeL1ToL2MessageSponge(seed + 0x3000), makeSiblingPath(seed + 0x4000, L1_TO_L2_MSG_TREE_HEIGHT), makeSiblingPath(seed + 0x5000, ARCHIVE_HEIGHT),