diff --git a/l1-contracts/scripts/constants-codegen/solidity.json b/l1-contracts/scripts/constants-codegen/solidity.json index 2046ce24d44a..2fcd7cc212c4 100644 --- a/l1-contracts/scripts/constants-codegen/solidity.json +++ b/l1-contracts/scripts/constants-codegen/solidity.json @@ -8,7 +8,5 @@ "EMPTY_EPOCH_OUT_HASH", "FEE_JUICE_ADDRESS", "BLS12_POINT_COMPRESSED_BYTES", - "ROOT_ROLLUP_PUBLIC_INPUTS_LENGTH", - "NUM_MSGS_PER_BASE_PARITY", - "NUM_BASE_PARITY_PER_ROOT_PARITY" + "ROOT_ROLLUP_PUBLIC_INPUTS_LENGTH" ] diff --git a/l1-contracts/src/core/libraries/crypto/FrontierLib.sol b/l1-contracts/src/core/libraries/crypto/FrontierLib.sol deleted file mode 100644 index 6fd66130a15a..000000000000 --- a/l1-contracts/src/core/libraries/crypto/FrontierLib.sol +++ /dev/null @@ -1,91 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright 2024 Aztec Labs. -pragma solidity >=0.8.27; - -import {Hash} from "@aztec/core/libraries/crypto/Hash.sol"; - -/** - * @title FrontierLib - * @author Aztec Labs - * @notice Library for managing frontier trees. - */ -library FrontierLib { - struct Forest { - mapping(uint256 index => bytes32 zero) zeros; - } - - struct Tree { - uint256 nextIndex; - mapping(uint256 => bytes32) frontier; - } - - function initialize(Forest storage _self, uint256 _height) internal { - _self.zeros[0] = bytes32(0); - for (uint256 i = 1; i <= _height; i++) { - _self.zeros[i] = Hash.sha256ToField(bytes.concat(_self.zeros[i - 1], _self.zeros[i - 1])); - } - } - - function insertLeaf(Tree storage _self, bytes32 _leaf) internal returns (uint256) { - uint256 index = _self.nextIndex; - uint256 level = computeLevel(index); - bytes32 right = _leaf; - for (uint256 i = 0; i < level; i++) { - right = Hash.sha256ToField(bytes.concat(_self.frontier[i], right)); - } - _self.frontier[level] = right; - - _self.nextIndex++; - - return index; - } - - function root(Tree storage _self, Forest storage _forest, uint256 _height, uint256 _size) - internal - view - returns (bytes32) - { - uint256 next = _self.nextIndex; - if (next == 0) { - return _forest.zeros[_height]; - } - if (next == _size) { - return _self.frontier[_height]; - } - - uint256 index = next - 1; - uint256 level = computeLevel(index); - - // We should start at the highest frontier level with a left leaf - bytes32 temp = _self.frontier[level]; - - uint256 bits = index >> level; - for (uint256 i = level; i < _height; i++) { - bool isRight = bits & 1 == 1; - if (isRight) { - temp = Hash.sha256ToField(bytes.concat(_self.frontier[i], temp)); - } else { - temp = Hash.sha256ToField(bytes.concat(temp, _forest.zeros[i])); - } - bits >>= 1; - } - - return temp; - } - - function isFull(Tree storage _self, uint256 _size) internal view returns (bool) { - return _self.nextIndex == _size; - } - - function computeLevel(uint256 _leafIndex) internal pure returns (uint256) { - // The number of trailing ones is how many times in a row we are the right child. - // e.g., each time this happens we go another layer up to update the parent. - uint256 count = 0; - uint256 index = _leafIndex; - while (index & 1 == 1) { - count++; - index >>= 1; - } - return count; - } -} diff --git a/l1-contracts/src/core/libraries/rollup/ProposedHeaderLib.sol b/l1-contracts/src/core/libraries/rollup/ProposedHeaderLib.sol index 5a456dda77bb..374752f646f9 100644 --- a/l1-contracts/src/core/libraries/rollup/ProposedHeaderLib.sol +++ b/l1-contracts/src/core/libraries/rollup/ProposedHeaderLib.sol @@ -21,7 +21,6 @@ struct ProposedHeader { bytes32 lastArchiveRoot; bytes32 blockHeadersHash; bytes32 blobsHash; - bytes32 inHash; bytes32 inboxRollingHash; bytes32 outHash; Slot slotNumber; @@ -56,7 +55,6 @@ library ProposedHeaderLib { _header.lastArchiveRoot, _header.blockHeadersHash, _header.blobsHash, - _header.inHash, _header.inboxRollingHash, _header.outHash, _header.slotNumber, diff --git a/l1-contracts/test/Parity.t.sol b/l1-contracts/test/Parity.t.sol deleted file mode 100644 index 8ed33bf99e75..000000000000 --- a/l1-contracts/test/Parity.t.sol +++ /dev/null @@ -1,80 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright 2024 Aztec Labs. -pragma solidity >=0.8.27; - -import {Test} from "forge-std/Test.sol"; - -import {FrontierMerkle} from "./harnesses/Frontier.sol"; -import {Constants} from "@aztec/core/libraries/ConstantsGen.sol"; - -contract ParityTest is Test { - function setUp() public {} - - // Checks whether sha root matches output of base parity circuit - function testRootMatchesBaseParity() public { - uint248[256] memory msgs; - for (uint248 i = 0; i < msgs.length; i++) { - msgs[i] = i; - } - - // We can't use Constants.NUM_MSGS_PER_BASE_PARITY directly when defining the array so we do the check here to - // ensure it does not get outdated. - assertEq(msgs.length, Constants.NUM_MSGS_PER_BASE_PARITY, "NUM_MSGS_PER_BASE_PARITY changed, update msgs."); - - uint256 treeHeight = 8; // log_2(NUM_MSGS_PER_BASE_PARITY) - // We don't have log_2 directly accessible in solidity so I just do the following check here to ensure - // the hardcoded value is not outdated. - assertEq( - 2 ** treeHeight, - Constants.NUM_MSGS_PER_BASE_PARITY, - "Base parity circuit subtree height changed, update treeHeight." - ); - - FrontierMerkle frontier = new FrontierMerkle(treeHeight); - - for (uint256 i = 0; i < msgs.length; i++) { - frontier.insertLeaf(bytes32(bytes.concat(new bytes(1), bytes31(msgs[i])))); - } - // matches noir-protocol-circuits/crates/parity-lib/src/tests/parity_base_tests.nr - bytes32 expectedRoot = 0x00279d4d4dd5bcb9b1a4e742640588b917102f9f8bc97a6c95706ca4e7a8a76b; - assertEq(frontier.root(), expectedRoot, "Root does not match base parity circuit root"); - } - - // Checks whether sha root matches output of root parity circuit - function testRootMatchesRootParity() public { - // sha256 roots coming out of base parity circuits - // matches noir-protocol-circuits/crates/parity-lib/src/root/root_parity_inputs.nr - uint248[4] memory baseRoots = [ - 0xb3a3fc1968999f2c2d798b900bdf0de41311be2a4d20496a7e792a521fc8ab, - 0x43f78e0ebc9633ce336a8c086064d898c32fb5d7d6011f5427459c0b8d14e9, - 0x024259b6404280addcc9319bc5a32c9a5d56af5c93b2f941fa326064fbe963, - 0x53042d820859d80c474d4694e03778f8dc0ac88fc1c3a97b4369c1096e904a - ]; - - // We can't use Constants.NUM_BASE_PARITY_PER_ROOT_PARITY directly when defining the array so we do the check here - // to ensure it does not get outdated. - assertEq( - baseRoots.length, - Constants.NUM_BASE_PARITY_PER_ROOT_PARITY, - "NUM_BASE_PARITY_PER_ROOT_PARITY changed, update baseRoots." - ); - - uint256 treeHeight = 2; // log_2(NUM_BASE_PARITY_PER_ROOT_PARITY) - // We don't have log_2 directly accessible in solidity so I just do the following check here to ensure - // the hardcoded value is not outdated. - assertEq( - 2 ** treeHeight, - Constants.NUM_BASE_PARITY_PER_ROOT_PARITY, - "Root parity circuit subtree height changed, update treeHeight." - ); - - FrontierMerkle frontier = new FrontierMerkle(treeHeight); - - for (uint256 i = 0; i < baseRoots.length; i++) { - frontier.insertLeaf(bytes32(bytes.concat(new bytes(1), bytes31(baseRoots[i])))); - } - - bytes32 expectedRoot = 0x00a0c56543aa73140e5ca27231eee3107bd4e11d62164feb411d77c9d9b2da47; - assertEq(frontier.root(), expectedRoot, "Root does not match root parity circuit root"); - } -} diff --git a/l1-contracts/test/RollupFieldRange.t.sol b/l1-contracts/test/RollupFieldRange.t.sol index 9763efa0ae4e..a3d05d087ddd 100644 --- a/l1-contracts/test/RollupFieldRange.t.sol +++ b/l1-contracts/test/RollupFieldRange.t.sol @@ -164,8 +164,6 @@ contract RollupFieldRangeTest is RollupBase { vm.warp(max(block.timestamp, Timestamp.unwrap(ts))); _populateInbox(full.populate.sender, full.populate.recipient, full.populate.l1ToL2Content); - // The header's inHash field is an unconstrained pass-through hint post-flip; propose does not check it. - header.inHash = bytes32(0); header.gasFees.feePerL2Gas = SafeCast.toUint128(rollup.getManaMinFeeAt(ts, true)); // Every field the range check guards, set to the maximal in-range value. diff --git a/l1-contracts/test/base/DecoderBase.sol b/l1-contracts/test/base/DecoderBase.sol index 7abcb375c1aa..21f1431d7978 100644 --- a/l1-contracts/test/base/DecoderBase.sol +++ b/l1-contracts/test/base/DecoderBase.sol @@ -46,7 +46,6 @@ contract DecoderBase is TestBase { address coinbase; bytes32 feeRecipient; GasFees gasFees; - bytes32 inHash; bytes32 inboxRollingHash; bytes32 lastArchiveRoot; bytes32 outHash; @@ -104,7 +103,6 @@ contract DecoderBase is TestBase { lastArchiveRoot: full.checkpoint.header.lastArchiveRoot, blockHeadersHash: full.checkpoint.header.blockHeadersHash, blobsHash: full.checkpoint.header.blobsHash, - inHash: full.checkpoint.header.inHash, inboxRollingHash: full.checkpoint.header.inboxRollingHash, outHash: full.checkpoint.header.outHash, slotNumber: Slot.wrap(full.checkpoint.header.slotNumber), diff --git a/l1-contracts/test/base/RollupBase.sol b/l1-contracts/test/base/RollupBase.sol index f7080e4cc617..cdf6ee4014a2 100644 --- a/l1-contracts/test/base/RollupBase.sol +++ b/l1-contracts/test/base/RollupBase.sol @@ -171,8 +171,6 @@ contract RollupBase is DecoderBase { // We jump to the time of the block, always past the L1 block the messages above landed in. vm.warp(max(block.timestamp + 1, Timestamp.unwrap(full.checkpoint.header.timestamp))); - // The header's inHash field is an unconstrained pass-through hint post-flip; propose does not check it. - full.checkpoint.header.inHash = bytes32(0); // Streaming Inbox: reference the newest bucket so the checkpoint consumes all messages // seeded above and the mandatory-consumption assert is trivially satisfied (a wrong ref could only revert). uint256 bucketHint = rollup.getInbox().getCurrentBucketSeq(); diff --git a/l1-contracts/test/escape-hatch/integration/EscapeHatchIntegrationBase.sol b/l1-contracts/test/escape-hatch/integration/EscapeHatchIntegrationBase.sol index 5e6cd5fea89e..ea22ac757e75 100644 --- a/l1-contracts/test/escape-hatch/integration/EscapeHatchIntegrationBase.sol +++ b/l1-contracts/test/escape-hatch/integration/EscapeHatchIntegrationBase.sol @@ -130,18 +130,17 @@ abstract contract EscapeHatchIntegrationBase is ValidatorSelectionTestBase { * @dev Uses: * - archive: GENESIS_ARCHIVE_ROOT * - oracleInput: zero - * - header fields from fixture for blockHeadersHash/blobsHash/inHash/outHash, rest overridden + * - header fields from fixture for blockHeadersHash/blobsHash/outHash, rest overridden */ function _buildProposeArgs(address _proposer) internal view returns (ProposeArgs memory args, bytes memory blobs) { bytes32 archive = bytes32(Constants.GENESIS_ARCHIVE_ROOT); Slot slotNumber = rollup.getCurrentSlot(); - // Build header fresh, only copying blockHeadersHash/blobsHash/inHash/outHash from fixture + // Build header fresh, only copying blockHeadersHash/blobsHash/outHash from fixture ProposedHeader memory header = ProposedHeader({ lastArchiveRoot: archive, blockHeadersHash: full.checkpoint.header.blockHeadersHash, blobsHash: full.checkpoint.header.blobsHash, - inHash: full.checkpoint.header.inHash, inboxRollingHash: full.checkpoint.header.inboxRollingHash, outHash: full.checkpoint.header.outHash, slotNumber: slotNumber, diff --git a/l1-contracts/test/fixtures/empty_checkpoint_1.json b/l1-contracts/test/fixtures/empty_checkpoint_1.json index 1f829f1b819b..eb94728b3ccd 100644 --- a/l1-contracts/test/fixtures/empty_checkpoint_1.json +++ b/l1-contracts/test/fixtures/empty_checkpoint_1.json @@ -34,7 +34,6 @@ "lastArchiveRoot": "0x177a4955b31ecaafad999753938a44e526b54c5ba5d536688227f85f15cfbdf5", "blockHeadersHash": "0x2e3e0911389bc48fa8126a93273d016cc7dc08019f8ffc5f1f5ae7d90745eaa2", "blobsHash": "0x00e5b752fe6bc2154155ff3a979c4c5fa91d3ac0d716169ac521e1560fd83b2b", - "inHash": "0x00de7b349d2306334734e4f58b1302a6ed5a6c796a706f6597a5641b6d468223", "inboxRollingHash": "0x00cad6497cf81748167158f149ba70c31f34c68b0ae1b156117de63e073d14b5", "outHash": "0x00c95e0ceb41951039e1592745ec2faea9866f6eaf01bf189a4463b4143af093", "slotNumber": 99, @@ -48,7 +47,7 @@ "totalManaUsed": 0, "accumulatedFees": 0 }, - "headerHash": "0x0002fc455005d993521be4e6f8ed92d1e0b50e58b21445881c87d02ed51a1e4c", + "headerHash": "0x00230a553b85ec7931eded45ec1ea096e47789226758b16dca0403f5ab53f3d6", "numTxs": 0 } } diff --git a/l1-contracts/test/fixtures/empty_checkpoint_2.json b/l1-contracts/test/fixtures/empty_checkpoint_2.json index d2dcd961b92f..5ee1a899f2f8 100644 --- a/l1-contracts/test/fixtures/empty_checkpoint_2.json +++ b/l1-contracts/test/fixtures/empty_checkpoint_2.json @@ -34,7 +34,6 @@ "lastArchiveRoot": "0x09d45c1e01b8596153838a068ddb470ead95e6e1e151f40b4d48664c1e311af6", "blockHeadersHash": "0x0b3bda1754ca30707b8c0bbe72760c68e574cf23309e7e4fd7cabea36b4078da", "blobsHash": "0x000e9acabf609c9c113078ecb383ba6310573ce246958b605452132617d2c960", - "inHash": "0x006504de282a40084bb8098456a915c645d53482d351db52fa9433b6cd638763", "inboxRollingHash": "0x0080dbf2bd9576e81fd99fc0f9d35bfd7fc15a6221d0b6e1e92a1884f14d0c84", "outHash": "0x00c95e0ceb41951039e1592745ec2faea9866f6eaf01bf189a4463b4143af093", "slotNumber": 102, @@ -48,7 +47,7 @@ "totalManaUsed": 0, "accumulatedFees": 0 }, - "headerHash": "0x00120ecaf7a72f61adf3c8ee180acd930401221668a4792a8f91584755840d16", + "headerHash": "0x00a184a160ada9af4ba75961ba596043d76f56c75b2e7e9652a0c162a7bf59b9", "numTxs": 0 } } diff --git a/l1-contracts/test/fixtures/mixed_checkpoint_1.json b/l1-contracts/test/fixtures/mixed_checkpoint_1.json index b1604aceb038..fda840d182d1 100644 --- a/l1-contracts/test/fixtures/mixed_checkpoint_1.json +++ b/l1-contracts/test/fixtures/mixed_checkpoint_1.json @@ -67,7 +67,6 @@ "lastArchiveRoot": "0x177a4955b31ecaafad999753938a44e526b54c5ba5d536688227f85f15cfbdf5", "blockHeadersHash": "0x087b6f59388fa4207876ee1b50521ff838d6eed422d0ad07ff996393f32a77e6", "blobsHash": "0x001bef3ff3f657c565ff86d5072186f7f2bfddb8a31ca0714562c164fe954d84", - "inHash": "0x00de7b349d2306334734e4f58b1302a6ed5a6c796a706f6597a5641b6d468223", "inboxRollingHash": "0x00cad6497cf81748167158f149ba70c31f34c68b0ae1b156117de63e073d14b5", "outHash": "0x00cffdbb0e7f5e164d314d781d38ae31230910a3bae4c34e7df6222df71b1539", "slotNumber": 99, @@ -81,7 +80,7 @@ "totalManaUsed": 0, "accumulatedFees": 0 }, - "headerHash": "0x00c1431d117825d25f8169e35eb70ed4b16c0fc4b3cac85506852c240d03740e", + "headerHash": "0x00f1c11fddfca8ff6073d1715c8b886d501facad96b7b032fb440462849697b8", "numTxs": 4 } } diff --git a/l1-contracts/test/fixtures/mixed_checkpoint_2.json b/l1-contracts/test/fixtures/mixed_checkpoint_2.json index d2125d5786a4..2fdaa2de220d 100644 --- a/l1-contracts/test/fixtures/mixed_checkpoint_2.json +++ b/l1-contracts/test/fixtures/mixed_checkpoint_2.json @@ -67,7 +67,6 @@ "lastArchiveRoot": "0x0c87f76b6c5cc918111d2acc975bf7f2133f5c3cc23c8cf6cc1c3e78f5c84a3e", "blockHeadersHash": "0x144dbe32a03df9ccc672207c93cd22099eb91e44a71e1676148cd3c6c6c98b9e", "blobsHash": "0x008bd0b669b942b57ccf85d3401214db65cde3608afa0b2ae0e57f35ec60d72e", - "inHash": "0x006504de282a40084bb8098456a915c645d53482d351db52fa9433b6cd638763", "inboxRollingHash": "0x0080dbf2bd9576e81fd99fc0f9d35bfd7fc15a6221d0b6e1e92a1884f14d0c84", "outHash": "0x008a85da85a596471f2e5fe402fde332723da8d24b6e7affd60d16c7cb7e9020", "slotNumber": 102, @@ -81,7 +80,7 @@ "totalManaUsed": 0, "accumulatedFees": 0 }, - "headerHash": "0x00ab5faa05cb38faa2cd1fc62c40007f64594c9fdc3bb8b7b478a351a7c7dc5a", + "headerHash": "0x00e0c076d07c17d50e3aefe9ca264ba7a4894fab4956272bfbea600e9e34bb0f", "numTxs": 4 } } diff --git a/l1-contracts/test/fixtures/single_tx_checkpoint_1.json b/l1-contracts/test/fixtures/single_tx_checkpoint_1.json index 6d409f05d521..1969d1dfa7f7 100644 --- a/l1-contracts/test/fixtures/single_tx_checkpoint_1.json +++ b/l1-contracts/test/fixtures/single_tx_checkpoint_1.json @@ -43,7 +43,6 @@ "lastArchiveRoot": "0x177a4955b31ecaafad999753938a44e526b54c5ba5d536688227f85f15cfbdf5", "blockHeadersHash": "0x07db5c24565ad9a2c9d39ef7d9a4446e9742d6090567ff28aef9a45f4738a5cb", "blobsHash": "0x00ec2400a7cfc9d975cb0802980d49387588738160a0cf0301f07e1abad6456c", - "inHash": "0x00de7b349d2306334734e4f58b1302a6ed5a6c796a706f6597a5641b6d468223", "inboxRollingHash": "0x00cad6497cf81748167158f149ba70c31f34c68b0ae1b156117de63e073d14b5", "outHash": "0x007c92c6cf05665e1c02a305370a4d38bcb8b555261c6b39c862f8c067d6bcb7", "slotNumber": 99, @@ -57,7 +56,7 @@ "totalManaUsed": 0, "accumulatedFees": 0 }, - "headerHash": "0x0072a35395a79027db315321cbdaba5bc0b070979c3395b7369ec902ca48d9b2", + "headerHash": "0x0018784f81057f236b2da6a1e9bc20cb7c931343cbecb2035b605dc8ca5dd3cb", "numTxs": 1 } } diff --git a/l1-contracts/test/fixtures/single_tx_checkpoint_2.json b/l1-contracts/test/fixtures/single_tx_checkpoint_2.json index dc1244dcd73c..5487087aed88 100644 --- a/l1-contracts/test/fixtures/single_tx_checkpoint_2.json +++ b/l1-contracts/test/fixtures/single_tx_checkpoint_2.json @@ -43,7 +43,6 @@ "lastArchiveRoot": "0x0f198bb13b63a3a8791a8b9994167bbe1554811a106fda0acb5e6c6539b1713e", "blockHeadersHash": "0x12813725f2d16ce92088d2401ffa4a53ce6061bf75b77320ca7b8ef6c5145adf", "blobsHash": "0x002b8ae4c9f405529e2b689b829852ad52f77acdac57d1dbac3dabea1760affc", - "inHash": "0x006504de282a40084bb8098456a915c645d53482d351db52fa9433b6cd638763", "inboxRollingHash": "0x0080dbf2bd9576e81fd99fc0f9d35bfd7fc15a6221d0b6e1e92a1884f14d0c84", "outHash": "0x0007eac1d76cddf92b28b8f11cd292f199f35dfc588376092986575cef487f59", "slotNumber": 102, @@ -57,7 +56,7 @@ "totalManaUsed": 0, "accumulatedFees": 0 }, - "headerHash": "0x000b3c0bf27d3123a81b66570e4b63734cb06bbfd260a70fabbbfd41dec13255", + "headerHash": "0x00782cfd7fa9f4e407c734be2832e8afb818d2ef8a0a11d288a3df34bcae0047", "numTxs": 1 } } diff --git a/l1-contracts/test/harnesses/Frontier.sol b/l1-contracts/test/harnesses/Frontier.sol deleted file mode 100644 index 719e5692c8b5..000000000000 --- a/l1-contracts/test/harnesses/Frontier.sol +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright 2024 Aztec Labs. -pragma solidity >=0.8.27; - -import {FrontierLib} from "@aztec/core/libraries/crypto/FrontierLib.sol"; - -import {Ownable} from "@oz/access/Ownable.sol"; - -// This truncates each hash and hash preimage to 31 bytes to follow Noir. -// It follows the logic in /noir-protocol-circuits/crates/parity-lib/src/utils/sha256_merkle_tree.nr -// TODO(Miranda): Possibly nuke this contract, and use a generic version which can either use -// regular sha256 or sha256ToField when emulating circuits -contract FrontierMerkle is Ownable { - using FrontierLib for FrontierLib.Tree; - using FrontierLib for FrontierLib.Forest; - - uint256 public immutable HEIGHT; - uint256 public immutable SIZE; - - // Practically immutable value as we only set it in the constructor. - FrontierLib.Forest internal forest; - - FrontierLib.Tree internal tree; - - constructor(uint256 _height) Ownable(msg.sender) { - HEIGHT = _height; - SIZE = 2 ** _height; - forest.initialize(_height); - } - - function insertLeaf(bytes32 _leaf) external onlyOwner returns (uint256) { - return tree.insertLeaf(_leaf); - } - - function root() external view returns (bytes32) { - return tree.root(forest, HEIGHT, SIZE); - } - - function isFull() external view returns (bool) { - return tree.isFull(SIZE); - } -} diff --git a/l1-contracts/test/merkle/Frontier.t.sol b/l1-contracts/test/merkle/Frontier.t.sol deleted file mode 100644 index b6b8e3844a90..000000000000 --- a/l1-contracts/test/merkle/Frontier.t.sol +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright 2024 Aztec Labs. -pragma solidity >=0.8.27; - -import {Test} from "forge-std/Test.sol"; - -import {NaiveMerkle} from "./Naive.sol"; -import {FrontierMerkle} from "../harnesses/Frontier.sol"; - -contract FrontierTest is Test { - function setUp() public { - // Pause gas metering as calculating the root on each insert is expensive - vm.pauseGasMetering(); - } - - function testFrontier() public { - uint256 depth = 10; - - NaiveMerkle merkle = new NaiveMerkle(depth); - FrontierMerkle frontier = new FrontierMerkle(depth); - - uint256 upper = frontier.SIZE(); - for (uint256 i = 0; i < upper; i++) { - bytes32 leaf = sha256(abi.encode(i + 1)); - merkle.insertLeaf(leaf); - frontier.insertLeaf(leaf); - assertEq(merkle.computeRoot(), frontier.root(), "Frontier Roots should be equal"); - } - } -} diff --git a/l1-contracts/test/validator-selection/ValidatorSelection.t.sol b/l1-contracts/test/validator-selection/ValidatorSelection.t.sol index 80820932ec1b..a968be6d803f 100644 --- a/l1-contracts/test/validator-selection/ValidatorSelection.t.sol +++ b/l1-contracts/test/validator-selection/ValidatorSelection.t.sol @@ -546,8 +546,6 @@ contract ValidatorSelectionTest is ValidatorSelectionTestBase { { uint128 manaMinFee = SafeCast.toUint128(rollup.getManaMinFeeAt(Timestamp.wrap(block.timestamp), true)); - // The header's inHash field is an unconstrained pass-through hint post-flip; propose does not check it. - header.inHash = bytes32(0); header.gasFees.feePerL2Gas = manaMinFee; } diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/block_rollup_public_inputs.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/block_rollup_public_inputs.nr index d78a718b3269..e014d61946b2 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/block_rollup_public_inputs.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/block_rollup_public_inputs.nr @@ -40,11 +40,13 @@ pub struct BlockRollupPublicInputs { // stored in the checkpoint header, enabling validation of the blocks included in a checkpoint given their headers. pub block_headers_hash: Field, - // Whether this block range starts at the first block of its checkpoint. Only the first block root sets this true; - // merges propagate it from the left rollup and `validate_consecutive_block_rollups` asserts the right rollup's is - // false, so it can only reach the checkpoint root through the leftmost leaf. It replaces `in_hash`'s former - // structural role: the checkpoint root asserts the merged value is true, and it drives the block-end blob-absorb - // flag (the l1-to-l2 tree root is absorbed only for the first block). + // Whether this block range starts at the first block of its checkpoint. Only the first block root variants set it + // true; merges propagate it from the left rollup (`merge_block_rollups`), and + // `validate_consecutive_block_rollups` asserts the right rollup of every merge is not a first block. The flag is + // therefore a proof that the range's leftmost leaf is a first-variant block root, which is what lets the + // checkpoint root assert its leftmost block is one (`checkpoint_root_inputs_validator`) even though it cannot see + // the leaf VK indices. Since only first variants may be fully empty (no txs and no messages), that also confines + // a fully-empty filler block to the first position of a checkpoint. pub is_first_block: bool, // Poseidon2 message-bundle sponge threaded across the blocks of the checkpoint. The leftmost block starts from the diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/parity_public_inputs.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/parity_public_inputs.nr index a2260af3e24e..1914ae700516 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/parity_public_inputs.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/parity_public_inputs.nr @@ -3,12 +3,6 @@ use types::traits::{Deserialize, Empty, Serialize}; #[derive(Deserialize, Eq, Serialize)] pub struct ParityPublicInputs { - // The L1 `in_hash`: the sha256 frontier root of the checkpoint's L1-to-L2 messages, checked on L1 against - // `inbox.consume()`. It is an UNCONSTRAINED pass-through: `InboxParity` echoes the value the prover supplies and - // does not recompute it from the messages (the frontier tree is gone from the parity body). Until the Fast Inbox - // flip moves the L1 anchor to the rolling hash, `in_hash` remains the authoritative L1 check, so the prover must - // supply the true frontier root; the in-circuit tie between it and the processed messages is deferred to the flip. - pub in_hash: Field, // Rolling hash of the Inbox message chain before absorbing this checkpoint's messages. pub start_rolling_hash: Field, // Rolling hash of the Inbox message chain after absorbing the `num_msgs` real messages. Each link is @@ -29,7 +23,6 @@ pub struct ParityPublicInputs { impl Empty for ParityPublicInputs { fn empty() -> Self { ParityPublicInputs { - in_hash: 0, start_rolling_hash: 0, end_rolling_hash: 0, start_sponge: L1ToL2MessageSponge::empty(), diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/checkpoint_root/checkpoint_root_single_block_rollup.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/checkpoint_root/checkpoint_root_single_block_rollup.nr index 79a4ef5f2f1a..3265df9d7c37 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/checkpoint_root/checkpoint_root_single_block_rollup.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/checkpoint_root/checkpoint_root_single_block_rollup.nr @@ -21,8 +21,10 @@ use types::{ // // That `is_first_block` assertion is the only thing keeping a message-only block from standing as a checkpoint's sole // block. If it is ever relaxed (e.g. replaced by a "leftmost block's start_msg_sponge is empty" check), this entry -// must be dropped in the same change, or a transaction-less single-block checkpoint — one whose blob omits the -// first-block l1-to-l2 root — would silently become provable. +// must be dropped in the same change, because barring the message-only variant from standing alone is what keeps +// each checkpoint shape to a single valid encoding: a sole transaction-less block carrying a message bundle is +// already provable as an empty-tx-first block, which accepts a non-empty bundle. It is also what keeps the rule +// confining fully-empty blocks to the first position enforceable. global ALLOWED_PREVIOUS_VK_INDICES: [u32; 4] = [ BLOCK_ROOT_FIRST_ROLLUP_VK_INDEX, BLOCK_ROOT_SINGLE_TX_FIRST_ROLLUP_VK_INDEX, diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/checkpoint_root/components/checkpoint_rollup_public_inputs_composer.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/checkpoint_root/components/checkpoint_rollup_public_inputs_composer.nr index 56aa0e132933..b1f4bf8df971 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/checkpoint_root/components/checkpoint_rollup_public_inputs_composer.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/checkpoint_root/components/checkpoint_rollup_public_inputs_composer.nr @@ -21,8 +21,8 @@ use types::{ pub struct CheckpointRollupPublicInputsComposer { merged_rollup: BlockRollupPublicInputs, - // Public inputs of the inbox parity proof (verified by the caller). The checkpoint's `in_hash` and - // `inbox_rolling_hash` are sourced from it, and its message sponge is checked against the blocks' accumulated one. + // Public inputs of the inbox parity proof (verified by the caller). The checkpoint's `inbox_rolling_hash` is + // sourced from it, and its message sponge is checked against the blocks' accumulated one. parity: ParityPublicInputs, // The below are all hints: previous_out_hash: AppendOnlyTreeSnapshot, @@ -117,8 +117,8 @@ impl CheckpointRollupPublicInputsComposer { new_archive: merged_rollup.new_archive, previous_out_hash: self.previous_out_hash, new_out_hash, - // Sourced from the inbox parity proof, which commits to the same message list behind the L1-checked - // `in_hash`. Checkpoint merges assert continuity across checkpoints (`right.start == left.end`). + // Sourced from the inbox parity proof, which commits to the checkpoint's message list. Checkpoint merges + // assert continuity across checkpoints (`right.start == left.end`). start_inbox_rolling_hash: self.parity.start_rolling_hash, end_inbox_rolling_hash: self.parity.end_rolling_hash, checkpoint_header_hashes, @@ -145,10 +145,7 @@ impl CheckpointRollupPublicInputsComposer { last_archive_root: merged_rollup.previous_archive.root, block_headers_hash: merged_rollup.block_headers_hash, blobs_hash: self.blobs_hash, - // `in_hash` (the sha256 frontier root, checked on L1) and `inbox_rolling_hash` are sourced from the inbox - // parity proof. `in_hash` is an unconstrained pass-through there (see `ParityPublicInputs::in_hash`); it - // remains the authoritative L1 check until the Fast Inbox flip. - in_hash: self.parity.in_hash, + // Sourced from the inbox parity proof: the checkpoint's only inbox commitment (AZIP-22 Fast Inbox). inbox_rolling_hash: self.parity.end_rolling_hash, epoch_out_hash, slot_number: constants.slot_number, diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/checkpoint_root/tests/mod.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/checkpoint_root/tests/mod.nr index 38b92d5163e4..9dec29e65cba 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/checkpoint_root/tests/mod.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/checkpoint_root/tests/mod.nr @@ -154,8 +154,8 @@ impl TestBuilder { let start_block_number = fixture_builder.start_block_number; if !is_first_block(left_rollup_vk_index) & (left_rollup_vk_index != BLOCK_MERGE_ROLLUP_VK_INDEX) { - // Change the start_block_number to be smaller than the left rollup's start_block_number so that the in_hash - // won't be set on the left rollup. + // Change the start_block_number to be smaller than the left rollup's start_block_number so that the + // first-block designation is not on the left rollup. fixture_builder.start_block_number = start_block_number - 1; } let mut left_rollup = fixture_builder.get_merged_block_rollup_public_inputs( @@ -163,8 +163,8 @@ impl TestBuilder { start_block_number + num_left_blocks - 1, ); if is_first_block(right_rollup_vk_index) { - // Change the start_block_number to be the right rollup's start_block_number so that the in_hash will be set - // on the right rollup. + // Change the start_block_number to be the right rollup's start_block_number so that the first-block + // designation is on the right rollup. fixture_builder.start_block_number = start_block_number + num_left_blocks; } let mut right_rollup = fixture_builder.get_merged_block_rollup_public_inputs( @@ -192,8 +192,8 @@ impl TestBuilder { left_rollup.start_state = hints.previous_block_header.state; // The InboxParity proof commits to the same checkpoint messages the block roots absorbed. Its message sponge - // (empty to the checkpoint's accumulated value) matches the blocks' merged sponge; its rolling hash and in_hash - // feed the checkpoint header. Built at the base slot the block fixtures use for their sponge. + // (empty to the checkpoint's accumulated value) matches the blocks' merged sponge; its rolling hash feeds the + // checkpoint header. Built at the base slot the block fixtures use for their sponge. let inbox_parity = fixture_builder.get_parity_public_inputs(fixture_builder.start_slot_number); Self { @@ -445,7 +445,6 @@ impl TestBuilder { last_archive_root: left.previous_archive.root, block_headers_hash, blobs_hash: self.hints.blobs_hash, - in_hash: self.inbox_parity.in_hash, inbox_rolling_hash: self.inbox_parity.end_rolling_hash, epoch_out_hash, slot_number: left.constants.slot_number, diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/parity/inbox_parity.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/parity/inbox_parity.nr index 83f99510b696..c8784c827a6f 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/parity/inbox_parity.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/parity/inbox_parity.nr @@ -18,10 +18,6 @@ pub struct InboxParityPrivateInputs { pub(crate) start_rolling_hash: Field, // Message-bundle sponge before this checkpoint's messages (empty at checkpoint start). pub(crate) start_sponge: L1ToL2MessageSponge, - // The L1 `in_hash` (sha256 frontier root of this checkpoint's messages), passed through UNCONSTRAINED to the - // public inputs. See `ParityPublicInputs::in_hash`: the frontier tree is gone from this body, so the prover - // supplies the value and the circuit does not recompute or bind it to `msgs`. - pub(crate) in_hash: Field, pub(crate) vk_tree_root: Field, pub(crate) prover_id: Field, } @@ -35,7 +31,6 @@ pub struct InboxParityPrivateInputs { /// - Absorbs the same real messages into the message sponge (real-count absorb; the checkpoint root asserts this /// equals the sponge the block roots accumulate) /// - Asserts the padding lanes past `num_msgs` are zero so they cannot silently enter either accumulator -/// - Passes `in_hash` through unconstrained (see `InboxParityPrivateInputs::in_hash`) /// /// The output feeds the Checkpoint Root circuits, which verify this proof against the `{64, 256, 1024}` VK ladder. /// @@ -51,7 +46,6 @@ pub fn execute(inputs: InboxParityPrivateInputs) -> ParityPublicI end_sponge.absorb(inputs.msgs, inputs.num_msgs); ParityPublicInputs { - in_hash: inputs.in_hash, start_rolling_hash: inputs.start_rolling_hash, end_rolling_hash, start_sponge: inputs.start_sponge, diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/parity/tests/inbox_parity_tests.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/parity/tests/inbox_parity_tests.nr index 3ec816a2eb0d..89c20b998519 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/parity/tests/inbox_parity_tests.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/parity/tests/inbox_parity_tests.nr @@ -18,14 +18,11 @@ fn public_inputs_match_expected() { num_msgs: S, start_rolling_hash: 0, start_sponge: L1ToL2MessageSponge::empty(), - in_hash: 0x1234, vk_tree_root: 42, prover_id: 7, }; let public_inputs = inbox_parity::execute(private_inputs); - // `in_hash` is echoed through unconstrained. - assert_eq(public_inputs.in_hash, 0x1234); assert_eq(public_inputs.vk_tree_root, 42); assert_eq(public_inputs.prover_id, 7); @@ -57,7 +54,6 @@ fn rolling_hash_and_sponge_ignore_padding_and_thread_start() { num_msgs: 3, start_rolling_hash: start, start_sponge, - in_hash: 0, vk_tree_root: 42, prover_id: 7, }; @@ -88,7 +84,6 @@ fn sponge_matches_block_root_real_count_absorb() { num_msgs: 2, start_rolling_hash: 0, start_sponge: L1ToL2MessageSponge::empty(), - in_hash: 0, vk_tree_root: 42, prover_id: 7, }; @@ -113,7 +108,6 @@ fn non_zero_padding_lane_fails() { num_msgs: 2, start_rolling_hash: 0, start_sponge: L1ToL2MessageSponge::empty(), - in_hash: 0, vk_tree_root: 42, prover_id: 7, }; @@ -127,7 +121,6 @@ fn num_msgs_greater_than_size_fails() { num_msgs: S + 1, start_rolling_hash: 0, start_sponge: L1ToL2MessageSponge::empty(), - in_hash: 0, vk_tree_root: 42, prover_id: 7, }; diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/tests/rollup_fixture_builder.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/tests/rollup_fixture_builder.nr index 129c55ae4061..c96feaf9d36e 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/tests/rollup_fixture_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/tests/rollup_fixture_builder.nr @@ -48,7 +48,7 @@ pub struct RollupFixtureBuilder { pub protocol_contracts_hash: Field, pub prover_id: Field, - // Used to identify the first block, which will have an empty start_sponge_blob, and non-empty in_hash. + // Used to identify the first block, which will have an empty start_sponge_blob. pub start_block_number: u32, // Used to identify the first checkpoint, which will have an empty start_blob_accumulator. @@ -458,7 +458,6 @@ impl RollupFixtureBuilder { pub fn get_parity_public_inputs(self, slot_number: Field) -> ParityPublicInputs { ParityPublicInputs { - in_hash: slot_number * 85831493, start_rolling_hash: self.get_inbox_rolling_hash(slot_number), end_rolling_hash: self.get_inbox_rolling_hash(slot_number + 1), // The message sponge resets per checkpoint, so it starts empty and ends at the checkpoint's accumulated diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/checkpoint_header.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/checkpoint_header.nr index 59cb06c87a07..ca150f0d819f 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/checkpoint_header.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/checkpoint_header.nr @@ -14,10 +14,9 @@ pub struct CheckpointHeader { pub last_archive_root: Field, pub block_headers_hash: Field, pub blobs_hash: Field, - pub in_hash: Field, - // Inbox rolling-hash chain value after consuming all L1-to-L2 messages bundled into this checkpoint. It is the - // dual of `in_hash` (AZIP-22 Fast Inbox): the truncated-to-field sha256 chain the L1 Inbox accumulates. Currently a - // pass-through commitment; `in_hash` remains the authoritative L1 check until the Fast Inbox flip. + // Inbox rolling-hash chain value after consuming all L1-to-L2 messages bundled into this checkpoint (AZIP-22 Fast + // Inbox): the truncated-to-field sha256 chain the L1 Inbox accumulates. This is the checkpoint's only inbox + // commitment. pub inbox_rolling_hash: Field, // The root of the epoch out hash balanced tree. The out hash of the first checkpoint in the epoch is inserted at // index 0, the second at index 1, and so on. @@ -43,7 +42,6 @@ impl Empty for CheckpointHeader { last_archive_root: 0, block_headers_hash: 0, blobs_hash: 0, - in_hash: 0, inbox_rolling_hash: 0, epoch_out_hash: 0, slot_number: 0, @@ -62,7 +60,6 @@ impl CheckpointHeader { let last_archive_root_bytes: [u8; 32] = self.last_archive_root.to_be_bytes(); let block_headers_hash_bytes: [u8; 32] = self.block_headers_hash.to_be_bytes(); let blobs_hash_bytes: [u8; 32] = self.blobs_hash.to_be_bytes(); - let in_hash_bytes: [u8; 32] = self.in_hash.to_be_bytes(); let inbox_rolling_hash_bytes: [u8; 32] = self.inbox_rolling_hash.to_be_bytes(); let epoch_out_hash_bytes: [u8; 32] = self.epoch_out_hash.to_be_bytes(); let slot_number_bytes: [u8; 32] = self.slot_number.to_be_bytes(); @@ -83,7 +80,6 @@ impl CheckpointHeader { last_archive_root_bytes .concat(block_headers_hash_bytes) .concat(blobs_hash_bytes) - .concat(in_hash_bytes) .concat(inbox_rolling_hash_bytes) .concat(epoch_out_hash_bytes) .concat(slot_number_bytes) @@ -108,7 +104,7 @@ fn empty_checkpoint_header_hash_matches_ts() { let header = CheckpointHeader::empty(); // Generated from checkpoint_header.test.ts - let empty_checkpoint_header_hash_from_ts = 0x0008c3e5bbea4fba57201a69d4bf70a0d255df921fdef4924c22da087f9338c2; + let empty_checkpoint_header_hash_from_ts = 0x002e384af86a480f952aa16443fd29646a9063865e62d7c403fc7ed697bb7712; assert_eq(header.hash(), empty_checkpoint_header_hash_from_ts); } @@ -119,7 +115,6 @@ fn checkpoint_header_hash_matches_ts() { last_archive_root: 123, block_headers_hash: 456, blobs_hash: 77, - in_hash: 88, inbox_rolling_hash: 89, epoch_out_hash: 99, slot_number: 1234, @@ -137,7 +132,7 @@ fn checkpoint_header_hash_matches_ts() { assert_eq(deserialized, header); // Generated from checkpoint_header.test.ts - let checkpoint_header_hash_from_ts = 0x00519b87177a8a5e4edd03f4d820aec6d402497ef1ab70e2ecd4d4c39b339611; + let checkpoint_header_hash_from_ts = 0x00751391e842cd7b2014478255dd3309df86327197a0feb03f0af1b758f62ba5; assert_eq(header.hash(), checkpoint_header_hash_from_ts); } @@ -150,7 +145,6 @@ fn checkpoint_header_hash_large_values_matches_ts() { last_archive_root: MAX_FIELD_VALUE - 123, block_headers_hash: MAX_FIELD_VALUE - 456, blobs_hash: MAX_FIELD_VALUE - 77, - in_hash: MAX_FIELD_VALUE - 88, inbox_rolling_hash: MAX_FIELD_VALUE - 89, epoch_out_hash: MAX_FIELD_VALUE - 99, slot_number: MAX_FIELD_VALUE - 1234, @@ -170,7 +164,7 @@ fn checkpoint_header_hash_large_values_matches_ts() { assert_eq(deserialized, header); // Generated from checkpoint_header.test.ts - let checkpoint_header_hash_large_values_from_ts = 0x00d64307fa93c32ae46a4c3b6a1911d31994daa8d11d50201f0086a6cbaa9bac; + let checkpoint_header_hash_large_values_from_ts = 0x005bd09725c6e77a4a28a7ccdaf7875ba5882431ca3c82e62db96e8a12769ce5; assert_eq(header.hash(), checkpoint_header_hash_large_values_from_ts); } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr index 262adeec56b3..58dbd4831614 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr @@ -61,12 +61,10 @@ pub global NOTE_HASH_SUBTREE_ROOT_SIBLING_PATH_LENGTH: u32 = NOTE_HASH_TREE_HEIGHT - NOTE_HASH_SUBTREE_HEIGHT; pub global NULLIFIER_SUBTREE_ROOT_SIBLING_PATH_LENGTH: u32 = NULLIFIER_TREE_HEIGHT - NULLIFIER_SUBTREE_HEIGHT; -pub global L1_TO_L2_MSG_SUBTREE_ROOT_SIBLING_PATH_LENGTH: u32 = - L1_TO_L2_MSG_TREE_HEIGHT - L1_TO_L2_MSG_SUBTREE_HEIGHT; // Cap on L1-to-L2 messages bundled into a single L2 block. One quarter of the per-checkpoint // cap: a checkpoint drains its Inbox consumption across up to four message-bearing blocks. pub global MAX_L1_TO_L2_MSGS_PER_BLOCK: u32 = 256; -// Cap on L1-to-L2 messages consumed by a single checkpoint. Semantically today's NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP. +// Cap on L1-to-L2 messages consumed by a single checkpoint. pub global MAX_L1_TO_L2_MSGS_PER_CHECKPOINT: u32 = 1024; // Minimum bucket age, in seconds, at the start of a checkpoint's build frame for its consumption to be mandatory under // the streaming Inbox censorship cutoff. One L1 slot: validators cannot be required to act on @@ -109,9 +107,6 @@ pub global MAX_NULLIFIER_READ_REQUESTS_PER_CALL: u32 = 16; pub global MAX_KEY_VALIDATION_REQUESTS_PER_CALL: u32 = MAX_PRIVATE_LOGS_PER_CALL; // docs:end:constants -// ROLLUP CONTRACT CONSTANTS - constants used only in l1-contracts -pub global NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP: u32 = 1 << L1_TO_L2_MSG_SUBTREE_HEIGHT; - // VK TREE CONSTANTS pub global PRIVATE_KERNEL_INIT_VK_INDEX: u32 = 0; pub global PRIVATE_KERNEL_INNER_VK_INDEX: u32 = 1; @@ -453,7 +448,6 @@ pub global BLOCK_HEADER_LENGTH: u32 = APPEND_ONLY_TREE_SNAPSHOT_LENGTH pub global CHECKPOINT_HEADER_LENGTH: u32 = 1 /* last_archive_root */ + 1 /* block_headers_hash */ + 1 /* blobs_hash */ - + 1 /* in_hash */ + 1 /* inbox_rolling_hash */ + 1 /* out_hash */ + 1 /* slot_number */ @@ -675,12 +669,6 @@ pub global INBOX_PARITY_SIZE_SMALL: u32 = 64; pub global INBOX_PARITY_SIZE_MEDIUM: u32 = 256; pub global INBOX_PARITY_SIZE_LARGE: u32 = 1024; -// Fan-in of the sha256 `in_hash` frontier tree (NUM_BASE_PARITY_PER_ROOT_PARITY * NUM_MSGS_PER_BASE_PARITY == -// NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP). The circuits no longer build this tree, but the constants are still consumed -// by the generated Solidity `Constants` and the L1 `Parity.t.sol` test that checks the frontier-root construction. -pub global NUM_MSGS_PER_BASE_PARITY: u32 = 256; -pub global NUM_BASE_PARITY_PER_ROOT_PARITY: u32 = 4; - // Lengths of the different types of proofs in fields pub global RECURSIVE_PROOF_LENGTH: u32 = 410; pub global NESTED_RECURSIVE_PROOF_LENGTH: u32 = RECURSIVE_PROOF_LENGTH; @@ -1443,7 +1431,7 @@ mod test { use crate::traits::ToField; use super::{ GLOBAL_INDEX_CONTRACT_MIN_REVERTIBLE_SIDE_EFFECT_COUNTER_OFFSET, INBOX_PARITY_SIZE_LARGE, - MAX_ETH_ADDRESS_VALUE, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, + MAX_ETH_ADDRESS_VALUE, MAX_L1_TO_L2_MSGS_PER_CHECKPOINT, TOTAL_COUNTED_SIDE_EFFECTS_PER_CALL, }; @@ -1490,7 +1478,7 @@ mod test { #[test] fn parity_covers_all_l1_to_l2_msgs() { // The largest InboxParity rung must be able to hold a full checkpoint's worth of messages. - assert_eq(INBOX_PARITY_SIZE_LARGE, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP); + assert_eq(INBOX_PARITY_SIZE_LARGE, MAX_L1_TO_L2_MSGS_PER_CHECKPOINT); } #[test] diff --git a/yarn-project/archiver/src/l1/calldata_retriever.test.ts b/yarn-project/archiver/src/l1/calldata_retriever.test.ts index cc589173dbf9..ce2fa5c3f3c3 100644 --- a/yarn-project/archiver/src/l1/calldata_retriever.test.ts +++ b/yarn-project/archiver/src/l1/calldata_retriever.test.ts @@ -1248,7 +1248,7 @@ describe('CalldataRetriever', () => { expect(result.blockHash).toBe(tx.blockHash); // Verify all components are properly decoded - expect(result.header.inHash).toBeInstanceOf(Fr); + expect(result.header.inboxRollingHash).toBeInstanceOf(Fr); expect(result.header.gasFees).toBeInstanceOf(GasFees); // Verify instrumentation was called @@ -1322,7 +1322,7 @@ describe('CalldataRetriever', () => { expect(result.blockHash).toBe(blockHash); // Verify all components are properly decoded - expect(result.header.inHash).toBeInstanceOf(Fr); + expect(result.header.inboxRollingHash).toBeInstanceOf(Fr); expect(result.header.gasFees).toBeInstanceOf(GasFees); // Verify proxy implementation was checked diff --git a/yarn-project/archiver/src/test/mock_structs.ts b/yarn-project/archiver/src/test/mock_structs.ts index 5cc54cfe9bad..7cd23455029e 100644 --- a/yarn-project/archiver/src/test/mock_structs.ts +++ b/yarn-project/archiver/src/test/mock_structs.ts @@ -1,8 +1,4 @@ -import { - MAX_NOTE_HASHES_PER_TX, - NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, - PRIVATE_LOG_SIZE_IN_FIELDS, -} from '@aztec/constants'; +import { MAX_L1_TO_L2_MSGS_PER_CHECKPOINT, MAX_NOTE_HASHES_PER_TX, PRIVATE_LOG_SIZE_IN_FIELDS } from '@aztec/constants'; import { makeTuple } from '@aztec/foundation/array'; import { BlockNumber, CheckpointNumber, IndexWithinCheckpoint } from '@aztec/foundation/branded-types'; import { Buffer16, Buffer32 } from '@aztec/foundation/buffer'; @@ -104,12 +100,12 @@ export function makeInboxMessagesWithFullBlocks( opts: { initialCheckpointNumber?: CheckpointNumber } = {}, ): InboxMessage[] { const { initialCheckpointNumber = CheckpointNumber(13) } = opts; - return makeInboxMessages(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP * blockCount, { + return makeInboxMessages(MAX_L1_TO_L2_MSGS_PER_CHECKPOINT * blockCount, { // Keep the compact global index from makeInboxMessages; only spread the (now-vestigial) checkpoint assignment // across blocks so multi-block coverage still exercises differing checkpoint numbers. overrideFn: (msg, i) => { const checkpointNumber = CheckpointNumber( - initialCheckpointNumber + Math.floor(i / NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP), + initialCheckpointNumber + Math.floor(i / MAX_L1_TO_L2_MSGS_PER_CHECKPOINT), ); return { ...msg, checkpointNumber }; }, diff --git a/yarn-project/ethereum/src/contracts/rollup.ts b/yarn-project/ethereum/src/contracts/rollup.ts index f3f39b1020ef..ba41242c3e5f 100644 --- a/yarn-project/ethereum/src/contracts/rollup.ts +++ b/yarn-project/ethereum/src/contracts/rollup.ts @@ -79,7 +79,6 @@ export type ViemHeader = { lastArchiveRoot: `0x${string}`; blockHeadersHash: `0x${string}`; blobsHash: `0x${string}`; - inHash: `0x${string}`; inboxRollingHash: `0x${string}`; outHash: `0x${string}`; slotNumber: bigint; diff --git a/yarn-project/ivc-integration/src/base_parity_inputs.test.ts b/yarn-project/ivc-integration/src/base_parity_inputs.test.ts index 2d5ea50b7092..278ed58ca3a7 100644 --- a/yarn-project/ivc-integration/src/base_parity_inputs.test.ts +++ b/yarn-project/ivc-integration/src/base_parity_inputs.test.ts @@ -15,7 +15,7 @@ import { createLogger } from '@aztec/foundation/log'; import { Noir } from '@aztec/noir-noir_js'; import { ServerCircuitArtifacts } from '@aztec/noir-protocol-circuits-types/server'; import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree'; -import { L1ToL2MessageSponge, computeInHashFromL1ToL2Messages } from '@aztec/stdlib/messaging'; +import { L1ToL2MessageSponge } from '@aztec/stdlib/messaging'; import { InboxParityPrivateInputs } from '@aztec/stdlib/parity'; import { jest } from '@jest/globals'; @@ -47,7 +47,6 @@ describe('Inbox Parity Benchmark Inputs', () => { l1ToL2Messages, Fr.ZERO, L1ToL2MessageSponge.empty(), - computeInHashFromL1ToL2Messages(l1ToL2Messages), vkTreeRoot, Fr.random(), ); @@ -75,8 +74,6 @@ describe('Inbox Parity Benchmark Inputs', () => { num_absorbed: startSponge.numAbsorbed, }, // eslint-disable-next-line camelcase - in_hash: inputs.inHash.toString(), - // eslint-disable-next-line camelcase vk_tree_root: inputs.vkTreeRoot.toString(), // eslint-disable-next-line camelcase prover_id: inputs.proverId.toString(), diff --git a/yarn-project/ivc-integration/src/bb_js_debug.test.ts b/yarn-project/ivc-integration/src/bb_js_debug.test.ts index f478e61dd182..9ce6333f5fcb 100644 --- a/yarn-project/ivc-integration/src/bb_js_debug.test.ts +++ b/yarn-project/ivc-integration/src/bb_js_debug.test.ts @@ -12,7 +12,7 @@ import { createLogger } from '@aztec/foundation/log'; import { Noir } from '@aztec/noir-noir_js'; import { ServerCircuitArtifacts } from '@aztec/noir-protocol-circuits-types/server'; import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree'; -import { L1ToL2MessageSponge, computeInHashFromL1ToL2Messages } from '@aztec/stdlib/messaging'; +import { L1ToL2MessageSponge } from '@aztec/stdlib/messaging'; import { InboxParityPrivateInputs } from '@aztec/stdlib/parity'; import { jest } from '@jest/globals'; @@ -62,7 +62,6 @@ describe('BB.js Debug Wrapper', () => { l1ToL2Messages, Fr.ZERO, L1ToL2MessageSponge.empty(), - computeInHashFromL1ToL2Messages(l1ToL2Messages), vkTreeRoot, Fr.random(), ); @@ -88,8 +87,6 @@ describe('BB.js Debug Wrapper', () => { num_absorbed: startSponge.numAbsorbed, }, // eslint-disable-next-line camelcase - in_hash: inboxParityInputs.inHash.toString(), - // eslint-disable-next-line camelcase vk_tree_root: inboxParityInputs.vkTreeRoot.toString(), // eslint-disable-next-line camelcase prover_id: inboxParityInputs.proverId.toString(), diff --git a/yarn-project/noir-protocol-circuits-types/src/conversion/server.ts b/yarn-project/noir-protocol-circuits-types/src/conversion/server.ts index f37702498807..0e32f920b309 100644 --- a/yarn-project/noir-protocol-circuits-types/src/conversion/server.ts +++ b/yarn-project/noir-protocol-circuits-types/src/conversion/server.ts @@ -481,7 +481,6 @@ export function mapAvmProofDataToNoir( function mapParityPublicInputsToNoir(parityPublicInputs: ParityPublicInputs): ParityPublicInputsNoir { return { - in_hash: mapFieldToNoir(parityPublicInputs.inHash), start_rolling_hash: mapFieldToNoir(parityPublicInputs.startRollingHash), end_rolling_hash: mapFieldToNoir(parityPublicInputs.endRollingHash), start_sponge: mapL1ToL2MessageSpongeToNoir(parityPublicInputs.startSponge), @@ -520,7 +519,6 @@ export function mapRootRollupPublicInputsFromNoir( */ export function mapParityPublicInputsFromNoir(parityPublicInputs: ParityPublicInputsNoir): ParityPublicInputs { return new ParityPublicInputs( - mapFieldFromNoir(parityPublicInputs.in_hash), mapFieldFromNoir(parityPublicInputs.start_rolling_hash), mapFieldFromNoir(parityPublicInputs.end_rolling_hash), mapL1ToL2MessageSpongeFromNoir(parityPublicInputs.start_sponge), @@ -739,7 +737,6 @@ export function mapInboxParityPrivateInputsToNoir(inputs: InboxParityPrivateInpu num_msgs: mapNumberToNoir(inputs.numMessages), start_rolling_hash: mapFieldToNoir(inputs.startRollingHash), start_sponge: mapL1ToL2MessageSpongeToNoir(inputs.startSponge), - in_hash: mapFieldToNoir(inputs.inHash), vk_tree_root: mapFieldToNoir(inputs.vkTreeRoot), prover_id: mapFieldToNoir(inputs.proverId), }; diff --git a/yarn-project/prover-client/src/light/lightweight_checkpoint_builder.ts b/yarn-project/prover-client/src/light/lightweight_checkpoint_builder.ts index b50ef0313ed0..e2481445632b 100644 --- a/yarn-project/prover-client/src/light/lightweight_checkpoint_builder.ts +++ b/yarn-project/prover-client/src/light/lightweight_checkpoint_builder.ts @@ -248,7 +248,7 @@ export class LightweightCheckpointBuilder { this.blocks.push(block); // Accumulate the streaming bundle now that the block is fully built, so a mid-build throw above leaves the - // checkpoint's message list (and thus its inHash/rolling hash) consistent with the blocks actually built. + // checkpoint's message list (and thus its rolling hash) consistent with the blocks actually built. this.l1ToL2Messages.push(...l1ToL2Messages); const [msSpongeAbsorb] = await elapsed(() => this.spongeBlob.absorb(blockBlobFields)); @@ -283,9 +283,6 @@ export class LightweightCheckpointBuilder { const blobs = await getBlobsPerL1Block(this.blobFields); const blobsHash = computeBlobsHashFromBlobs(blobs); - // Legacy inHash is dead post-flip; the checkpoint header carries zero (AZIP-22 Fast Inbox). The consensus - // rolling hash over the consumed messages is the authoritative Inbox commitment. - const inHash = Fr.ZERO; const inboxRollingHash = accumulateInboxRollingHash(this.previousInboxRollingHash, this.l1ToL2Messages); const { slotNumber, coinbase, feeRecipient, gasFees } = this.constants; @@ -303,7 +300,6 @@ export class LightweightCheckpointBuilder { const header = CheckpointHeader.from({ lastArchiveRoot: this.lastArchives[0].root, blobsHash, - inHash, inboxRollingHash, epochOutHash, blockHeadersHash, diff --git a/yarn-project/prover-client/src/orchestrator/checkpoint-proving-state.ts b/yarn-project/prover-client/src/orchestrator/checkpoint-proving-state.ts index 04697e6e6249..98d3d9946ac3 100644 --- a/yarn-project/prover-client/src/orchestrator/checkpoint-proving-state.ts +++ b/yarn-project/prover-client/src/orchestrator/checkpoint-proving-state.ts @@ -161,15 +161,13 @@ export class CheckpointProvingState { /** * Builds the checkpoint's single InboxParity input. The circuit is sized to the smallest ladder rung that fits the * message count; the rolling hash starts from the previous checkpoint's end and the message sponge starts empty (it - * resets per checkpoint). `in_hash` (the L1 frontier root) is supplied as an unconstrained pass-through hint. + * resets per checkpoint). */ public getInboxParityInputs(): InboxParityPrivateInputs { return InboxParityPrivateInputs.fromMessages( this.l1ToL2Messages, this.startInboxRollingHash, L1ToL2MessageSponge.empty(), - // Legacy in_hash is dead post-flip; the InboxParity pass-through hint carries zero (AZIP-22 Fast Inbox). - Fr.ZERO, this.constants.vkTreeRoot, this.constants.proverId, ); diff --git a/yarn-project/prover-client/src/test/bb_prover_parity.test.ts b/yarn-project/prover-client/src/test/bb_prover_parity.test.ts index fbb920407ead..4848af4efd85 100644 --- a/yarn-project/prover-client/src/test/bb_prover_parity.test.ts +++ b/yarn-project/prover-client/src/test/bb_prover_parity.test.ts @@ -5,7 +5,7 @@ import { createLogger } from '@aztec/foundation/log'; import type { ServerProtocolArtifact } from '@aztec/noir-protocol-circuits-types/server'; import { ServerCircuitVks } from '@aztec/noir-protocol-circuits-types/server/vks'; import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree'; -import { L1ToL2MessageSponge, computeInHashFromL1ToL2Messages } from '@aztec/stdlib/messaging'; +import { L1ToL2MessageSponge } from '@aztec/stdlib/messaging'; import { INBOX_PARITY_SIZES, InboxParityPrivateInputs, type InboxParitySize } from '@aztec/stdlib/parity'; import { TestContext } from '../mocks/test_context.js'; @@ -48,15 +48,11 @@ describe('prover/bb_prover/parity', () => { // Fill the rung with real messages so `numMessages === size` (the largest circuit for that rung). const messages = Array.from({ length: size }, () => Fr.random()); const proverId = Fr.random(); - // The in_hash is a sha256 frontier root (top byte zeroed to fit the field), which `ParityPublicInputs` enforces; - // compute it from the messages rather than using a raw random field. - const inHash = computeInHashFromL1ToL2Messages(messages); const inputs = InboxParityPrivateInputs.fromMessages( messages, Fr.ZERO, L1ToL2MessageSponge.empty(), - inHash, getVKTreeRoot(), proverId, ); diff --git a/yarn-project/pxe/src/storage/backwards_compatibility_tests/__snapshots__/L2TipsKVStore.json b/yarn-project/pxe/src/storage/backwards_compatibility_tests/__snapshots__/L2TipsKVStore.json index e83ff396e83e..c919b85f457b 100644 --- a/yarn-project/pxe/src/storage/backwards_compatibility_tests/__snapshots__/L2TipsKVStore.json +++ b/yarn-project/pxe/src/storage/backwards_compatibility_tests/__snapshots__/L2TipsKVStore.json @@ -16,7 +16,7 @@ "pxe_l2_tip_checkpoints": [ { "key": "utf8:checkpointed", - "value": "{\"number\":47,\"hash\":\"0x009bac3aae6ac2c7945a4686ec07381ac88bbd323cbfaccd6dffea9b254ae820\"}" + "value": "{\"number\":47,\"hash\":\"0x00407c60c560276f14936ea39b073d155bc34a485e8a83c0439c9b8ef83f737e\"}" }, { "key": "utf8:proven", diff --git a/yarn-project/pxe/src/storage/backwards_compatibility_tests/schema_tests.ts b/yarn-project/pxe/src/storage/backwards_compatibility_tests/schema_tests.ts index a9a75a77ffbc..c1802c49f19b 100644 --- a/yarn-project/pxe/src/storage/backwards_compatibility_tests/schema_tests.ts +++ b/yarn-project/pxe/src/storage/backwards_compatibility_tests/schema_tests.ts @@ -276,7 +276,6 @@ export const SCHEMA_TESTS: readonly SchemaTest[] = [ new Fr(5n), new Fr(7n), new Fr(11n), - new Fr(13n), new Fr(15n), new Fr(17n), SlotNumber(19), diff --git a/yarn-project/sequencer-client/src/publisher/l1_publisher.integration.test.ts b/yarn-project/sequencer-client/src/publisher/l1_publisher.integration.test.ts index 1d15418c5d42..05f09d1745e5 100644 --- a/yarn-project/sequencer-client/src/publisher/l1_publisher.integration.test.ts +++ b/yarn-project/sequencer-client/src/publisher/l1_publisher.integration.test.ts @@ -14,6 +14,8 @@ import { } from '@aztec/blob-lib'; import { GENESIS_ARCHIVE_ROOT, + INBOX_LAG_SECONDS, + MAX_L1_TO_L2_MSGS_PER_BLOCK, MAX_L1_TO_L2_MSGS_PER_CHECKPOINT, MAX_NULLIFIERS_PER_TX, MAX_PROCESSABLE_L2_GAS, @@ -92,6 +94,7 @@ import { type PrivateKeyAccount, privateKeyToAccount } from 'viem/accounts'; import { foundry } from 'viem/chains'; import { type SequencerClientConfig, getConfigEnvVars } from '../config.js'; +import { selectInboxBucketForBlock } from '../sequencer/inbox_bucket_selector.js'; import { sendL1ToL2Message } from './l1_to_l2_messaging.js'; import { SequencerPublisherMetrics } from './sequencer-publisher-metrics.js'; import { SequencerPublisher } from './sequencer-publisher.js'; @@ -473,7 +476,7 @@ describe('L1Publisher integration', () => { /** * Build a checkpoint with a single block using the LightweightCheckpointBuilder. - * This properly computes all checkpoint header fields (blobsHash, blockHeadersHash, inHash, inboxRollingHash, + * This properly computes all checkpoint header fields (blobsHash, blockHeadersHash, inboxRollingHash, * epochOutHash, etc.). `previousInboxRollingHash` is the previous checkpoint's rolling hash (zero at genesis), so * the header's `inboxRollingHash` continues the on-chain Inbox chain over `l1ToL2Messages`. */ @@ -564,17 +567,23 @@ describe('L1Publisher integration', () => { '0x1647b194c649f5dd01d7c832f89b0f496043c9150797923ea89e93d5ac619a93', ); - // Streaming Inbox consumption (AZIP-22 Fast Inbox): each checkpoint consumes every message sent so far, so its - // header rolling hash continues the previous checkpoint's and matches the Inbox's current bucket. Consuming - // through the newest bucket trivially satisfies the mandatory-consumption assert. A checkpoint's messages are - // sent one per L1 block, so they span several Inbox buckets; the checkpoint's consumed bucket is the newest, - // whose sequence we read straight off the deployed Inbox and mirror into messageSource so the propose bucket - // hint can be looked up by the checkpoint's cumulative message count. + // Streaming Inbox consumption (AZIP-22 Fast Inbox): the L1 Rollup only lets a checkpoint consume Inbox buckets + // that have aged past the censorship cutoff (`toTimestamp(slot - 1) - INBOX_LAG_SECONDS`), measured in L1 time, + // not whole checkpoints. Each checkpoint mirrors the real Inbox buckets into messageSource, then reuses the + // production `selectInboxBucketForBlock` (which mirrors `ProposeLib.validateInboxConsumption`) to pick exactly + // the buckets it must consume, deriving the consumed bundle, the propose bucket hint, and the header rolling + // hash from that one selection so header, world state, and L1 agree by construction. const inbox = getContract({ address: getAddress(l1ContractAddresses.inboxAddress.toString()), abi: InboxAbi, client: l1Client, }); + // Every message sent to the Inbox, in insertion order, so each bucket's leaves can be mirrored into messageSource. + const allSentMessages: Fr[] = []; + let mirroredThroughSeq = 0n; + let mirroredThroughTotal = 0n; + // The last Inbox bucket this checkpoint chain has consumed through; genesis sentinel to start. + let parent = { seq: 0n, totalMsgCount: 0n }; let previousInboxRollingHash = Fr.ZERO; const blobFieldsPerCheckpoint: Fr[][] = []; // The below batched blob is used for testing different epochs with 1..numberOfConsecutiveBlocks blocks on L1. @@ -586,10 +595,32 @@ describe('L1Publisher integration', () => { // and causes a chain prune const l1ToL2Content = range(Math.min(16, MAX_L1_TO_L2_MSGS_PER_CHECKPOINT), 128 * i + 1 + 0x400).map(fr); - const currentL1ToL2Messages: Fr[] = []; + const sentThisCheckpoint: Fr[] = []; for (let j = 0; j < l1ToL2Content.length; j++) { - currentL1ToL2Messages.push(await sendToL2(l1ToL2Content[j], recipientAddress)); + sentThisCheckpoint.push(await sendToL2(l1ToL2Content[j], recipientAddress)); } + allSentMessages.push(...sentThisCheckpoint); + + // Mirror the Inbox's new buckets (seq, timestamp, rolling hash, totals) and their leaves into messageSource, + // so the selector, the world-state synchronizer, and L1 all read the same bucket state. + const currentBucketSeq = await inbox.read.getCurrentBucketSeq(); + for (let seq = mirroredThroughSeq + 1n; seq <= currentBucketSeq; seq++) { + const bucket = await inbox.read.getBucket([seq]); + const bucketMessages = allSentMessages.slice(Number(mirroredThroughTotal), Number(bucket.totalMsgCount)); + messageSource.setInboxBucket( + { + seq, + inboxRollingHash: Fr.fromString(bucket.rollingHash), + totalMsgCount: bucket.totalMsgCount, + timestamp: bucket.timestamp, + msgCount: Number(bucket.msgCount), + lastMessageIndex: bucket.totalMsgCount - 1n, + }, + bucketMessages, + ); + mirroredThroughTotal = bucket.totalMsgCount; + } + mirroredThroughSeq = currentBucketSeq; // Ensure that each transaction has unique (non-intersecting nullifier values) const totalNullifiersPerBlock = 4 * MAX_NULLIFIERS_PER_TX; @@ -612,6 +643,25 @@ describe('L1Publisher integration', () => { new GasFees(0, await rollup.getManaMinFeeAt(timestamp, true)), ); + // Reuse the production streaming selector to pick the buckets this single-block (hence last-block) checkpoint + // must consume, then derive the consumed bundle, the propose bucket hint, and the rolling-hash cursor from + // that one selection so the header, world state, and L1 all agree. + const buildFrameStart = await rollup.getTimestampForSlot(SlotNumber(slot - 1)); + const cutoffTimestamp = buildFrameStart - BigInt(INBOX_LAG_SECONDS); + const selection = await selectInboxBucketForBlock({ + messageSource, + now: buildFrameStart, + lagSeconds: BigInt(INBOX_LAG_SECONDS), + parent, + checkpointStartTotalMsgCount: parent.totalMsgCount, + perBlockCap: MAX_L1_TO_L2_MSGS_PER_BLOCK, + perCheckpointCap: MAX_L1_TO_L2_MSGS_PER_CHECKPOINT, + isLastBlock: true, + cutoffTimestamp, + }); + const currentL1ToL2Messages = selection.consume ? selection.bundle : []; + const bucketHint = selection.consume ? selection.bucket.seq : parent.seq; + const checkpoint = await buildCheckpoint( globalVariables, txs, @@ -621,24 +671,9 @@ describe('L1Publisher integration', () => { ); previousInboxRollingHash = checkpoint.header.inboxRollingHash; const block = checkpoint.blocks[0]; - - // Mirror the Inbox's newest bucket into messageSource so the world-state synchronizer can rebuild this - // block's consumed bundle when it syncs the block back on the next iteration, and so the propose bucket hint - // resolves to the sequence whose rolling hash the checkpoint header committed to. One bucket per block keyed - // by its cumulative L1->L2 tree leaf count -- exactly the value world-state looks the bucket up by. - const currentBucketSeq = await inbox.read.getCurrentBucketSeq(); - const cumulativeMsgCount = BigInt(block.header.state.l1ToL2MessageTree.nextAvailableLeafIndex); - messageSource.setInboxBucket( - { - seq: currentBucketSeq, - inboxRollingHash: checkpoint.header.inboxRollingHash, - totalMsgCount: cumulativeMsgCount, - timestamp, - msgCount: currentL1ToL2Messages.length, - lastMessageIndex: cumulativeMsgCount - 1n, - }, - currentL1ToL2Messages, - ); + if (selection.consume) { + parent = { seq: selection.bucket.seq, totalMsgCount: selection.bucket.totalMsgCount }; + } const totalManaUsed = txs.reduce((acc, tx) => acc.add(new Fr(tx.gasUsed.billedGas.l2Gas)), Fr.ZERO); expect(totalManaUsed.toBigInt()).toEqual(block.header.totalManaUsed.toBigInt()); @@ -667,8 +702,6 @@ describe('L1Publisher integration', () => { deployerAccount.address, ); - // The checkpoint consumed everything sent so far, so its consumed bucket is the Inbox's newest. - const bucketHint = (await messageSource.getInboxBucketByTotalMsgCount(cumulativeMsgCount))!.seq; await publisher.enqueueProposeCheckpoint( checkpoint, CommitteeAttestationsAndSigners.empty(getSignatureContext()), diff --git a/yarn-project/sequencer-client/src/publisher/write_json.ts b/yarn-project/sequencer-client/src/publisher/write_json.ts index 401a94908a6f..e3a2b74e1603 100644 --- a/yarn-project/sequencer-client/src/publisher/write_json.ts +++ b/yarn-project/sequencer-client/src/publisher/write_json.ts @@ -55,7 +55,6 @@ export async function writeJson( lastArchiveRoot: asHex(checkpointHeader.lastArchiveRoot), blockHeadersHash: asHex(checkpointHeader.blockHeadersHash), blobsHash: asHex(checkpointHeader.blobsHash), - inHash: asHex(checkpointHeader.inHash), inboxRollingHash: asHex(checkpointHeader.inboxRollingHash), outHash: asHex(checkpointHeader.epochOutHash), slotNumber: Number(checkpointHeader.slotNumber), diff --git a/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts b/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts index 95a866e36a3e..2f77bc1c6cc2 100644 --- a/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts +++ b/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts @@ -1,4 +1,4 @@ -import { NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/constants'; +import { MAX_L1_TO_L2_MSGS_PER_CHECKPOINT } from '@aztec/constants'; import { type EpochCache, type EpochCommitteeInfo, PROPOSER_PIPELINING_SLOT_OFFSET } from '@aztec/epoch-cache'; import { NoCommitteeError, type RollupContract } from '@aztec/ethereum/contracts'; import { @@ -349,7 +349,7 @@ describe('sequencer', () => { }); l1ToL2MessageSource = mock({ - getL1ToL2Messages: () => Promise.resolve(Array(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP).fill(Fr.ZERO)), + getL1ToL2Messages: () => Promise.resolve(Array(MAX_L1_TO_L2_MSGS_PER_CHECKPOINT).fill(Fr.ZERO)), getL2Tips: mockFn().mockResolvedValue({ proposed: { number: lastBlockNumber, hash }, checkpointed: { diff --git a/yarn-project/sequencer-client/src/test/utils.ts b/yarn-project/sequencer-client/src/test/utils.ts index 92845b668f07..5bb506d4f729 100644 --- a/yarn-project/sequencer-client/src/test/utils.ts +++ b/yarn-project/sequencer-client/src/test/utils.ts @@ -114,7 +114,7 @@ export function createMockSignatures(signer: Secp256k1Signer): CommitteeAttestat /** * Creates a CheckpointHeader from an L2Block for testing purposes. - * Uses mock values for blockHeadersHash, blobsHash and inHash since L2Block doesn't have these fields. + * Uses mock values for blockHeadersHash and blobsHash since L2Block doesn't have these fields. */ function createCheckpointHeaderFromBlock(block: L2Block): CheckpointHeader { const gv = block.header.globalVariables; @@ -122,7 +122,6 @@ function createCheckpointHeaderFromBlock(block: L2Block): CheckpointHeader { block.header.lastArchive.root, Fr.random(), // blockHeadersHash - mock value for testing Fr.random(), // blobsHash - mock value for testing - Fr.random(), // inHash - mock value for testing Fr.random(), // inboxRollingHash - mock value for testing Fr.random(), // outHash - mock value for testing gv.slotNumber, diff --git a/yarn-project/stdlib/src/messaging/in_hash.ts b/yarn-project/stdlib/src/messaging/in_hash.ts index 52833bf937fe..fd5191abb5c1 100644 --- a/yarn-project/stdlib/src/messaging/in_hash.ts +++ b/yarn-project/stdlib/src/messaging/in_hash.ts @@ -1,4 +1,4 @@ -import { NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/constants'; +import { MAX_L1_TO_L2_MSGS_PER_CHECKPOINT } from '@aztec/constants'; import { padArrayEnd } from '@aztec/foundation/collection'; import { Fr } from '@aztec/foundation/curves/bn254'; import { computeBalancedShaRoot } from '@aztec/foundation/trees'; @@ -7,6 +7,6 @@ import { computeBalancedShaRoot } from '@aztec/foundation/trees'; * Computes the inHash for a checkpoint (or the first block in a checkpoint) given its l1 to l2 messages. */ export function computeInHashFromL1ToL2Messages(unpaddedL1ToL2Messages: Fr[]): Fr { - const l1ToL2Messages = padArrayEnd(unpaddedL1ToL2Messages, Fr.ZERO, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP); + const l1ToL2Messages = padArrayEnd(unpaddedL1ToL2Messages, Fr.ZERO, MAX_L1_TO_L2_MSGS_PER_CHECKPOINT); return new Fr(computeBalancedShaRoot(l1ToL2Messages.map(msg => msg.toBuffer()))); } diff --git a/yarn-project/stdlib/src/messaging/inbox_leaf.ts b/yarn-project/stdlib/src/messaging/inbox_leaf.ts index 21d703d06150..397c29a07efa 100644 --- a/yarn-project/stdlib/src/messaging/inbox_leaf.ts +++ b/yarn-project/stdlib/src/messaging/inbox_leaf.ts @@ -1,4 +1,4 @@ -import { INITIAL_CHECKPOINT_NUMBER, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/constants'; +import { INITIAL_CHECKPOINT_NUMBER, MAX_L1_TO_L2_MSGS_PER_CHECKPOINT } from '@aztec/constants'; import { toBigIntBE } from '@aztec/foundation/bigint-buffer'; import { CheckpointNumber } from '@aztec/foundation/branded-types'; import { Fr } from '@aztec/foundation/curves/bn254'; @@ -24,7 +24,7 @@ export class InboxLeaf { } static smallestIndexForCheckpoint(checkpointNumber: CheckpointNumber): bigint { - return BigInt(checkpointNumber - INITIAL_CHECKPOINT_NUMBER) * BigInt(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP); + return BigInt(checkpointNumber - INITIAL_CHECKPOINT_NUMBER) * BigInt(MAX_L1_TO_L2_MSGS_PER_CHECKPOINT); } /** @@ -33,12 +33,12 @@ export class InboxLeaf { */ static indexRangeForCheckpoint(checkpointNumber: CheckpointNumber): [bigint, bigint] { const start = this.smallestIndexForCheckpoint(checkpointNumber); - const end = start + BigInt(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP); + const end = start + BigInt(MAX_L1_TO_L2_MSGS_PER_CHECKPOINT); return [start, end]; } /** Returns the checkpoint number for a given leaf index */ static checkpointNumberFromIndex(index: bigint): CheckpointNumber { - return CheckpointNumber(Number(index / BigInt(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP)) + INITIAL_CHECKPOINT_NUMBER); + return CheckpointNumber(Number(index / BigInt(MAX_L1_TO_L2_MSGS_PER_CHECKPOINT)) + INITIAL_CHECKPOINT_NUMBER); } } diff --git a/yarn-project/stdlib/src/p2p/checkpoint_proposal.ts b/yarn-project/stdlib/src/p2p/checkpoint_proposal.ts index 126e24b56dc4..4c5f0f402927 100644 --- a/yarn-project/stdlib/src/p2p/checkpoint_proposal.ts +++ b/yarn-project/stdlib/src/p2p/checkpoint_proposal.ts @@ -110,14 +110,8 @@ export class CheckpointProposal extends Gossipable implements Signable { ) { super(); - // Check that last block properties match those of the checkpoint. - if (lastBlock && 'inHash' in lastBlock && !lastBlock.inHash.equals(checkpointHeader.inHash)) { - throw new Error( - `CheckpointProposal lastBlock inHash ${lastBlock.inHash} does not match checkpoint inHash ${checkpointHeader.inHash}`, - ); - } - // The last block's bucket reference (AZIP-22 Fast Inbox) commits to the same rolling hash as the checkpoint header, - // mirroring the inHash cross-check above. Optional pre-flip: only enforced when the reference is set. + // Check that last block properties match those of the checkpoint. The last block's bucket reference (AZIP-22 Fast + // Inbox) commits to the same rolling hash as the checkpoint header. Only enforced when the reference is set. if (lastBlock?.bucketRef && !lastBlock.bucketRef.inboxRollingHash.equals(checkpointHeader.inboxRollingHash)) { throw new Error( `CheckpointProposal lastBlock bucketRef rolling hash ${lastBlock.bucketRef.inboxRollingHash} does not match checkpoint inboxRollingHash ${checkpointHeader.inboxRollingHash}`, @@ -147,7 +141,6 @@ export class CheckpointProposal extends Gossipable implements Signable { /** * Extract a BlockProposal from the last block info. - * Uses inHash from checkpointHeader.contentCommitment.inHash */ getBlockProposal(): BlockProposal | undefined { if (!this.lastBlock) { @@ -157,7 +150,7 @@ export class CheckpointProposal extends Gossipable implements Signable { return new BlockProposal( this.lastBlock.blockHeader, this.lastBlock.indexWithinCheckpoint, - this.checkpointHeader.inHash, + Fr.ZERO, this.archive, this.lastBlock.txHashes, this.lastBlock.signature, diff --git a/yarn-project/stdlib/src/p2p/wire_compat_fixtures.ts b/yarn-project/stdlib/src/p2p/wire_compat_fixtures.ts index 61f5720243ce..ca38bc7724f0 100644 --- a/yarn-project/stdlib/src/p2p/wire_compat_fixtures.ts +++ b/yarn-project/stdlib/src/p2p/wire_compat_fixtures.ts @@ -1,13 +1,14 @@ -// Golden wire fixtures captured from the pre-Fast-Inbox serialization format (AZIP-22, A-1381). They pin the exact -// bytes a legacy peer produces and consumes, so the optional bucket-reference tail added to proposals stays wire -// compatible: an unset proposal must serialize to these bytes, and decoding these bytes must yield no bucket reference. +// Golden wire fixtures that pin the exact bytes a peer produces and consumes, so the optional bucket-reference tail +// added to proposals stays wire compatible: an unset proposal must serialize to these bytes, and decoding these bytes +// must yield no bucket reference. The block-proposal fixtures predate the bucket-reference change (AZIP-22, A-1381); +// the checkpoint-proposal fixture was refreshed for the checkpoint-header format change that dropped `inHash`. // // Both fixtures come from a deterministic proposal built with: // BlockHeader.empty(), IndexWithinCheckpoint(3), inHash=Fr(42), archiveRoot=Fr(99), // txHashes=[TxHash.fromField(Fr(7)), TxHash.fromField(Fr(8))], Signature.empty(), EMPTY_COORDINATION_SIGNATURE_CONTEXT // (checkpoint: CheckpointHeader.empty(), archive=Fr(123), feeAssetPriceModifier=0, empty signature/context, and a // lastBlock with BlockHeader.empty(), IndexWithinCheckpoint(4), txHashes=[TxHash.fromField(Fr(7))], empty signature). -// DO NOT regenerate from current code — the whole point is that these bytes predate the change. +// Regenerate the checkpoint fixture only when the checkpoint serialization format changes on purpose. /** Legacy `BlockProposal.toBuffer()` bytes (no signedTxs, no bucket reference). */ export const LEGACY_BLOCK_PROPOSAL_HEX = @@ -15,7 +16,7 @@ export const LEGACY_BLOCK_PROPOSAL_HEX = /** Legacy `CheckpointProposal.toBuffer()` bytes (lastBlock without signedTxs or bucket reference). */ export const LEGACY_CHECKPOINT_PROPOSAL_HEX = - '0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000700000000'; + '0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000700000000'; /** Legacy `BlockProposal.getPayloadToSign()` bytes (no bucket reference). */ export const LEGACY_BLOCK_PROPOSAL_PAYLOAD_HEX = diff --git a/yarn-project/stdlib/src/parity/inbox_parity_private_inputs.ts b/yarn-project/stdlib/src/parity/inbox_parity_private_inputs.ts index 70639ab9fa16..fb799887d425 100644 --- a/yarn-project/stdlib/src/parity/inbox_parity_private_inputs.ts +++ b/yarn-project/stdlib/src/parity/inbox_parity_private_inputs.ts @@ -43,8 +43,6 @@ export class InboxParityPrivateInputs { public readonly startRollingHash: Fr, /** Message-bundle sponge before this checkpoint's messages (empty at checkpoint start). */ public readonly startSponge: L1ToL2MessageSponge, - /** The L1 `in_hash` (sha256 frontier root), passed through unconstrained by the circuit. */ - public readonly inHash: Fr, /** Root of the VK tree. */ public readonly vkTreeRoot: Fr, /** Prover identity committed to by the circuit, for sybil protection. */ @@ -63,7 +61,6 @@ export class InboxParityPrivateInputs { messages: Fr[], startRollingHash: Fr, startSponge: L1ToL2MessageSponge, - inHash: Fr, vkTreeRoot: Fr, proverId: Fr, ): InboxParityPrivateInputs { @@ -75,7 +72,6 @@ export class InboxParityPrivateInputs { messages.length, startRollingHash, startSponge, - inHash, vkTreeRoot, proverId, ); @@ -89,7 +85,6 @@ export class InboxParityPrivateInputs { new Fr(this.numMessages), this.startRollingHash, this.startSponge, - this.inHash, this.vkTreeRoot, this.proverId, ); @@ -117,7 +112,6 @@ export class InboxParityPrivateInputs { reader.readObject(L1ToL2MessageSponge), Fr.fromBuffer(reader), Fr.fromBuffer(reader), - Fr.fromBuffer(reader), ); } diff --git a/yarn-project/stdlib/src/parity/parity_public_inputs.ts b/yarn-project/stdlib/src/parity/parity_public_inputs.ts index 40a9d12b48fd..2c07ab721ad1 100644 --- a/yarn-project/stdlib/src/parity/parity_public_inputs.ts +++ b/yarn-project/stdlib/src/parity/parity_public_inputs.ts @@ -8,11 +8,6 @@ import { L1ToL2MessageSponge } from '../messaging/l1_to_l2_message_sponge.js'; export class ParityPublicInputs { constructor( - /** - * The L1 `in_hash` (sha256 frontier root of the checkpoint's messages). Unconstrained pass-through: InboxParity - * echoes the value the prover supplies; it stays the authoritative L1 check until the Fast Inbox flip. - */ - public inHash: Fr, /** Inbox rolling hash before absorbing this checkpoint's messages. */ public startRollingHash: Fr, /** Inbox rolling hash after absorbing the `numMsgs` real messages. */ @@ -27,11 +22,7 @@ export class ParityPublicInputs { public vkTreeRoot: Fr, /** Prover identity committed to by the circuit, for sybil protection. */ public proverId: Fr, - ) { - if (inHash.toBuffer()[0] != 0) { - throw new Error(`inHash buffer must be 31 bytes. Got 32 bytes`); - } - } + ) {} /** * Serializes the inputs to a buffer. @@ -39,7 +30,6 @@ export class ParityPublicInputs { */ toBuffer() { return serializeToBuffer( - this.inHash, this.startRollingHash, this.endRollingHash, this.startSponge, @@ -79,7 +69,6 @@ export class ParityPublicInputs { */ static getFields(fields: FieldsOf) { return [ - fields.inHash, fields.startRollingHash, fields.endRollingHash, fields.startSponge, @@ -98,7 +87,6 @@ export class ParityPublicInputs { static fromBuffer(buffer: Buffer | BufferReader) { const reader = BufferReader.asReader(buffer); return new ParityPublicInputs( - reader.readObject(Fr), reader.readObject(Fr), reader.readObject(Fr), reader.readObject(L1ToL2MessageSponge), diff --git a/yarn-project/stdlib/src/rollup/checkpoint_header.test.ts b/yarn-project/stdlib/src/rollup/checkpoint_header.test.ts index 5c29c4ce8017..764a57fdab9c 100644 --- a/yarn-project/stdlib/src/rollup/checkpoint_header.test.ts +++ b/yarn-project/stdlib/src/rollup/checkpoint_header.test.ts @@ -22,7 +22,7 @@ describe('CheckpointHeader', () => { const header = CheckpointHeader.empty(); const hash = header.hash().toString(); - expect(hash).toMatchInlineSnapshot(`"0x0008c3e5bbea4fba57201a69d4bf70a0d255df921fdef4924c22da087f9338c2"`); + expect(hash).toMatchInlineSnapshot(`"0x002e384af86a480f952aa16443fd29646a9063865e62d7c403fc7ed697bb7712"`); // Run with AZTEC_GENERATE_TEST_DATA=1 to update noir test data updateInlineTestData( @@ -37,7 +37,6 @@ describe('CheckpointHeader', () => { lastArchiveRoot: new Fr(123), blockHeadersHash: new Fr(456), blobsHash: new Fr(77), - inHash: new Fr(88), inboxRollingHash: new Fr(89), epochOutHash: new Fr(99), slotNumber: SlotNumber(1234), @@ -50,7 +49,7 @@ describe('CheckpointHeader', () => { }); const hash = header.hash().toString(); - expect(hash).toMatchInlineSnapshot(`"0x00519b87177a8a5e4edd03f4d820aec6d402497ef1ab70e2ecd4d4c39b339611"`); + expect(hash).toMatchInlineSnapshot(`"0x00751391e842cd7b2014478255dd3309df86327197a0feb03f0af1b758f62ba5"`); // Run with AZTEC_GENERATE_TEST_DATA=1 to update noir test data updateInlineTestData( @@ -65,7 +64,6 @@ describe('CheckpointHeader', () => { lastArchiveRoot: new Fr(MAX_FIELD_VALUE - 123n), blockHeadersHash: new Fr(MAX_FIELD_VALUE - 456n), blobsHash: new Fr(MAX_FIELD_VALUE - 77n), - inHash: new Fr(MAX_FIELD_VALUE - 88n), inboxRollingHash: new Fr(MAX_FIELD_VALUE - 89n), epochOutHash: new Fr(MAX_FIELD_VALUE - 99n), slotNumber: SlotNumber(1234), @@ -81,7 +79,7 @@ describe('CheckpointHeader', () => { const hash = header.hash().toString(); - expect(hash).toMatchInlineSnapshot(`"0x00d64307fa93c32ae46a4c3b6a1911d31994daa8d11d50201f0086a6cbaa9bac"`); + expect(hash).toMatchInlineSnapshot(`"0x005bd09725c6e77a4a28a7ccdaf7875ba5882431ca3c82e62db96e8a12769ce5"`); // Run with AZTEC_GENERATE_TEST_DATA=1 to update noir test data updateInlineTestData( diff --git a/yarn-project/stdlib/src/rollup/checkpoint_header.ts b/yarn-project/stdlib/src/rollup/checkpoint_header.ts index 32cbc58874e0..bd59f7746a97 100644 --- a/yarn-project/stdlib/src/rollup/checkpoint_header.ts +++ b/yarn-project/stdlib/src/rollup/checkpoint_header.ts @@ -30,12 +30,10 @@ export class CheckpointHeader { public blockHeadersHash: Fr, /** Hash of the blobs in the checkpoint. */ public blobsHash: Fr, - /** Root of the l1 to l2 messages subtree. */ - public inHash: Fr, /** - * Inbox rolling-hash chain value after consuming all L1-to-L2 messages bundled into this checkpoint. The dual of - * `inHash` (AZIP-22 Fast Inbox): the truncated-to-field sha256 chain the L1 Inbox accumulates. Currently a - * pass-through commitment; `inHash` remains the authoritative L1 check until the Fast Inbox flip. + * Inbox rolling-hash chain value after consuming all L1-to-L2 messages bundled into this checkpoint (AZIP-22 Fast + * Inbox): the truncated-to-field sha256 chain the L1 Inbox accumulates. This is the checkpoint's only inbox + * commitment. */ public inboxRollingHash: Fr, /** @@ -68,7 +66,6 @@ export class CheckpointHeader { lastArchiveRoot: schemas.Fr, blockHeadersHash: schemas.Fr, blobsHash: schemas.Fr, - inHash: schemas.Fr, inboxRollingHash: schemas.Fr, epochOutHash: schemas.Fr, slotNumber: schemas.SlotNumber, @@ -87,7 +84,6 @@ export class CheckpointHeader { fields.lastArchiveRoot, fields.blockHeadersHash, fields.blobsHash, - fields.inHash, fields.inboxRollingHash, fields.epochOutHash, fields.slotNumber, @@ -113,7 +109,6 @@ export class CheckpointHeader { reader.readObject(Fr), reader.readObject(Fr), reader.readObject(Fr), - reader.readObject(Fr), SlotNumber(Fr.fromBuffer(reader).toNumber()), reader.readUInt64(), reader.readObject(EthAddress), @@ -129,7 +124,6 @@ export class CheckpointHeader { this.lastArchiveRoot.equals(other.lastArchiveRoot) && this.blockHeadersHash.equals(other.blockHeadersHash) && this.blobsHash.equals(other.blobsHash) && - this.inHash.equals(other.inHash) && this.inboxRollingHash.equals(other.inboxRollingHash) && this.epochOutHash.equals(other.epochOutHash) && this.slotNumber === other.slotNumber && @@ -159,7 +153,6 @@ export class CheckpointHeader { this.lastArchiveRoot, this.blockHeadersHash, this.blobsHash, - this.inHash, this.inboxRollingHash, this.epochOutHash, new Fr(this.slotNumber), @@ -181,7 +174,6 @@ export class CheckpointHeader { lastArchiveRoot: Fr.ZERO, blockHeadersHash: Fr.ZERO, blobsHash: Fr.ZERO, - inHash: Fr.ZERO, inboxRollingHash: Fr.ZERO, epochOutHash: Fr.ZERO, slotNumber: SlotNumber.ZERO, @@ -200,7 +192,6 @@ export class CheckpointHeader { lastArchiveRoot: Fr.random(), blockHeadersHash: Fr.random(), blobsHash: Fr.random(), - inHash: Fr.random(), inboxRollingHash: Fr.random(), epochOutHash: Fr.random(), slotNumber: SlotNumber(Math.floor(Math.random() * 1000) + 1), @@ -219,7 +210,6 @@ export class CheckpointHeader { this.lastArchiveRoot.isZero() && this.blockHeadersHash.isZero() && this.blobsHash.isZero() && - this.inHash.isZero() && this.inboxRollingHash.isZero() && this.epochOutHash.isZero() && this.slotNumber === 0 && @@ -249,7 +239,6 @@ export class CheckpointHeader { Fr.fromString(header.lastArchiveRoot), Fr.fromString(header.blockHeadersHash), Fr.fromString(header.blobsHash), - Fr.fromString(header.inHash), Fr.fromString(header.inboxRollingHash), Fr.fromString(header.outHash), SlotNumber.fromBigInt(header.slotNumber), @@ -275,7 +264,6 @@ export class CheckpointHeader { lastArchiveRoot: this.lastArchiveRoot.toString(), blockHeadersHash: this.blockHeadersHash.toString(), blobsHash: this.blobsHash.toString(), - inHash: this.inHash.toString(), inboxRollingHash: this.inboxRollingHash.toString(), outHash: this.epochOutHash.toString(), slotNumber: BigInt(this.slotNumber), @@ -296,7 +284,6 @@ export class CheckpointHeader { lastArchive: this.lastArchiveRoot.toString(), blockHeadersHash: this.blockHeadersHash.toString(), blobsHash: this.blobsHash.toString(), - inHash: this.inHash.toString(), inboxRollingHash: this.inboxRollingHash.toString(), epochOutHash: this.epochOutHash.toString(), slotNumber: this.slotNumber, @@ -314,7 +301,6 @@ export class CheckpointHeader { lastArchiveRoot: ${this.lastArchiveRoot.toString()}, blockHeadersHash: ${this.blockHeadersHash.toString()}, blobsHash: ${inspect(this.blobsHash)}, - inHash: ${inspect(this.inHash)}, inboxRollingHash: ${inspect(this.inboxRollingHash)}, epochOutHash: ${inspect(this.epochOutHash)}, slotNumber: ${this.slotNumber}, diff --git a/yarn-project/stdlib/src/rollup/checkpoint_root_rollup_private_inputs.ts b/yarn-project/stdlib/src/rollup/checkpoint_root_rollup_private_inputs.ts index 7838c30ee375..01f908d77221 100644 --- a/yarn-project/stdlib/src/rollup/checkpoint_root_rollup_private_inputs.ts +++ b/yarn-project/stdlib/src/rollup/checkpoint_root_rollup_private_inputs.ts @@ -119,8 +119,8 @@ export class CheckpointRootRollupPrivateInputs { RollupHonkProofData, ], /** - * Inbox parity proof over the checkpoint's L1-to-L2 messages (AZIP-22 Fast Inbox): it commits to the same message - * list behind the L1-checked `inHash`, and its message sponge is checked against the blocks' accumulated one. + * Inbox parity proof over the checkpoint's L1-to-L2 messages (AZIP-22 Fast Inbox): it commits to the checkpoint's + * message list, and its message sponge is checked against the blocks' accumulated one. */ public inboxParity: UltraHonkProofData, public hints: CheckpointRootRollupHints, @@ -168,8 +168,8 @@ export class CheckpointRootSingleBlockRollupPrivateInputs { constructor( public previousRollup: RollupHonkProofData, /** - * Inbox parity proof over the checkpoint's L1-to-L2 messages (AZIP-22 Fast Inbox): it commits to the same message - * list behind the L1-checked `inHash`, and its message sponge is checked against the block's accumulated one. + * Inbox parity proof over the checkpoint's L1-to-L2 messages (AZIP-22 Fast Inbox): it commits to the checkpoint's + * message list, and its message sponge is checked against the block's accumulated one. */ public inboxParity: UltraHonkProofData, public hints: CheckpointRootRollupHints, diff --git a/yarn-project/stdlib/src/tests/factories.ts b/yarn-project/stdlib/src/tests/factories.ts index 06f5fcdd5e00..20b8ca8a0af0 100644 --- a/yarn-project/stdlib/src/tests/factories.ts +++ b/yarn-project/stdlib/src/tests/factories.ts @@ -17,6 +17,7 @@ import { MAX_ENQUEUED_CALLS_PER_TX, MAX_KEY_VALIDATION_REQUESTS_PER_CALL, MAX_L1_TO_L2_MSGS_PER_BLOCK, + MAX_L1_TO_L2_MSGS_PER_CHECKPOINT, MAX_L2_TO_L1_MSGS_PER_CALL, MAX_L2_TO_L1_MSGS_PER_TX, MAX_NOTE_HASHES_PER_CALL, @@ -36,7 +37,6 @@ import { NOTE_HASH_SUBTREE_ROOT_SIBLING_PATH_LENGTH, NULLIFIER_SUBTREE_ROOT_SIBLING_PATH_LENGTH, NULLIFIER_TREE_HEIGHT, - NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, PRIVATE_LOG_SIZE_IN_FIELDS, PUBLIC_DATA_TREE_HEIGHT, RECURSIVE_ROLLUP_HONK_PROOF_LENGTH, @@ -821,7 +821,6 @@ export function makeCheckpointRollupPublicInputs(seed = 0) { export function makeParityPublicInputs(seed = 0): ParityPublicInputs { return new ParityPublicInputs( - new Fr(BigInt(seed + 0x200)), new Fr(BigInt(seed + 0x400)), new Fr(BigInt(seed + 0x500)), makeL1ToL2MessageSponge(seed + 0x550), @@ -842,7 +841,6 @@ export function makeInboxParityPrivateInputs(seed = 0): InboxParityPrivateInputs numMsgs, new Fr(seed + 0x3500), makeL1ToL2MessageSponge(seed + 0x3800), - new Fr(seed + 0x3900), new Fr(seed + 0x4000), new Fr(seed + 0x5000), ); @@ -888,7 +886,6 @@ export function makeCheckpointHeader(seed = 0, overrides: Partial { const archive = Fr.random(); const checkpointHeader = makeCheckpointHeader(1); - // Create the block proposal first (as the sequencer would), using the checkpoint's inHash - // so that getSender() can verify the block proposal sender matches + // Create the block proposal first (as the sequencer would) so that getSender() can verify the block proposal + // sender matches. The block-level inHash is dead post-flip (AZIP-22 Fast Inbox), so it carries zero. const blockProposal = await service.createBlockProposal( blockHeader, CheckpointNumber(1), indexWithinCheckpoint, - checkpointHeader.inHash, + Fr.ZERO, archive, txs, addresses[0], diff --git a/yarn-project/validator-client/src/validator.test.ts b/yarn-project/validator-client/src/validator.test.ts index 1af680ff3218..0a0ae849ea4a 100644 --- a/yarn-project/validator-client/src/validator.test.ts +++ b/yarn-project/validator-client/src/validator.test.ts @@ -593,7 +593,7 @@ describe('ValidatorClient', () => { const emptyInHash = computeInHashFromL1ToL2Messages([]); const checkpointProposal = await makeCheckpointProposal({ signer, - checkpointHeader: makeCheckpointHeader(1, { slotNumber: proposal.slotNumber, inHash: emptyInHash }), + checkpointHeader: makeCheckpointHeader(1, { slotNumber: proposal.slotNumber }), archiveRoot: Fr.random(), lastBlock: { blockHeader: makeBlockHeader(1, { blockNumber, slotNumber: proposal.slotNumber }), @@ -669,9 +669,8 @@ describe('ValidatorClient', () => { validatorClient.updateConfig({ skipPushProposedBlocksToArchiver: false }); validatorClient.getProposalHandler().register(p2pClient, true); - const emptyInHash = computeInHashFromL1ToL2Messages([]); const checkpointProposal = await makeCheckpointProposal({ - checkpointHeader: makeCheckpointHeader(1, { slotNumber: proposal.slotNumber, inHash: emptyInHash }), + checkpointHeader: makeCheckpointHeader(1, { slotNumber: proposal.slotNumber }), archiveRoot: Fr.random(), lastBlock: { blockHeader: makeBlockHeader(1, { blockNumber, slotNumber: proposal.slotNumber }), @@ -680,7 +679,7 @@ describe('ValidatorClient', () => { }, }); const equivocatedCheckpointProposal = await makeCheckpointProposal({ - checkpointHeader: makeCheckpointHeader(1, { slotNumber: proposal.slotNumber, inHash: emptyInHash }), + checkpointHeader: makeCheckpointHeader(1, { slotNumber: proposal.slotNumber }), archiveRoot: Fr.random(), lastBlock: { blockHeader: makeBlockHeader(1, { blockNumber, slotNumber: proposal.slotNumber }), diff --git a/yarn-project/world-state/src/native/native_world_state.test.ts b/yarn-project/world-state/src/native/native_world_state.test.ts index aeceac469d4e..e205170c8676 100644 --- a/yarn-project/world-state/src/native/native_world_state.test.ts +++ b/yarn-project/world-state/src/native/native_world_state.test.ts @@ -1,12 +1,12 @@ import { ARCHIVE_HEIGHT, L1_TO_L2_MSG_TREE_HEIGHT, + MAX_L1_TO_L2_MSGS_PER_CHECKPOINT, MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, NOTE_HASH_TREE_HEIGHT, NULLIFIER_TREE_HEIGHT, - NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, PUBLIC_DATA_TREE_HEIGHT, } from '@aztec/constants'; import { BlockNumber, SlotNumber } from '@aztec/foundation/branded-types'; @@ -170,7 +170,7 @@ describe('NativeWorldState', () => { const status = await ws.handleL2BlockAndMessages(block, messages); - // Non-first blocks append their bundle exactly as given (no padding to NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP). + // Non-first blocks append their bundle exactly as given (no padding to MAX_L1_TO_L2_MSGS_PER_CHECKPOINT). expect(status.meta.messageTreeMeta.size).toBe(BigInt(numMessages)); }); }); @@ -1508,8 +1508,8 @@ describe('NativeWorldState', () => { expect(status.meta.messageTreeMeta).toMatchObject({ depth: L1_TO_L2_MSG_TREE_HEIGHT, - size: BigInt(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP * (i + 1)), - committedSize: BigInt(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP * (i + 1)), + size: BigInt(MAX_L1_TO_L2_MSGS_PER_CHECKPOINT * (i + 1)), + committedSize: BigInt(MAX_L1_TO_L2_MSGS_PER_CHECKPOINT * (i + 1)), initialSize: BigInt(0), oldestHistoricBlock: 1, unfinalizedBlockHeight: i + 1, diff --git a/yarn-project/world-state/src/test/utils.ts b/yarn-project/world-state/src/test/utils.ts index 8870091b3951..4ce5c907bc44 100644 --- a/yarn-project/world-state/src/test/utils.ts +++ b/yarn-project/world-state/src/test/utils.ts @@ -1,8 +1,8 @@ import { + MAX_L1_TO_L2_MSGS_PER_CHECKPOINT, MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX, NULLIFIER_SUBTREE_HEIGHT, - NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, } from '@aztec/constants'; import { asyncMap } from '@aztec/foundation/async-map'; import { BlockNumber, type CheckpointNumber, IndexWithinCheckpoint } from '@aztec/foundation/branded-types'; @@ -78,7 +78,7 @@ export function mockBlock( size: number, fork: MerkleTreeWriteOperations, maxEffects: number | undefined = 1000, // Defaults to the maximum tx effects. - numL1ToL2Messages: number = NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, + numL1ToL2Messages: number = MAX_L1_TO_L2_MSGS_PER_CHECKPOINT, isFirstBlockInCheckpoint: boolean = true, ) { return mockBlockWithIndex(blockNum, isFirstBlockInCheckpoint ? 0 : 1, size, fork, numL1ToL2Messages, maxEffects); @@ -94,7 +94,7 @@ export async function mockBlockWithIndex( indexWithinCheckpoint: number, size: number, fork: MerkleTreeWriteOperations, - numL1ToL2Messages: number = NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, + numL1ToL2Messages: number = MAX_L1_TO_L2_MSGS_PER_CHECKPOINT, maxEffects: number | undefined = 1000, // Defaults to the maximum tx effects. ) { const block = await L2Block.random(blockNum, { diff --git a/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts b/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts index 1dabb0aacd30..f1cb5cb06f70 100644 --- a/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts +++ b/yarn-project/world-state/src/world-state-db/merkle_tree_db.ts @@ -34,7 +34,7 @@ export interface MerkleTreeAdminDatabase extends ForkMerkleTreeOperations, Reado * Handles a single L2 block: inserts its note hashes, nullifiers, public data writes, and the block's L1-to-L2 * message bundle into the merkle trees. Any block may carry a message bundle and transition the L1-to-L2 message * tree, not just the first block of a checkpoint. A first-in-checkpoint bundle is padded to - * NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP to match how the circuits build the tree; a non-first bundle is appended + * MAX_L1_TO_L2_MSGS_PER_CHECKPOINT to match how the circuits build the tree; a non-first bundle is appended * exactly as given. Padding is a transitional concern of this method that moves entirely to the caller at the flip. * @param block - The L2 block to handle. * @param l1ToL2Messages - The L1 to L2 messages for the block.