diff --git a/barretenberg/.claude/skills/gate-counts/SKILL.md b/barretenberg/.claude/skills/gate-counts/SKILL.md index 688ed27cea44..82f4e64ac8db 100644 --- a/barretenberg/.claude/skills/gate-counts/SKILL.md +++ b/barretenberg/.claude/skills/gate-counts/SKILL.md @@ -209,40 +209,42 @@ Then retry `bb gates`. The committed `Prover.toml` files carry fake recursive proofs whose length is frozen at the value present when they were last generated. After a proof-length change they must be regenerated (or hacked — see below). Two commands cover all -protocol circuits: +protocol circuits, split by whether the sample needs a real client-proved +transaction. -**Kernel circuits + most rollup circuits** (spins up an L1/anvil sandbox): +**Block-root and above rollup circuits — prover-client suite** (simulated +orchestrator, no L1 sandbox): ```bash -AZTEC_GENERATE_TEST_DATA=1 FAKE_PROOFS=1 \ - yarn workspace @aztec/end-to-end test:e2e e2e_prover/full.test +AZTEC_GENERATE_TEST_DATA=1 \ + yarn workspace @aztec/prover-client test regenerate_rollup_sample_inputs ``` -Regenerates `Prover.toml` for: `private-kernel-init` and its `private-kernel-init-N` -variants, `private-kernel-inner` and its `private-kernel-inner-N` variants, -`private-kernel-reset`, `private-kernel-reset-tail`, -`private-kernel-reset-tail-to-public`, `rollup-tx-base-private`, -`rollup-tx-base-public`, `rollup-block-root`, -`rollup-block-root-single-tx`, `rollup-checkpoint-root-single-block`, -`rollup-checkpoint-merge`, `rollup-root`. +Regenerates `Prover.toml` for `rollup-block-root-first-empty-tx`, +`rollup-block-root-first`, `rollup-block-root-first-single-tx`, +`rollup-block-root`, `rollup-block-root-single-tx`, `rollup-block-root-msgs-only`, +`rollup-block-merge`, `rollup-checkpoint-root`, `rollup-checkpoint-root-single-block`, +`rollup-checkpoint-merge`, `rollup-tx-merge`, `rollup-root`. -**Rollup circuits NOT covered by `full.test`** (incl. `rollup-tx-merge`): +**Private-kernel + transaction-base circuits — e2e prover full test** (spins up +an L1/anvil sandbox): ```bash AZTEC_GENERATE_TEST_DATA=1 FAKE_PROOFS=1 \ - yarn workspace @aztec/prover-client test src/orchestrator/orchestrator_single_checkpoint.test.ts + yarn workspace @aztec/end-to-end test:e2e single-node/prover/server/full.test ``` -Regenerates `Prover.toml` for `rollup-tx-merge`, `rollup-block-root-no-txs`, -`rollup-block-merge`, `rollup-checkpoint-root` (multi-block checkpoint test). - -> **`rollup-tx-merge`'s `Prover.toml` is regenerated by the prover-client -> command, NOT `full.test`** — it is commented out in `full.test`'s circuit -> list. Use the prover-client command for the tx-merge circuit. +Regenerates `Prover.toml` for: `private-kernel-init` and its `private-kernel-init-N` +variants, `private-kernel-inner` and its `private-kernel-inner-N` variants, +`private-kernel-reset-tail`, `private-kernel-reset-tail-to-public`, +`rollup-tx-base-private`, `rollup-tx-base-public`. These need real client-proved +transactions the simulated orchestrator cannot produce. (`private-kernel-reset`, +the inner reset, is commented out and hand-maintained — not regenerated.) The circuit lists live in the `updateProtocolCircuitSampleInputs(...)` loops in -`yarn-project/end-to-end/src/e2e_prover/full.test.ts` and -`yarn-project/prover-client/src/orchestrator/orchestrator_single_checkpoint.test.ts`; +`yarn-project/end-to-end/src/single-node/prover/server/full.test.ts` and the +`scenarios` array in +`yarn-project/prover-client/src/test/regenerate_rollup_sample_inputs.test.ts`; re-read them if a circuit seems missing (entries get commented in/out). ### Build prerequisites for regeneration diff --git a/barretenberg/.claude/skills/update-prover-toml/SKILL.md b/barretenberg/.claude/skills/update-prover-toml/SKILL.md index 4f6f7124fea9..0298d57ff1ce 100644 --- a/barretenberg/.claude/skills/update-prover-toml/SKILL.md +++ b/barretenberg/.claude/skills/update-prover-toml/SKILL.md @@ -1,6 +1,6 @@ --- name: update-prover-toml -description: Regenerate the protocol-circuit sample Prover.toml files (the witnesses CI feeds to `nargo execute`). Covers which e2e test/commands regenerate which tomls, and the one exception — private-kernel-reset/Prover.toml (the inner reset) — which is NOT generated by the test at all and is maintained entirely by hand. Use when a `nargo execute` CI check fails on a crates/*/Prover.toml, after changing protocol-circuit inputs/constants, or when asked to refresh/regenerate Prover.toml sample inputs. +description: Regenerate the protocol-circuit sample Prover.toml files (the witnesses CI feeds to `nargo execute`). Covers which of the two commands regenerates which tomls — the prover-client `regenerate_rollup_sample_inputs` suite for the block-root and above rollup circuits, and the e2e prover full test for the private-kernel and transaction-base circuits — and the one exception, private-kernel-reset/Prover.toml (the inner reset), which is NOT generated by the test at all and is maintained entirely by hand. Use when a `nargo execute` CI check fails on a crates/*/Prover.toml, after changing protocol-circuit inputs/constants, or when asked to refresh/regenerate Prover.toml sample inputs. --- # Updating protocol-circuit Prover.toml files @@ -21,39 +21,40 @@ The tomls are captured from a real end-to-end run, not written by hand (except t ## How regeneration works -`yarn-project/end-to-end/src/e2e_prover/full.test.ts` has a test "generates sample Prover.toml files if generate test data is on". During proving, circuit inputs are captured via `pushTestData`/`getTestData`; the test then calls `updateProtocolCircuitSampleInputs(circuitName, TOML.stringify(...))` (`yarn-project/foundation/src/testing/files/index.ts`), which writes `noir-projects/fnd/noir-protocol-circuits/crates//Prover.toml`. +Two commands cover the protocol-circuit tomls, split by whether the sample needs a real client-proved transaction. Both capture circuit inputs during proving via `pushTestData`/`getTestData` and write them with `updateProtocolCircuitSampleInputs(circuitName, TOML.stringify(...))` (`yarn-project/foundation/src/testing/files/index.ts`), which writes `noir-projects/fnd/noir-protocol-circuits/crates//Prover.toml`. -The test early-returns unless **both** are true: `AZTEC_GENERATE_TEST_DATA=1` (enables `isGenerateTestDataEnabled()`) and fake proofs (`FAKE_PROOFS=1`, i.e. `REAL_PROOFS` is false). +### Block-root and above rollup circuits — prover-client suite -### Command - -From the repo root: +`yarn-project/prover-client/src/test/regenerate_rollup_sample_inputs.test.ts` drives representative epochs through the simulated orchestrator and dumps each rollup circuit's captured input. The whole suite is `describe.skip`ped unless `AZTEC_GENERATE_TEST_DATA=1`, and it needs no L1 sandbox. ```bash -AZTEC_GENERATE_TEST_DATA=1 FAKE_PROOFS=1 yarn-project/end-to-end/scripts/run_test.sh simple e2e_prover/full +AZTEC_GENERATE_TEST_DATA=1 yarn workspace @aztec/prover-client test regenerate_rollup_sample_inputs ``` -(Equivalently, from `yarn-project/end-to-end`: `AZTEC_GENERATE_TEST_DATA=1 FAKE_PROOFS=1 yarn test:e2e e2e_prover/full.test`.) This is a full-stack run (L1 anvil + node + prover), ~15 min with fake proofs. +Regenerates: `rollup-block-root-first-empty-tx`, `rollup-block-root-first`, `rollup-block-root-first-single-tx`, `rollup-block-root`, `rollup-block-root-single-tx`, `rollup-block-root-msgs-only`, `rollup-block-merge`, `rollup-checkpoint-root`, `rollup-checkpoint-root-single-block`, `rollup-checkpoint-merge`, `rollup-tx-merge`, `rollup-root`. The scenario list lives in the `scenarios` array in that test; each scenario's `dump` field names the tomls it owns. -After it finishes, review and stage the changed tomls with `git diff noir-projects/fnd/noir-protocol-circuits/crates/*/Prover.toml`. +### Private-kernel and transaction-base circuits — e2e prover full test -### Which tomls this regenerates +`full.test.ts` has a test "generates sample Prover.toml files if generate test data is on". It early-returns unless **both** `AZTEC_GENERATE_TEST_DATA=1` (enables `isGenerateTestDataEnabled()`) and fake proofs (`FAKE_PROOFS=1`, i.e. `REAL_PROOFS` is false) are set. These circuits need real client-proved transactions, which the simulated orchestrator suite above cannot produce. -The circuit list lives in the `forEach(circuitName => ...)` loop in `full.test.ts`. Currently active (regenerated by the run above): +From the repo root: + +```bash +AZTEC_GENERATE_TEST_DATA=1 FAKE_PROOFS=1 yarn-project/end-to-end/scripts/run_test.sh simple single-node/prover/server/full +``` + +(Equivalently, from `yarn-project/end-to-end`: `AZTEC_GENERATE_TEST_DATA=1 FAKE_PROOFS=1 yarn test:e2e single-node/prover/server/full.test`.) This is a full-stack run (L1 anvil + node + prover), ~15 min with fake proofs. + +Regenerates: - `private-kernel-init`, `private-kernel-init-2` … `private-kernel-init-5` - `private-kernel-inner`, `private-kernel-inner-2` … `private-kernel-inner-5` - `private-kernel-reset-tail`, `private-kernel-reset-tail-to-public` (the terminal resets — these run as part of every tx's tail) - `rollup-tx-base-private`, `rollup-tx-base-public` -- `rollup-block-root`, `rollup-block-root-single-tx` -- `rollup-checkpoint-root-single-block`, `rollup-checkpoint-merge` -- `rollup-root` - -`private-kernel-reset` (the **inner** reset) is deliberately **commented out** of this list and is **not** generated by the test — see the exception below. -Other entries commented out (e.g. `rollup-tx-merge`, `rollup-block-root-no-txs`, `rollup-block-merge`, `rollup-checkpoint-root`) are also not produced by this flow; the inline comment points to the orchestrator single-checkpoint test for those. +The circuit list lives in the `forEach(circuitName => ...)` loop in `full.test.ts`. `private-kernel-reset` (the **inner** reset) is deliberately **commented out** of this list and is **not** generated by the test — see the exception below. -If you add a new circuit crate, add its name to that list so its toml gets generated. +After either run, review and stage the changed tomls with `git diff noir-projects/fnd/noir-protocol-circuits/crates/*/Prover.toml`. If you add a new circuit crate, add its name to the appropriate list so its toml gets generated. ## ⚠️ Exception: `private-kernel-reset/Prover.toml` (inner reset) is NOT generated — hand-maintain it diff --git a/barretenberg/cpp/CLAUDE.md b/barretenberg/cpp/CLAUDE.md index bc27c5c261cc..a31f266ece4e 100644 --- a/barretenberg/cpp/CLAUDE.md +++ b/barretenberg/cpp/CLAUDE.md @@ -139,21 +139,21 @@ run the generation commands above. Proof-length-affecting changes (e.g. `CHONK_PROOF_LENGTH` bumps from MegaFlavor entity additions) make the committed `Prover.toml` fixtures stale. `nargo execute --program-dir ` then fails with `Type Array { length: N, typ: Field } is expected to have length N but value Vec(...)`. -Regenerate via the e2e prover full test with fake proofs: +Regeneration is split across two commands. The block-root, block-merge, checkpoint, tx-merge, and root rollup samples come from the prover-client suite, which drives the simulated orchestrator and needs no L1 sandbox: ```bash -cd yarn-project -AZTEC_GENERATE_TEST_DATA=1 FAKE_PROOFS=1 yarn workspace @aztec/end-to-end test e2e_prover/full.test +AZTEC_GENERATE_TEST_DATA=1 yarn workspace @aztec/prover-client test regenerate_rollup_sample_inputs ``` -`FAKE_PROOFS=1` skips real proving — runs in ~2 min (orchestrator + witness generation only). Writes 12 `Prover.toml` files under `noir-projects/fnd/noir-protocol-circuits/crates//Prover.toml`. - -For circuits not exercised by `full.test.ts` (`rollup-tx-merge`, `rollup-block-root-no-txs`, `rollup-block-merge`, `rollup-checkpoint-root`), additionally run: +The private-kernel samples and the transaction-base rollup samples (`rollup-tx-base-private`, `rollup-tx-base-public`) need real client-proved transactions, so they come from the e2e prover full test with fake proofs: ```bash -AZTEC_GENERATE_TEST_DATA=1 yarn workspace @aztec/prover-client test orchestrator_single_checkpoint +cd yarn-project +AZTEC_GENERATE_TEST_DATA=1 FAKE_PROOFS=1 yarn workspace @aztec/end-to-end test single-node/prover/server/full.test ``` +`FAKE_PROOFS=1` skips real proving. Both write into `noir-projects/fnd/noir-protocol-circuits/crates//Prover.toml`. + Verify with `nargo execute --program-dir noir-projects/fnd/noir-protocol-circuits/crates/` for any previously-failing crate; should print `Circuit witness successfully solved`. ## Verification Keys diff --git a/docs/docs-developers/docs/foundational-topics/ethereum-aztec-messaging/inbox.md b/docs/docs-developers/docs/foundational-topics/ethereum-aztec-messaging/inbox.md index fcf43a73c32c..d3d818b629c5 100644 --- a/docs/docs-developers/docs/foundational-topics/ethereum-aztec-messaging/inbox.md +++ b/docs/docs-developers/docs/foundational-topics/ethereum-aztec-messaging/inbox.md @@ -35,34 +35,10 @@ These functions allow you to query the current state of the Inbox. | Function | Returns | Description | | -------------------------- | ----------------- | ------------------------------------------------ | -| `getRoot(uint256)` | `bytes32` | Returns the root of a message tree for a given checkpoint number. | -| `getState()` | `InboxState` | Returns the current inbox state (rolling hash, total messages inserted, in-progress checkpoint). | +| `getState()` | `InboxState` | Returns the current inbox state (rolling hash, total messages inserted). | | `getTotalMessagesInserted()` | `uint64` | Returns the total number of messages inserted into the inbox. | -| `getInProgress()` | `uint64` | Returns the checkpoint number currently being filled. | | `getFeeAssetPortal()` | `address` | Returns the address of the Fee Juice portal. | -## Internal functions - -:::note -The following functions are only callable by the Rollup contract and are documented here for completeness. -::: - -### `consume()` - -Consumes a message tree for a given checkpoint number. - -#include_code consume l1-contracts/src/core/interfaces/messagebridge/IInbox.sol solidity - -| Name | Type | Description | -| ----------- | --------- | ---------------------------------------- | -| _toConsume | `uint256` | The checkpoint number to consume. | -| ReturnValue | `bytes32` | The root of the consumed message tree. | - -#### Edge cases - -- Will revert with `Inbox__Unauthorized()` if `msg.sender != ROLLUP`. -- Will revert with `Inbox__MustBuildBeforeConsume()` if trying to consume a checkpoint that hasn't been built yet. - ## Related pages - [Outbox](./outbox.md) - L2 to L1 message passing diff --git a/docs/docs-developers/docs/tutorials/js_tutorials/aave_bridge.md b/docs/docs-developers/docs/tutorials/js_tutorials/aave_bridge.md index 75fd938ae815..1972200094da 100644 --- a/docs/docs-developers/docs/tutorials/js_tutorials/aave_bridge.md +++ b/docs/docs-developers/docs/tutorials/js_tutorials/aave_bridge.md @@ -96,7 +96,7 @@ sequenceDiagram Portal->>Aave: withdraw(aTokenAmount) Aave-->>Portal: underlying + yield Portal->>Inbox: sendL2Message(content) - Note over Inbox: Wait for 2 L2 blocks (required for L1→L2 message availability) + Note over Inbox: Wait for an L2 block to include the message (12-30s) User->>Bridge: claim_public(amount_with_yield) Bridge->>Inbox: consume_l1_to_l2_message Bridge->>Token: mint_to_public(user, amount_with_yield) @@ -441,7 +441,7 @@ Extract the message leaf index: #include_code get_claim_leaf_index /docs/examples/ts/aave_bridge/index.ts typescript -On the local network, L2 blocks are only produced when transactions are submitted. L1-to-L2 messages require 2 L2 blocks before they can be consumed on L2. This utility deploys two dummy contracts (with random salts for unique addresses) to force block production. On devnet or testnet, blocks are produced continuously and this step is unnecessary: +On the local network, L2 blocks are only produced when transactions are submitted. An L1-to-L2 message can only be consumed once an L2 block includes it, and the network waits until the message is at least 12 seconds old before including it. This utility deploys two dummy contracts (with random salts for unique addresses) to force block production. On devnet or testnet, blocks are produced continuously and this step is unnecessary: #include_code mine_blocks /docs/examples/ts/aave_bridge/index.ts typescript @@ -501,7 +501,7 @@ If `claim_public` reverts, ensure you called `set_minter(l2Bridge.address, true) ### L1→L2 message not found — claim reverts after mining blocks -L1-to-L2 messages need 2 L2 blocks after the L1 transaction before they become consumable. Make sure `mine2Blocks` runs before the claim. If the issue persists, verify the `messageLeafIndex` extracted from the `MessageSent` event is correct. +An L1-to-L2 message becomes consumable once an L2 block includes it, which takes 12 to 30 seconds after the L1 transaction. Make sure `mine2Blocks` runs before the claim. If the issue persists, verify the `messageLeafIndex` extracted from the `MessageSent` event is correct. ## Next Steps diff --git a/docs/docs-developers/docs/tutorials/js_tutorials/token_bridge.md b/docs/docs-developers/docs/tutorials/js_tutorials/token_bridge.md index b5855967ec7e..880643436470 100644 --- a/docs/docs-developers/docs/tutorials/js_tutorials/token_bridge.md +++ b/docs/docs-developers/docs/tutorials/js_tutorials/token_bridge.md @@ -437,15 +437,15 @@ To bridge, first approve the portal address to transfer the NFT, then transfer i #include_code deposit_to_aztec /docs/examples/ts/token_bridge/index.ts typescript -The `Inbox` contract will emit an important log: `MessageSent(inProgress, index, leaf, updatedRollingHash);`. This log provides the **leaf index** of the message in the [L1-L2 Message Tree](../../foundational-topics/ethereum-aztec-messaging/index.md)—the location of the message in the tree that will appear on L2. You need this index, plus the secret, to correctly claim and decrypt the message. +The `Inbox` contract will emit an important log: `MessageSent(index, hash, inboxRollingHash, bucketSeq);`. Its first field is the **leaf index** of the message in the [L1-L2 Message Tree](../../foundational-topics/ethereum-aztec-messaging/index.md)—the location of the message in the tree that will appear on L2. You need this index, plus the secret, to correctly claim and decrypt the message. Use viem to extract this information: #include_code get_message_leaf_index /docs/examples/ts/token_bridge/index.ts typescript -This extracts the logs from the deposit and retrieves the leaf index. You can now claim it on L2. However, for security reasons, at least 2 blocks must pass before a message can be claimed on L2. If you called `claim` on the L2 contract immediately, it would return "no message available". +This extracts the logs from the deposit and retrieves the leaf index. You can now claim it on L2. However, a message can only be claimed once an L2 block includes it, and the network waits until the message is at least 12 seconds old before including it, so expect 12 to 30 seconds of latency. If you called `claim` on the L2 contract immediately, it would return "no message available". -Add a utility function to mine two blocks (it deploys a contract with a random salt): +On a local network blocks are only produced when transactions are submitted, so add a utility function that forces a couple of blocks (it deploys a contract with a random salt): #include_code mine_blocks /docs/examples/ts/token_bridge/index.ts typescript diff --git a/docs/docs-developers/docs/tutorials/js_tutorials/uniswap_swap.md b/docs/docs-developers/docs/tutorials/js_tutorials/uniswap_swap.md index 0aadfdbcda16..537605723c49 100644 --- a/docs/docs-developers/docs/tutorials/js_tutorials/uniswap_swap.md +++ b/docs/docs-developers/docs/tutorials/js_tutorials/uniswap_swap.md @@ -309,7 +309,7 @@ Bridge WETH from L1 to L2: When depositing from L1 to L2, we use a secret/secret-hash pattern: generate a random secret on the client, send only the hash to L1 (in the deposit transaction), then later reveal the secret on L2 to claim the tokens. This prevents **front-running attacks**: a malicious sequencer (the node that orders and processes L2 transactions) cannot observe the L1 deposit and claim the tokens themselves because they don't know the secret. Only someone who knows the preimage can claim. ::: -Before claiming, we need to mine 2 L2 blocks. L1-to-L2 messages are not available in the same block they are sent -- the rollup must first include them in an L2 block, and then one more block must pass before the message can be consumed. We use a helper that deploys throwaway contracts to force these blocks: +Before claiming, we need to mine a couple of L2 blocks. An L1-to-L2 message is not available the moment it is sent -- the rollup only includes it in an L2 block once it is at least 12 seconds old, and it becomes consumable as soon as that block lands. We use a helper that deploys throwaway contracts to force these blocks: #include_code mine_blocks /docs/examples/ts/example_swap/index.ts typescript diff --git a/docs/docs-developers/docs/tutorials/testing_governance_rollup_upgrade.md b/docs/docs-developers/docs/tutorials/testing_governance_rollup_upgrade.md index c6344662f839..a31f879a901d 100644 --- a/docs/docs-developers/docs/tutorials/testing_governance_rollup_upgrade.md +++ b/docs/docs-developers/docs/tutorials/testing_governance_rollup_upgrade.md @@ -89,7 +89,6 @@ export AZTEC_EPOCH_DURATION=16 export AZTEC_TARGET_COMMITTEE_SIZE=48 export AZTEC_LAG_IN_EPOCHS_FOR_VALIDATOR_SET=2 export AZTEC_LAG_IN_EPOCHS_FOR_RANDAO=2 -export AZTEC_INBOX_LAG=2 export AZTEC_PROOF_SUBMISSION_EPOCHS=2 export AZTEC_LOCAL_EJECTION_THRESHOLD=0 export AZTEC_SLASHING_ROUND_SIZE_IN_EPOCHS=1 diff --git a/docs/docs-operate/operators/reference/node-api-reference.md b/docs/docs-operate/operators/reference/node-api-reference.md index 084abb36b32b..e5264253cb1f 100644 --- a/docs/docs-operate/operators/reference/node-api-reference.md +++ b/docs/docs-operate/operators/reference/node-api-reference.md @@ -635,22 +635,24 @@ curl -X POST http://localhost:8080 \ -d '{"jsonrpc":"2.0","method":"aztec_getL1ToL2MessageMembershipWitness","params":["latest","0x1234..."],"id":1}' ``` -### aztec_getL1ToL2MessageCheckpoint +### aztec_getL1ToL2MessageIndex -Returns the L2 checkpoint number in which this L1 to L2 message becomes available, or undefined if not found. +Returns the compact leaf index assigned to this L1 to L2 message as soon as the node has ingested it from L1, +before any L2 block consumes it. Returns undefined if the node has not yet seen the message. The message becomes +consumable once a block's L1-to-L2 message tree grows past this index. **Parameters**: 1. `l1ToL2Message` - `Fr` -**Returns**: `number | undefined` +**Returns**: `bigint | undefined` **Example**: ```bash curl -X POST http://localhost:8080 \ -H 'Content-Type: application/json' \ - -d '{"jsonrpc":"2.0","method":"aztec_getL1ToL2MessageCheckpoint","params":["0x1234..."],"id":1}' + -d '{"jsonrpc":"2.0","method":"aztec_getL1ToL2MessageIndex","params":["0x1234..."],"id":1}' ``` ### aztec_getL2ToL1MembershipWitness diff --git a/docs/examples/ts/aave_bridge/index.ts b/docs/examples/ts/aave_bridge/index.ts index db53401fcc00..725332ac4bca 100644 --- a/docs/examples/ts/aave_bridge/index.ts +++ b/docs/examples/ts/aave_bridge/index.ts @@ -347,10 +347,8 @@ const INBOX_ABI = [ type: "event", name: "MessageSent", inputs: [ - { name: "checkpointNumber", type: "uint256", indexed: true }, { name: "index", type: "uint256", indexed: false }, { name: "hash", type: "bytes32", indexed: true }, - { name: "rollingHash", type: "bytes16", indexed: false }, { name: "inboxRollingHash", type: "bytes32", indexed: false }, { name: "bucketSeq", type: "uint256", indexed: false }, ], diff --git a/docs/examples/ts/example_swap/index.ts b/docs/examples/ts/example_swap/index.ts index 099c8cea3878..87b75160f49d 100644 --- a/docs/examples/ts/example_swap/index.ts +++ b/docs/examples/ts/example_swap/index.ts @@ -259,10 +259,8 @@ const INBOX_ABI = [ type: "event", name: "MessageSent", inputs: [ - { name: "checkpointNumber", type: "uint256", indexed: true }, { name: "index", type: "uint256", indexed: false }, { name: "hash", type: "bytes32", indexed: true }, - { name: "rollingHash", type: "bytes16", indexed: false }, { name: "inboxRollingHash", type: "bytes32", indexed: false }, { name: "bucketSeq", type: "uint256", indexed: false }, ], diff --git a/docs/examples/ts/token_bridge/index.ts b/docs/examples/ts/token_bridge/index.ts index 02f8976bf7e7..ee68871013a2 100644 --- a/docs/examples/ts/token_bridge/index.ts +++ b/docs/examples/ts/token_bridge/index.ts @@ -165,10 +165,8 @@ const INBOX_ABI = [ type: "event", name: "MessageSent", inputs: [ - { name: "checkpointNumber", type: "uint256", indexed: true }, { name: "index", type: "uint256", indexed: false }, { name: "hash", type: "bytes32", indexed: true }, - { name: "rollingHash", type: "bytes16", indexed: false }, { name: "inboxRollingHash", type: "bytes32", indexed: false }, { name: "bucketSeq", type: "uint256", indexed: false }, ], diff --git a/docs/scripts/node_api_reference_generation/generate_node_api_reference.ts b/docs/scripts/node_api_reference_generation/generate_node_api_reference.ts index 73ad56bbcdf1..3a4c1b864f00 100644 --- a/docs/scripts/node_api_reference_generation/generate_node_api_reference.ts +++ b/docs/scripts/node_api_reference_generation/generate_node_api_reference.ts @@ -559,7 +559,7 @@ const METHOD_GROUPS: { heading: string; namespace: string; methods: string[] }[] namespace: 'aztec', methods: [ 'getL1ToL2MessageMembershipWitness', - 'getL1ToL2MessageCheckpoint', + 'getL1ToL2MessageIndex', 'getL2ToL1MembershipWitness', 'getL2ToL1Messages', ], diff --git a/l1-contracts/gas_benchmark.md b/l1-contracts/gas_benchmark.md index 6f68e4ff4503..516eda73c2c5 100644 --- a/l1-contracts/gas_benchmark.md +++ b/l1-contracts/gas_benchmark.md @@ -12,24 +12,24 @@ ## No Validators -| Function | Avg Gas | Max Gas | Calldata Size | Calldata Gas | -|----------------------|---------|---------|---------------|--------------| -| propose | 195,988 | 222,201 | 932 | 14,912 | -| submitEpochRootProof | 697,655 | 743,529 | 2,820 | 45,120 | -| setupEpoch | 31,998 | 113,793 | - | - | +| Function | Avg Gas | Max Gas | Calldata Size | Calldata Gas | +|----------------------|---------|-----------|---------------|--------------| +| propose | 199,557 | 225,741 | 996 | 15,936 | +| submitEpochRootProof | 994,378 | 1,032,871 | 14,148 | 226,368 | +| setupEpoch | 32,042 | 113,837 | - | - | -**Avg Gas Cost per Second**: 3,341.5 gas/second +**Avg Gas Cost per Second**: 3,648.7 gas/second *Epoch duration*: 0h 38m 24s ## Validators -| Function | Avg Gas | Max Gas | Calldata Size | Calldata Gas | -|----------------------|---------|---------|---------------|--------------| -| propose | 324,449 | 351,604 | 4,452 | 71,232 | -| submitEpochRootProof | 896,101 | 941,944 | 5,316 | 85,056 | -| aggregate3 | 373,118 | 386,457 | - | - | -| setupEpoch | 46,459 | 547,626 | - | - | +| Function | Avg Gas | Max Gas | Calldata Size | Calldata Gas | +|----------------------|-----------|-----------|---------------|--------------| +| propose | 327,969 | 355,787 | 4,516 | 72,256 | +| submitEpochRootProof | 1,575,427 | 1,673,267 | 16,644 | 266,304 | +| aggregate3 | 376,569 | 389,943 | - | - | +| setupEpoch | 46,504 | 547,670 | - | - | -**Avg Gas Cost per Second**: 5,304.3 gas/second +**Avg Gas Cost per Second**: 5,942.9 gas/second *Epoch duration*: 0h 38m 24s diff --git a/l1-contracts/gas_benchmark_results.json b/l1-contracts/gas_benchmark_results.json index 45c6a506461d..ba78f7e71f00 100644 --- a/l1-contracts/gas_benchmark_results.json +++ b/l1-contracts/gas_benchmark_results.json @@ -2,62 +2,62 @@ "no_validators": { "propose": { "calls": 150, - "min": 182373, - "mean": 195988, - "median": 191762, - "max": 222201, - "calldata_size": 932, - "calldata_gas": 14912 + "min": 185913, + "mean": 199557, + "median": 195302, + "max": 225741, + "calldata_size": 996, + "calldata_gas": 15936 }, "setupEpoch": { "calls": 150, - "min": 29264, - "mean": 31998, - "median": 29264, - "max": 113793 + "min": 29309, + "mean": 32042, + "median": 29309, + "max": 113837 }, "submitEpochRootProof": { "calls": 4, - "min": 676491, - "mean": 697655, - "median": 685300, - "max": 743529, - "calldata_size": 2820, - "calldata_gas": 45120 + "min": 975675, + "mean": 994378, + "median": 984484, + "max": 1032871, + "calldata_size": 14148, + "calldata_gas": 226368 } }, "validators": { "propose": { "calls": 150, - "min": 302105, - "mean": 324449, - "median": 323910, - "max": 351604, - "calldata_size": 4452, - "calldata_gas": 71232 + "min": 305630, + "mean": 327969, + "median": 327423, + "max": 355787, + "calldata_size": 4516, + "calldata_gas": 72256 }, "setupEpoch": { "calls": 150, - "min": 29264, - "mean": 46459, - "median": 29264, - "max": 547626 + "min": 29309, + "mean": 46504, + "median": 29309, + "max": 547670 }, "submitEpochRootProof": { "calls": 4, - "min": 874924, - "mean": 896101, - "median": 883769, - "max": 941944, - "calldata_size": 5316, - "calldata_gas": 85056 + "min": 1463669, + "mean": 1575427, + "median": 1582387, + "max": 1673267, + "calldata_size": 16644, + "calldata_gas": 266304 }, "aggregate3": { "calls": 55, - "min": 362015, - "mean": 373118, - "median": 372828, - "max": 386457 + "min": 365451, + "mean": 376569, + "median": 376253, + "max": 389943 } } } \ No newline at end of file diff --git a/l1-contracts/gas_report.json b/l1-contracts/gas_report.json index 173c6310bea8..045df4147f2d 100644 --- a/l1-contracts/gas_report.json +++ b/l1-contracts/gas_report.json @@ -1,291 +1,344 @@ [ { - "contract": "src/core/Rollup.sol:Rollup", + "contract": "src/core/messagebridge/Inbox.sol:Inbox", + "deployment": { + "gas": 0, + "size": 5932 + }, + "functions": { + "getBucket(uint256)": { + "calls": 4667, + "min": 7414, + "mean": 7414, + "median": 7414, + "max": 7414 + }, + "getCurrentBucketSeq()": { + "calls": 4667, + "min": 408, + "mean": 1407, + "median": 408, + "max": 2408 + }, + "getFeeAssetPortal()": { + "calls": 2586, + "min": 212, + "mean": 212, + "median": 212, + "max": 212 + }, + "sendL2Message((bytes32,uint256),bytes32,bytes32)": { + "calls": 37328, + "min": 41480, + "mean": 44796, + "median": 41480, + "max": 100232 + } + } + }, + { + "contract": "src/core/messagebridge/Outbox.sol:Outbox", + "deployment": { + "gas": 0, + "size": 3107 + }, + "functions": { + "getRootData(uint256,uint256)": { + "calls": 7, + "min": 2661, + "mean": 2661, + "median": 2661, + "max": 2661 + } + } + }, + { + "contract": "src/governance/CoinIssuer.sol:CoinIssuer", + "deployment": { + "gas": 0, + "size": 2438 + }, + "functions": { + "owner()": { + "calls": 5172, + "min": 397, + "mean": 397, + "median": 397, + "max": 397 + } + } + }, + { + "contract": "src/governance/Registry.sol:Registry", + "deployment": { + "gas": 0, + "size": 4970 + }, + "functions": { + "getCanonicalRollup()": { + "calls": 1738, + "min": 1073, + "mean": 4073, + "median": 4073, + "max": 7073 + }, + "getRewardDistributor()": { + "calls": 2586, + "min": 420, + "mean": 420, + "median": 420, + "max": 420 + }, + "owner()": { + "calls": 7758, + "min": 353, + "mean": 353, + "median": 353, + "max": 353 + } + } + }, + { + "contract": "src/governance/RewardDistributor.sol:RewardDistributor", + "deployment": { + "gas": 0, + "size": 3137 + }, + "functions": { + "availableTo(address)": { + "calls": 869, + "min": 20573, + "mean": 20573, + "median": 20573, + "max": 20573 + } + } + }, + { + "contract": "test/RollupWithPreheating.sol:RollupWithPreheating", "deployment": { "gas": 0, - "size": 43935 + "size": 42877 }, "functions": { "archive()": { - "calls": 2323, - "min": 4583, - "mean": 4583, - "median": 4583, - "max": 4583 - }, - "getBlock(uint256)": { - "calls": 847, - "min": 9208, - "mean": 9208, - "median": 9208, - "max": 9208 + "calls": 2333, + "min": 4641, + "mean": 4641, + "median": 4641, + "max": 4641 + }, + "checkBlob()": { + "calls": 1, + "min": 2509, + "mean": 2509, + "median": 2509, + "max": 2509 + }, + "getCheckpoint(uint256)": { + "calls": 879, + "min": 27343, + "mean": 27343, + "median": 27343, + "max": 27343 + }, + "getCheckpointReward()": { + "calls": 2589, + "min": 1128, + "mean": 1133, + "median": 1128, + "max": 5628 }, "getCollectiveProverRewardsForEpoch(uint256)": { "calls": 3, - "min": 2614, - "mean": 2614, - "median": 2614, - "max": 2614 + "min": 5837, + "mean": 5837, + "median": 5837, + "max": 5837 + }, + "getCurrentEpoch()": { + "calls": 870, + "min": 915, + "mean": 915, + "median": 915, + "max": 915 }, "getCurrentSlot()": { "calls": 100, - "min": 2735, - "mean": 2735, - "median": 2735, - "max": 2735 + "min": 2737, + "mean": 2737, + "median": 2737, + "max": 2737 }, "getEpochDuration()": { "calls": 2, - "min": 2400, - "mean": 2400, - "median": 2400, - "max": 2400 + "min": 2486, + "mean": 2486, + "median": 2486, + "max": 2486 }, - "getFeeAssetPerEth()": { - "calls": 1, - "min": 5440, - "mean": 5440, - "median": 5440, - "max": 5440 + "getEpochProofPublicInputs(uint256,uint256,(bytes32,bytes32,bytes32,bytes32,bytes32,address),(bytes32,bytes32,bytes32,bytes32,bytes32,uint256,uint256,address,bytes32,(uint128,uint128),uint256,uint256)[],bytes)": { + "calls": 4, + "min": 16751, + "mean": 35891, + "median": 26547, + "max": 73720 + }, + "getEthPerFeeAsset()": { + "calls": 2, + "min": 11043, + "mean": 11043, + "median": 11043, + "max": 11043 }, "getFeeAssetPortal()": { - "calls": 3104, - "min": 453, - "mean": 1453, - "median": 1453, - "max": 2453 + "calls": 4660, + "min": 566, + "mean": 1456, + "median": 566, + "max": 2566 }, "getInbox()": { - "calls": 6198, - "min": 2498, - "mean": 2498, - "median": 2498, - "max": 2498 + "calls": 9073, + "min": 2543, + "mean": 2543, + "median": 2543, + "max": 2543 }, "getL1FeesAt(uint256)": { "calls": 2, - "min": 9434, - "mean": 9434, - "median": 9434, - "max": 9434 - }, - "getManaBaseFeeAt(uint256,bool)": { - "calls": 2325, - "min": 30538, - "mean": 32450, - "median": 30538, - "max": 36293 + "min": 9086, + "mean": 9086, + "median": 9086, + "max": 9086 + }, + "getManaMinFeeAt(uint256,bool)": { + "calls": 2336, + "min": 27032, + "mean": 28689, + "median": 27032, + "max": 32050 }, "getManaTarget()": { - "calls": 2, - "min": 2422, - "mean": 2422, - "median": 2422, - "max": 2422 + "calls": 1026, + "min": 5526, + "mean": 5526, + "median": 5526, + "max": 5526 }, "getOutbox()": { - "calls": 3875, - "min": 2452, - "mean": 2452, - "median": 2452, - "max": 2452 - }, - "getPendingBlockNumber()": { - "calls": 1545, - "min": 2485, - "mean": 2485, - "median": 2485, - "max": 2485 - }, - "getProofSubmissionWindow()": { - "calls": 4, - "min": 2404, - "mean": 2404, - "median": 2404, - "max": 2404 - }, - "getProvenBlockNumber()": { - "calls": 6196, - "min": 2468, - "mean": 2468, - "median": 2468, - "max": 2468 + "calls": 2, + "min": 2521, + "mean": 2521, + "median": 2521, + "max": 2521 + }, + "getPendingCheckpointNumber()": { + "calls": 1679, + "min": 2462, + "mean": 2462, + "median": 2462, + "max": 2462 + }, + "getProvenCheckpointNumber()": { + "calls": 1684, + "min": 2585, + "mean": 2585, + "median": 2585, + "max": 2585 }, "getProvingCostPerManaInEth()": { "calls": 1, - "min": 2407, - "mean": 2407, - "median": 2407, - "max": 2407 + "min": 5729, + "mean": 5729, + "median": 5729, + "max": 5729 }, "getProvingCostPerManaInFeeAsset()": { "calls": 1, - "min": 8164, - "mean": 8164, - "median": 8164, - "max": 8164 + "min": 14475, + "mean": 14475, + "median": 14475, + "max": 14475 }, "getSequencerRewards(address)": { "calls": 2, - "min": 2716, - "mean": 2716, - "median": 2716, - "max": 2716 + "min": 5981, + "mean": 5981, + "median": 5981, + "max": 5981 }, "getTimestampForSlot(uint256)": { - "calls": 2432, - "min": 2802, - "mean": 2802, - "median": 2802, - "max": 2802 + "calls": 2442, + "min": 2808, + "mean": 2808, + "median": 2808, + "max": 2808 }, "getVersion()": { - "calls": 3875, - "min": 471, - "mean": 1669, - "median": 2471, - "max": 2471 + "calls": 4919, + "min": 499, + "mean": 1447, + "median": 499, + "max": 2499 }, "owner()": { - "calls": 1552, - "min": 420, - "mean": 420, - "median": 420, - "max": 420 + "calls": 5173, + "min": 533, + "mean": 533, + "median": 533, + "max": 2533 }, - "propose((bytes32,bytes,(int256),bytes,bytes32[]),(address,(uint8,bytes32,bytes32))[],bytes)": { - "calls": 2327, - "min": 180367, - "mean": 343443, - "median": 348542, - "max": 380636 + "propose((bytes32,(int256),(bytes32,bytes32,bytes32,bytes32,bytes32,uint256,uint256,address,bytes32,(uint128,uint128),uint256,uint256),uint256),(bytes,bytes),address[],(uint8,bytes32,bytes32),bytes)": { + "calls": 2339, + "min": 0, + "mean": 266918, + "median": 284436, + "max": 325546 }, "prune()": { "calls": 6, - "min": 25709, - "mean": 32858, - "median": 33454, - "max": 37939 + "min": 26689, + "mean": 33247, + "median": 33682, + "max": 38274 }, "setProvingCostPerMana(uint256)": { - "calls": 2, - "min": 28780, - "mean": 28780, - "median": 28780, - "max": 28780 - }, - "submitEpochRootProof((uint256,uint256,(bytes32,bytes32,address),bytes32[],bytes,bytes))": { - "calls": 846, - "min": 58871, - "mean": 417467, - "median": 414067, - "max": 448154 - } - } - }, - { - "contract": "src/core/messagebridge/Inbox.sol:Inbox", - "deployment": { - "gas": 0, - "size": 7350 - }, - "functions": { - "getFeeAssetPortal()": { - "calls": 1552, - "min": 234, - "mean": 234, - "median": 234, - "max": 234 - }, - "getInProgress()": { - "calls": 3, - "min": 2425, - "mean": 2425, - "median": 2425, - "max": 2425 - }, - "getRoot(uint256)": { - "calls": 2325, - "min": 4809, - "mean": 4817, - "median": 4809, - "max": 4835 - }, - "sendL2Message((bytes32,uint256),bytes32,bytes32)": { - "calls": 37168, - "min": 43418, - "mean": 53020, - "median": 46820, - "max": 77618 - } - } - }, - { - "contract": "src/core/messagebridge/Outbox.sol:Outbox", - "deployment": { - "gas": 0, - "size": 2855 - }, - "functions": { - "getRootData(uint256)": { - "calls": 2323, - "min": 5627, - "mean": 5627, - "median": 5627, - "max": 5627 - } - } - }, - { - "contract": "src/governance/Registry.sol:Registry", - "deployment": { - "gas": 0, - "size": 4070 - }, - "functions": { - "getCanonicalRollup()": { - "calls": 2526, - "min": 1016, - "mean": 3016, - "median": 1016, - "max": 7016 + "calls": 1, + "min": 52474, + "mean": 52474, + "median": 52474, + "max": 52474 }, - "getRewardDistributor()": { - "calls": 1552, - "min": 267, - "mean": 267, - "median": 267, - "max": 267 + "submitEpochRootProof((uint256,uint256,(bytes32,bytes32,bytes32,bytes32,bytes32,address),(bytes32,bytes32,bytes32,bytes32,bytes32,uint256,uint256,address,bytes32,(uint128,uint128),uint256,uint256)[],(bytes,bytes),bytes,bytes))": { + "calls": 876, + "min": 58286, + "mean": 376918, + "median": 382965, + "max": 421657 }, - "owner()": { - "calls": 3104, - "min": 407, - "mean": 407, - "median": 407, - "max": 407 + "updateManaTarget(uint256)": { + "calls": 512, + "min": 26051, + "mean": 28652, + "median": 27293, + "max": 31299 } } }, { - "contract": "src/governance/RewardDistributor.sol:RewardDistributor", + "contract": "test/governance/helpers/TestGov.sol:TestGov", "deployment": { "gas": 0, - "size": 2290 + "size": 16556 }, "functions": { - "BLOCK_REWARD()": { - "calls": 1555, - "min": 238, - "mean": 238, - "median": 238, - "max": 238 - }, - "canonicalRollup()": { - "calls": 842, - "min": 10158, - "mean": 10158, - "median": 10158, - "max": 10158 + "isAllBeneficiariesAllowed()": { + "calls": 2586, + "min": 404, + "mean": 404, + "median": 404, + "max": 404 } } } diff --git a/l1-contracts/script/deploy/RollupConfiguration.sol b/l1-contracts/script/deploy/RollupConfiguration.sol index f19c5e9a620e..d70ddf1be185 100644 --- a/l1-contracts/script/deploy/RollupConfiguration.sol +++ b/l1-contracts/script/deploy/RollupConfiguration.sol @@ -110,7 +110,6 @@ contract RollupConfiguration is IRollupConfiguration, Test { config.targetCommitteeSize = vm.envUint("AZTEC_TARGET_COMMITTEE_SIZE"); config.lagInEpochsForValidatorSet = vm.envUint("AZTEC_LAG_IN_EPOCHS_FOR_VALIDATOR_SET"); config.lagInEpochsForRandao = vm.envUint("AZTEC_LAG_IN_EPOCHS_FOR_RANDAO"); - config.inboxLag = vm.envUint("AZTEC_INBOX_LAG"); config.aztecProofSubmissionEpochs = vm.envUint("AZTEC_PROOF_SUBMISSION_EPOCHS"); config.localEjectionThreshold = vm.envUint("AZTEC_LOCAL_EJECTION_THRESHOLD"); config.slashingQuorum = vm.envOr("AZTEC_SLASHING_QUORUM", slashingRoundSize / 2 + 1); diff --git a/l1-contracts/scripts/constants-codegen/solidity.json b/l1-contracts/scripts/constants-codegen/solidity.json index 2046ce24d44a..147c0c25975b 100644 --- a/l1-contracts/scripts/constants-codegen/solidity.json +++ b/l1-contracts/scripts/constants-codegen/solidity.json @@ -1,6 +1,8 @@ [ "MAX_FIELD_VALUE", "L1_TO_L2_MSG_SUBTREE_HEIGHT", + "MAX_L1_TO_L2_MSGS_PER_CHECKPOINT", + "INBOX_LAG_SECONDS", "MAX_L2_TO_L1_MSGS_PER_TX", "INITIAL_CHECKPOINT_NUMBER", "MAX_CHECKPOINTS_PER_EPOCH", @@ -8,7 +10,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/scripts/network-defaults.json b/l1-contracts/scripts/network-defaults.json index 3d04bcf9ab46..96bc9ce58497 100644 --- a/l1-contracts/scripts/network-defaults.json +++ b/l1-contracts/scripts/network-defaults.json @@ -14,7 +14,6 @@ "AZTEC_ENTRY_QUEUE_FLUSH_SIZE_MIN": 48, "AZTEC_ENTRY_QUEUE_FLUSH_SIZE_QUOTIENT": 2, "AZTEC_ENTRY_QUEUE_MAX_FLUSH_SIZE": 48, - "AZTEC_INBOX_LAG": 1, "AZTEC_PROOF_SUBMISSION_EPOCHS": 1, "AZTEC_MANA_TARGET": 100000000, "AZTEC_PROVING_COST_PER_MANA": 100, diff --git a/l1-contracts/src/core/RollupCore.sol b/l1-contracts/src/core/RollupCore.sol index ac0a89dfa0d5..5d8f33eca314 100644 --- a/l1-contracts/src/core/RollupCore.sol +++ b/l1-contracts/src/core/RollupCore.sol @@ -15,7 +15,6 @@ import {IStakingCore} from "@aztec/core/interfaces/IStaking.sol"; import {IValidatorSelectionCore} from "@aztec/core/interfaces/IValidatorSelection.sol"; import {IInbox} from "@aztec/core/interfaces/messagebridge/IInbox.sol"; import {IOutbox} from "@aztec/core/interfaces/messagebridge/IOutbox.sol"; -import {Constants} from "@aztec/core/libraries/ConstantsGen.sol"; import {CommitteeAttestations} from "@aztec/core/libraries/rollup/AttestationLib.sol"; import {Errors} from "@aztec/core/libraries/Errors.sol"; import {EpochProofExtLib} from "@aztec/core/libraries/rollup/EpochProofExtLib.sol"; @@ -620,18 +619,7 @@ contract RollupCore is EIP712("Aztec Rollup", "1"), Ownable, IStakingCore, IVali rollupStore.config.epochProofVerifier = _epochProofVerifier; rollupStore.config.version = _config.version; - IInbox inbox = IInbox( - address( - new Inbox( - address(this), - _feeAsset, - _config.version, - Constants.L1_TO_L2_MSG_SUBTREE_HEIGHT, - _config.inboxLag, - INBOX_BUCKET_RING_SIZE - ) - ) - ); + IInbox inbox = IInbox(address(new Inbox(address(this), _feeAsset, _config.version, INBOX_BUCKET_RING_SIZE))); rollupStore.config.inbox = inbox; rollupStore.config.outbox = IOutbox(address(new Outbox(address(this), _config.version))); diff --git a/l1-contracts/src/core/interfaces/IRollup.sol b/l1-contracts/src/core/interfaces/IRollup.sol index 6a347b11d8d5..a6451fb20631 100644 --- a/l1-contracts/src/core/interfaces/IRollup.sol +++ b/l1-contracts/src/core/interfaces/IRollup.sol @@ -27,9 +27,8 @@ struct PublicInputArgs { bytes32 previousArchive; bytes32 endArchive; bytes32 outHash; - // Inbox rolling-hash chain segment consumed across the proven epoch (AZIP-22 Fast Inbox). Deliberately UNVALIDATED - // until the Fast Inbox flip, when they get checked against per-checkpoint records written at propose; for now they - // are only passed through to the proof's public inputs. + // Inbox rolling-hash chain segment consumed across the proven epoch. Both boundaries are + // anchored at proof submission against the rolling hashes recorded at propose for checkpoints start - 1 and end. bytes32 previousInboxRollingHash; bytes32 endInboxRollingHash; address proverId; @@ -84,7 +83,6 @@ struct RollupConfigInput { RewardBoostConfig rewardBoostConfig; StakingQueueConfig stakingQueueConfig; uint256 localEjectionThreshold; - uint256 inboxLag; } struct RollupConfig { diff --git a/l1-contracts/src/core/interfaces/messagebridge/IInbox.sol b/l1-contracts/src/core/interfaces/messagebridge/IInbox.sol index 8a9366b1b0c5..11815bb79877 100644 --- a/l1-contracts/src/core/interfaces/messagebridge/IInbox.sol +++ b/l1-contracts/src/core/interfaces/messagebridge/IInbox.sol @@ -5,8 +5,8 @@ pragma solidity >=0.8.27; import {DataStructures} from "../../libraries/DataStructures.sol"; // Maximum number of messages a single bucket can hold before further messages in the same L1 block spill over -// into the next bucket. Matches the number of L1 to L2 messages a single L2 block can insert once the streaming -// inbox is live, so any one bucket is always consumable by one block. +// into the next bucket. Matches the number of L1 to L2 messages a single L2 block can insert, so any one bucket +// is always consumable by one block. uint256 constant MAX_MSGS_PER_BUCKET = 256; /** @@ -16,16 +16,9 @@ uint256 constant MAX_MSGS_PER_BUCKET = 256; */ interface IInbox { struct InboxState { - // Rolling hash of all messages inserted into the inbox. - // Used by clients to check for consistency. - // TODO: remove once the streaming inbox (AZIP-22 Fast Inbox) flips on and clients rely on the - // consensus rolling hash tracked in the buckets instead. - bytes16 rollingHash; - // This value is not used much by the contract, but it is useful for synching the node faster - // as it can more easily figure out if it can just skip looking for events for a time period. + // Cumulative number of messages inserted into the inbox. Useful for synching the node faster as it can + // more easily figure out if it can just skip looking for events for a time period. uint64 totalMessagesInserted; - // Number of a tree which is currently being filled - uint64 inProgress; } /** @@ -50,21 +43,12 @@ interface IInbox { /** * @notice Emitted when a message is sent - * @param checkpointNumber - The checkpoint number in which the message is included - * @param index - The index of the message in the L1 to L2 messages tree + * @param index - The compact cumulative index of the message in the Inbox insertion order * @param hash - The hash of the message - * @param rollingHash - The rolling hash of all messages inserted into the inbox * @param inboxRollingHash - The consensus rolling hash (truncated sha256 chain) after this message * @param bucketSeq - The sequence number of the bucket this message was absorbed into */ - event MessageSent( - uint256 indexed checkpointNumber, - uint256 index, - bytes32 indexed hash, - bytes16 rollingHash, - bytes32 inboxRollingHash, - uint256 bucketSeq - ); + event MessageSent(uint256 index, bytes32 indexed hash, bytes32 inboxRollingHash, uint256 bucketSeq); // docs:start:send_l1_to_l2_message /** @@ -74,37 +58,19 @@ interface IInbox { * @param _content - The content of the message (application specific) * @param _secretHash - The secret hash of the message (make it possible to hide when a specific message is consumed * on L2) - * @return The key of the message in the set and its leaf index in the tree + * @return The key of the message in the set and its compact cumulative index */ function sendL2Message(DataStructures.L2Actor memory _recipient, bytes32 _content, bytes32 _secretHash) external returns (bytes32, uint256); // docs:end:send_l1_to_l2_message - // docs:start:consume - /** - * @notice Consumes the current tree, and starts a new one if needed - * @dev Only callable by the rollup contract - * @dev In the first iteration we return empty tree root because first checkpoint's messages tree is always - * empty because there has to be a 1 checkpoint lag to prevent sequencer DOS attacks - * - * @param _toConsume - The checkpoint number to consume - * - * @return The root of the consumed tree - */ - function consume(uint256 _toConsume) external returns (bytes32); - // docs:end:consume - function getFeeAssetPortal() external view returns (address); - function getRoot(uint256 _checkpointNumber) external view returns (bytes32); - function getState() external view returns (InboxState memory); function getTotalMessagesInserted() external view returns (uint64); - function getInProgress() external view returns (uint64); - /** * @notice Returns the sequence number of the bucket currently accumulating messages * @return The current bucket sequence number diff --git a/l1-contracts/src/core/libraries/Errors.sol b/l1-contracts/src/core/libraries/Errors.sol index 83227e372e55..e78dcfa9cafd 100644 --- a/l1-contracts/src/core/libraries/Errors.sol +++ b/l1-contracts/src/core/libraries/Errors.sol @@ -22,12 +22,10 @@ library Errors { error DevNet__InvalidProposer(address expected, address actual); // 0x11e6e6f7 // Inbox - error Inbox__Unauthorized(); // 0xe5336a6b error Inbox__ActorTooLarge(bytes32 actor); // 0xa776a06e error Inbox__VersionMismatch(uint256 expected, uint256 actual); // 0x47452014 error Inbox__ContentTooLarge(bytes32 content); // 0x47452014 error Inbox__SecretHashTooLarge(bytes32 secretHash); // 0xecde7e2c - error Inbox__MustBuildBeforeConsume(); // 0xc4901999 error Inbox__BucketOutOfWindow(uint256 seq, uint256 current); // 0xfee255b7 // Outbox @@ -58,7 +56,6 @@ library Errors { error Rollup__InvalidCheckpointHeader(bytes32 expected, bytes32 actual); error Rollup__InvalidCheckpointHeaderCount(uint256 expected, uint256 actual); error Rollup__InvalidCheckpointNumber(uint256 expected, uint256 actual); // 0xd1ba9bfa - error Rollup__InvalidInHash(bytes32 expected, bytes32 actual); // 0xcd6f4233 error Rollup__InvalidInboxRollingHash(bytes32 expected, bytes32 actual); // 0xed1f7bb5 error Rollup__InvalidPreviousInboxRollingHash(bytes32 expected, bytes32 actual); // 0x2fe7cae5 error Rollup__InvalidEndInboxRollingHash(bytes32 expected, bytes32 actual); // 0x4a9cdb72 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/ProposeLib.sol b/l1-contracts/src/core/libraries/rollup/ProposeLib.sol index 23dd2aeb7d20..1a2d70f9e944 100644 --- a/l1-contracts/src/core/libraries/rollup/ProposeLib.sol +++ b/l1-contracts/src/core/libraries/rollup/ProposeLib.sol @@ -9,6 +9,7 @@ import {IInbox, MAX_MSGS_PER_BUCKET} from "@aztec/core/interfaces/messagebridge/ import {TempCheckpointLog} from "@aztec/core/libraries/compressed-data/CheckpointLog.sol"; import {FeeHeader} from "@aztec/core/libraries/compressed-data/fees/FeeStructs.sol"; import {ChainTipsLib, CompressedChainTips} from "@aztec/core/libraries/compressed-data/Tips.sol"; +import {Constants} from "@aztec/core/libraries/ConstantsGen.sol"; import {Errors} from "@aztec/core/libraries/Errors.sol"; import {CommitteeAttestations} from "@aztec/core/libraries/rollup/AttestationLib.sol"; import {CoordinationSignatureLib} from "@aztec/core/libraries/rollup/CoordinationSignatureLib.sol"; @@ -22,14 +23,6 @@ import {SafeCast} from "@oz/utils/math/SafeCast.sol"; import {ProposedHeader, ProposedHeaderLib} from "./ProposedHeaderLib.sol"; import {STFLib} from "./STFLib.sol"; -// Streaming-inbox protocol constants (AZIP-22 Fast Inbox). These mirror the protocol circuit constants and -// should move into the generated Constants library once the Solidity emitter includes them. -// Minimum bucket age, in seconds, at the start of a checkpoint's build frame for its consumption to be -// mandatory. One L1 slot: validators cannot be required to act on buckets they may not have seen. -uint256 constant INBOX_LAG_SECONDS = 12; -// Maximum number of L1 to L2 messages a single checkpoint can insert. -uint256 constant MAX_L1_TO_L2_MSGS_PER_CHECKPOINT = 1024; - struct ProposeArgs { bytes32 archive; OracleInput oracleInput; @@ -142,7 +135,7 @@ library ProposeLib { * - Checkpoint header validations (see validateHeader function for details) * - Proposer signature is valid for designated slot proposer: * Errors.ValidatorSelection__MissingProposerSignature - * - Inbox hash matches expected value: Errors.Rollup__InvalidInHash + * - Streaming Inbox consumption is valid: Errors.Rollup__InvalidInboxRollingHash * - Archive root is within the scalar field: Errors.Rollup__FieldElementOutOfRange * * Validations NOT performed: @@ -154,7 +147,7 @@ library ProposeLib { * - Store archive root for the new checkpoint number * - Store checkpoint metadata in circular storage (TempCheckpointLog) * - Update L1 gas fee oracle - * - Consume inbox messages + * - Validate streaming Inbox consumption against the parent checkpoint * - Setup epoch for validator selection (first block of the epoch) * * @param _args - The arguments to propose the checkpoint @@ -401,8 +394,7 @@ library ProposeLib { /** * @notice Validates a checkpoint's Inbox consumption against the streaming inbox buckets and returns how - * far consumption has reached. Not yet called from propose(): the legacy `consume()`/`inHash` flow - * remains the enforced path until the streaming inbox (AZIP-22 Fast Inbox) flips on. + * far consumption has reached. Called from propose() as the enforced consumption path. * * @dev Read-only; performs no Inbox write. Checks, in order: * 1. The checkpoint header's `inboxRollingHash` must equal the rolling hash snapshotted in the Inbox @@ -466,17 +458,17 @@ library ProposeLib { ); require( - bucket.totalMsgCount - _parentTotalMsgCount <= MAX_L1_TO_L2_MSGS_PER_CHECKPOINT, + bucket.totalMsgCount - _parentTotalMsgCount <= Constants.MAX_L1_TO_L2_MSGS_PER_CHECKPOINT, Errors.Rollup__TooManyInboxMessagesConsumed(bucket.totalMsgCount - _parentTotalMsgCount) ); if (_bucketHint < _inbox.getCurrentBucketSeq()) { IInbox.InboxBucket memory next = _inbox.getBucket(_bucketHint + 1); Timestamp buildFrameStart = TimeLib.toTimestamp(_slotNumber - Slot.wrap(1)); - Timestamp cutoff = buildFrameStart - Timestamp.wrap(INBOX_LAG_SECONDS); + Timestamp cutoff = buildFrameStart - Timestamp.wrap(Constants.INBOX_LAG_SECONDS); require( next.timestamp > Timestamp.unwrap(cutoff) - || next.totalMsgCount - _parentTotalMsgCount > MAX_L1_TO_L2_MSGS_PER_CHECKPOINT, + || next.totalMsgCount - _parentTotalMsgCount > Constants.MAX_L1_TO_L2_MSGS_PER_CHECKPOINT, Errors.Rollup__UnconsumedInboxMessages(_bucketHint + 1) ); } 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/src/core/messagebridge/Inbox.sol b/l1-contracts/src/core/messagebridge/Inbox.sol index 4258afb2cc64..fa1b0f5067e2 100644 --- a/l1-contracts/src/core/messagebridge/Inbox.sol +++ b/l1-contracts/src/core/messagebridge/Inbox.sol @@ -5,7 +5,6 @@ pragma solidity >=0.8.27; import {IRollup} from "@aztec/core/interfaces/IRollup.sol"; import {IInbox, MAX_MSGS_PER_BUCKET} from "@aztec/core/interfaces/messagebridge/IInbox.sol"; import {Constants} from "@aztec/core/libraries/ConstantsGen.sol"; -import {FrontierLib} from "@aztec/core/libraries/crypto/FrontierLib.sol"; import {Hash} from "@aztec/core/libraries/crypto/Hash.sol"; import {DataStructures} from "@aztec/core/libraries/DataStructures.sol"; import {Errors} from "@aztec/core/libraries/Errors.sol"; @@ -31,66 +30,31 @@ uint256 constant MIN_BUCKET_RING_SIZE = 512; */ contract Inbox is IInbox { using Hash for DataStructures.L1ToL2Msg; - using FrontierLib for FrontierLib.Forest; - using FrontierLib for FrontierLib.Tree; address public immutable ROLLUP; uint256 public immutable VERSION; address public immutable FEE_ASSET_PORTAL; - uint256 public immutable LAG; - uint256 public immutable BUCKET_RING_SIZE; - uint256 internal immutable HEIGHT; - uint256 internal immutable SIZE; - bytes32 internal immutable EMPTY_ROOT; // The root of an empty frontier tree - - // Practically immutable value as we only set it in the constructor. - FrontierLib.Forest internal forest; - - mapping(uint256 checkpointNumber => FrontierLib.Tree tree) public trees; - - InboxState internal state; - - // Ring of rolling-hash buckets, keyed by `bucketSeq % BUCKET_RING_SIZE`. Inert for the legacy - // frontier-tree flow; consumed by the streaming inbox checks at `propose` (AZIP-22 Fast Inbox). + // Ring of rolling-hash buckets, keyed by `bucketSeq % BUCKET_RING_SIZE`. Consumed by the streaming inbox + // checks at `propose`. mapping(uint256 ringIndex => InboxBucket bucket) internal buckets; uint64 internal currentBucketSeq; - constructor( - address _rollup, - IERC20 _feeAsset, - uint256 _version, - uint256 _height, - uint256 _lag, - uint256 _bucketRingSize - ) { + constructor(address _rollup, IERC20 _feeAsset, uint256 _version, uint256 _bucketRingSize) { ROLLUP = _rollup; VERSION = _version; - HEIGHT = _height; - SIZE = 2 ** _height; - - require(_lag > 0, "LAG TOO SMALL"); - LAG = _lag; - require(_bucketRingSize >= MIN_BUCKET_RING_SIZE, "BUCKET RING TOO SMALL"); BUCKET_RING_SIZE = _bucketRingSize; - state = InboxState({ - rollingHash: 0, totalMessagesInserted: 0, inProgress: SafeCast.toUint64(Constants.INITIAL_CHECKPOINT_NUMBER + LAG) - }); - // Genesis bucket: a checkpoint consuming no messages references the same bucket as its parent, so the // first checkpoint against an empty Inbox references this one and no base case leaks into `propose`. buckets[0] = InboxBucket({rollingHash: 0, totalMsgCount: 0, timestamp: SafeCast.toUint64(block.timestamp), msgCount: 0}); - forest.initialize(_height); - EMPTY_ROOT = trees[type(uint256).max].root(forest, HEIGHT, SIZE); - FEE_ASSET_PORTAL = address(new FeeJuicePortal(IRollup(_rollup), _feeAsset, IInbox(this), VERSION)); } @@ -104,7 +68,7 @@ contract Inbox is IInbox { * @param _secretHash - The secret hash of the message (make it possible to hide when a specific message is consumed * on L2) * - * @return Hash of the sent message and its leaf index in the tree. + * @return Hash of the sent message and its compact cumulative index. */ function sendL2Message(DataStructures.L2Actor memory _recipient, bytes32 _content, bytes32 _secretHash) external @@ -116,24 +80,11 @@ contract Inbox is IInbox { require(uint256(_content) <= Constants.MAX_FIELD_VALUE, Errors.Inbox__ContentTooLarge(_content)); require(uint256(_secretHash) <= Constants.MAX_FIELD_VALUE, Errors.Inbox__SecretHashTooLarge(_secretHash)); - // Is this the best way to read a packed struct into local variables in a single SLOAD - // without having to use assembly and manual unpacking? - InboxState memory _state = state; - bytes16 rollingHash = _state.rollingHash; - uint64 totalMessagesInserted = _state.totalMessagesInserted; - uint64 inProgress = _state.inProgress; - - FrontierLib.Tree storage currentTree = trees[inProgress]; - - if (currentTree.isFull(SIZE)) { - inProgress += 1; - currentTree = trees[inProgress]; - } - // Compact cumulative message index: the zero-based position of this message in the Inbox's - // insertion order, equal to the number of messages inserted before it. It is embedded in the leaf preimage and - // matches the streaming L1-to-L2 tree's leaf count, so consumers do not need per-checkpoint tree geometry. - uint256 index = totalMessagesInserted; + // insertion order, equal to the number of messages inserted before it. It matches the streaming L1-to-L2 tree's + // leaf count, so consumers do not need per-checkpoint tree geometry. Sourced from the current bucket's running + // total, which the absorb below then advances (a rollover carries the total forward unchanged). + uint256 index = _totalMessagesInserted(); // If the sender is the fee asset portal, we use a magic address to simpler have it initialized at genesis. // We assume that no-one will know the private key for this address and that the precompile won't change to @@ -149,69 +100,24 @@ contract Inbox is IInbox { }); bytes32 leaf = message.sha256ToField(); - currentTree.insertLeaf(leaf); - - bytes16 updatedRollingHash = bytes16(keccak256(abi.encodePacked(rollingHash, leaf))); - state = InboxState({ - rollingHash: updatedRollingHash, totalMessagesInserted: totalMessagesInserted + 1, inProgress: inProgress - }); (uint64 bucketSeq, bytes32 inboxRollingHash) = _absorbIntoBucket(leaf); - emit MessageSent(inProgress, index, leaf, updatedRollingHash, inboxRollingHash, bucketSeq); + emit MessageSent(index, leaf, inboxRollingHash, bucketSeq); return (leaf, index); } - /** - * @notice Consumes the current tree, and starts a new one if needed - * - * @dev Only callable by the rollup contract - * @dev In the first iteration we return empty tree root because first checkpoint's messages tree is always - * empty because there has to be a 1 checkpoint lag to prevent sequencer DOS attacks - * - * @param _toConsume - The checkpoint number to consume - * - * @return The root of the consumed tree - */ - function consume(uint256 _toConsume) external override(IInbox) returns (bytes32) { - require(msg.sender == ROLLUP, Errors.Inbox__Unauthorized()); - - uint64 inProgress = state.inProgress; - require(_toConsume < inProgress, Errors.Inbox__MustBuildBeforeConsume()); - - bytes32 root = EMPTY_ROOT; - if (_toConsume > Constants.INITIAL_CHECKPOINT_NUMBER) { - root = trees[_toConsume].root(forest, HEIGHT, SIZE); - } - - // Once consumption reaches the current lag boundary, open the next checkpoint - // so new inserts keep a full LAG-sized buffer ahead of the rollup. - if (_toConsume + LAG == inProgress) { - state.inProgress = inProgress + 1; - } - - return root; - } - function getFeeAssetPortal() external view override(IInbox) returns (address) { return FEE_ASSET_PORTAL; } - function getRoot(uint256 _checkpointNumber) external view override(IInbox) returns (bytes32) { - return trees[_checkpointNumber].root(forest, HEIGHT, SIZE); - } - function getState() external view override(IInbox) returns (InboxState memory) { - return state; + return InboxState({totalMessagesInserted: _totalMessagesInserted()}); } function getTotalMessagesInserted() external view override(IInbox) returns (uint64) { - return state.totalMessagesInserted; - } - - function getInProgress() external view override(IInbox) returns (uint64) { - return state.inProgress; + return _totalMessagesInserted(); } function getCurrentBucketSeq() external view override(IInbox) returns (uint64) { @@ -264,4 +170,9 @@ contract Inbox is IInbox { return (bucketSeq, bucket.rollingHash); } + + // Cumulative number of messages inserted into the Inbox, tracked by the current bucket's running total. + function _totalMessagesInserted() internal view returns (uint64) { + return buckets[currentBucketSeq % BUCKET_RING_SIZE].totalMsgCount; + } } diff --git a/l1-contracts/test/Inbox.t.sol b/l1-contracts/test/Inbox.t.sol index 7f9e36d4c1e4..d9661b10db02 100644 --- a/l1-contracts/test/Inbox.t.sol +++ b/l1-contracts/test/Inbox.t.sol @@ -17,23 +17,13 @@ import {DataStructures} from "@aztec/core/libraries/DataStructures.sol"; contract InboxTest is Test { using Hash for DataStructures.L1ToL2Msg; - uint256 internal constant FIRST_REAL_TREE_NUM = Constants.INITIAL_CHECKPOINT_NUMBER + TestConstants.AZTEC_INBOX_LAG; - // We set low depth (5) to ensure we sufficiently test the tree transitions - uint256 internal constant HEIGHT = 5; - uint256 internal constant SIZE = 2 ** HEIGHT; - InboxHarness internal inbox; uint256 internal version = 0; - uint256 internal checkpointNumber = Constants.INITIAL_CHECKPOINT_NUMBER; - bytes32 internal emptyTreeRoot; function setUp() public { address rollup = address(this); IERC20 feeAsset = new TestERC20("Fee Asset", "FA", address(this)); - inbox = new InboxHarness( - rollup, feeAsset, version, HEIGHT, TestConstants.AZTEC_INBOX_LAG, TestConstants.AZTEC_INBOX_BUCKET_RING_SIZE - ); - emptyTreeRoot = inbox.getEmptyRoot(); + inbox = new InboxHarness(rollup, feeAsset, version, TestConstants.AZTEC_INBOX_BUCKET_RING_SIZE); } function _fakeMessage() internal view returns (DataStructures.L1ToL2Msg memory) { @@ -48,10 +38,6 @@ contract InboxTest is Test { }); } - function _divideAndRoundUp(uint256 a, uint256 b) internal pure returns (uint256) { - return (a + b - 1) / b; - } - function _boundMessage(DataStructures.L1ToL2Msg memory _message, uint256 _globalLeafIndex) internal view @@ -73,38 +59,17 @@ contract InboxTest is Test { return _message; } - // Since there is a LAG checkpoint lag between tree to be consumed and tree in progress the following invariant should - // never be violated - modifier checkInvariant() { - _; - assertLt(checkpointNumber, inbox.getInProgress()); - } - - function testRevertIfNotConsumingFromRollup() public { - vm.prank(address(0x1)); - vm.expectRevert(Errors.Inbox__Unauthorized.selector); - inbox.consume(checkpointNumber); - } - - function testRevertIFConsumingInFuture() public { - vm.expectRevert(Errors.Inbox__MustBuildBeforeConsume.selector); - inbox.consume(checkpointNumber + 1000); - } - - function testFuzzInsert(DataStructures.L1ToL2Msg memory _message) public checkInvariant { + function testFuzzInsert(DataStructures.L1ToL2Msg memory _message) public { Inbox.InboxState memory stateBefore = inbox.getState(); // Compact cumulative index: the message's index is the count inserted before it. uint256 globalLeafIndex = stateBefore.totalMessagesInserted; DataStructures.L1ToL2Msg memory message = _boundMessage(_message, globalLeafIndex); bytes32 leaf = message.sha256ToField(); - bytes16 expectedRollingHash = bytes16(keccak256(abi.encodePacked(stateBefore.rollingHash, leaf))); bytes32 expectedInboxRollingHash = Hash.accumulateInboxRollingHash(bytes32(0), leaf); vm.expectEmit(true, true, true, true); // event we expect - emit IInbox.MessageSent( - FIRST_REAL_TREE_NUM, globalLeafIndex, leaf, expectedRollingHash, expectedInboxRollingHash, 1 - ); + emit IInbox.MessageSent(globalLeafIndex, leaf, expectedInboxRollingHash, 1); // event we will get (bytes32 insertedLeaf, uint256 insertedIndex) = inbox.sendL2Message(message.recipient, message.content, message.secretHash); @@ -114,19 +79,14 @@ contract InboxTest is Test { Inbox.InboxState memory stateAfter = inbox.getState(); assertEq(stateBefore.totalMessagesInserted + 1, stateAfter.totalMessagesInserted); - assertEq(expectedRollingHash, stateAfter.rollingHash); - assertEq(stateBefore.inProgress, stateAfter.inProgress); } - function testSendDuplicateL2Messages() public checkInvariant { + function testSendDuplicateL2Messages() public { DataStructures.L1ToL2Msg memory message = _fakeMessage(); (bytes32 leaf1, uint256 index1) = inbox.sendL2Message(message.recipient, message.content, message.secretHash); (bytes32 leaf2, uint256 index2) = inbox.sendL2Message(message.recipient, message.content, message.secretHash); (bytes32 leaf3, uint256 index3) = inbox.sendL2Message(message.recipient, message.content, message.secretHash); - // Only 1 tree should be non-zero - assertEq(inbox.getNumTrees(), TestConstants.AZTEC_INBOX_LAG); - // All the leaves should be different since the index gets mixed in assertNotEq(leaf1, leaf2); assertNotEq(leaf2, leaf3); @@ -163,70 +123,4 @@ contract InboxTest is Test { vm.expectRevert(abi.encodeWithSelector(Errors.Inbox__SecretHashTooLarge.selector, message.secretHash)); inbox.sendL2Message(message.recipient, message.content, message.secretHash); } - - function testFuzzSendAndConsume( - DataStructures.L1ToL2Msg[] memory _messagesFirstBatch, - DataStructures.L1ToL2Msg[] memory _messagesSecondBatch, - uint256 _numTreesToConsumeFirstBatch, - uint256 _numTreesToConsumeSecondBatch - ) public { - // Send first batch of messages - _send(_messagesFirstBatch); - - // Consume first few trees - _consume(_numTreesToConsumeFirstBatch); - - // Send second batch of messages - _send(_messagesSecondBatch); - - // Consume second batch of trees - _consume(_numTreesToConsumeSecondBatch); - } - - function _send(DataStructures.L1ToL2Msg[] memory _messages) internal checkInvariant { - bytes32 toConsumeRoot = inbox.getToConsumeRoot(checkpointNumber); - - // We send the messages and then check that toConsume root did not change. - for (uint256 i = 0; i < _messages.length; i++) { - DataStructures.L1ToL2Msg memory message = _boundMessage(_messages[i], inbox.getNextMessageIndex()); - - // We check whether a new tree is correctly initialized when the one in progress is full - uint256 numTrees = inbox.getNumTrees(); - uint256 expectedNumTrees = inbox.treeInProgressFull() ? numTrees + 1 : numTrees; - - inbox.sendL2Message(message.recipient, message.content, message.secretHash); - - assertEq(inbox.getNumTrees(), expectedNumTrees, "Unexpected number of trees"); - } - - // Root of a tree waiting to be consumed should not change because we introduced a LAG checkpoint lag to prevent - // sequencer DOS attacks - assertEq( - inbox.getToConsumeRoot(checkpointNumber), toConsumeRoot, "Root of a tree waiting to be consumed should not change" - ); - } - - function _consume(uint256 _numTreesToConsume) internal checkInvariant { - uint256 initialNumTrees = inbox.getNumTrees(); - // We use (initialNumTrees * 2) as upper bound here because we want to test the case where we go beyond - // the currently initalized number of trees. When consuming the newly initialized trees we should get zero roots. - uint256 numTreesToConsume = bound(_numTreesToConsume, 1, initialNumTrees * 2); - - // Now we consume the trees - for (uint256 i = 0; i < numTreesToConsume; i++) { - uint256 numTrees = inbox.getNumTrees(); - uint256 expectedNumTrees = - (checkpointNumber + TestConstants.AZTEC_INBOX_LAG == inbox.getInProgress()) ? numTrees + 1 : numTrees; - bytes32 root = inbox.consume(checkpointNumber); - - // We check whether a new tree is correctly initialized when the one which was in progress was set as to consume - assertEq(inbox.getNumTrees(), expectedNumTrees, "Unexpected number of trees"); - - // If we go beyong the number of trees initialized before consuming we should get empty root - if (i > initialNumTrees) { - assertEq(root, emptyTreeRoot, "Root of a newly initialized tree not empty"); - } - checkpointNumber += 1; - } - } } diff --git a/l1-contracts/test/InboxBuckets.t.sol b/l1-contracts/test/InboxBuckets.t.sol index 5f23173c2416..86037e2f86b4 100644 --- a/l1-contracts/test/InboxBuckets.t.sol +++ b/l1-contracts/test/InboxBuckets.t.sol @@ -9,15 +9,11 @@ import {IInbox, MAX_MSGS_PER_BUCKET} from "@aztec/core/interfaces/messagebridge/ import {MIN_BUCKET_RING_SIZE} from "@aztec/core/messagebridge/Inbox.sol"; import {InboxHarness} from "./harnesses/InboxHarness.sol"; import {TestConstants} from "./harnesses/TestConstants.sol"; -import {Constants} from "@aztec/core/libraries/ConstantsGen.sol"; import {Errors} from "@aztec/core/libraries/Errors.sol"; import {Hash} from "@aztec/core/libraries/crypto/Hash.sol"; import {DataStructures} from "@aztec/core/libraries/DataStructures.sol"; contract InboxBucketsTest is Test { - uint256 internal constant FIRST_REAL_TREE_NUM = Constants.INITIAL_CHECKPOINT_NUMBER + TestConstants.AZTEC_INBOX_LAG; - uint256 internal constant HEIGHT = 10; - InboxHarness internal inbox; uint256 internal version = 0; bytes32 internal expectedRollingHash; @@ -28,7 +24,7 @@ contract InboxBucketsTest is Test { function _deployInbox(uint256 _ringSize) internal returns (InboxHarness) { IERC20 feeAsset = new TestERC20("Fee Asset", "FA", address(this)); - return new InboxHarness(address(this), feeAsset, version, HEIGHT, TestConstants.AZTEC_INBOX_LAG, _ringSize); + return new InboxHarness(address(this), feeAsset, version, _ringSize); } function _send(InboxHarness _inbox, uint256 _salt) internal returns (bytes32) { @@ -136,11 +132,10 @@ contract InboxBucketsTest is Test { index: inbox.getState().totalMessagesInserted }); bytes32 leaf = Hash.sha256ToField(message); - bytes16 legacyHash = bytes16(keccak256(abi.encodePacked(inbox.getState().rollingHash, leaf))); bytes32 inboxRollingHash = Hash.accumulateInboxRollingHash(bytes32(0), leaf); vm.expectEmit(true, true, true, true, address(inbox)); - emit IInbox.MessageSent(FIRST_REAL_TREE_NUM, message.index, leaf, legacyHash, inboxRollingHash, 1); + emit IInbox.MessageSent(message.index, leaf, inboxRollingHash, 1); inbox.sendL2Message(recipient, content, secretHash); } @@ -234,7 +229,7 @@ contract InboxBucketsTest is Test { function testConstructorRevertsBelowRingFloor() public { IERC20 feeAsset = new TestERC20("Fee Asset", "FA", address(this)); vm.expectRevert("BUCKET RING TOO SMALL"); - new InboxHarness(address(this), feeAsset, version, HEIGHT, TestConstants.AZTEC_INBOX_LAG, MIN_BUCKET_RING_SIZE - 1); + new InboxHarness(address(this), feeAsset, version, MIN_BUCKET_RING_SIZE - 1); } // Gas cost of a message absorbed into an already-open bucket (the common per-message case): the @@ -250,7 +245,7 @@ contract InboxBucketsTest is Test { } // Gas cost of the first message of a new L1 block: a larger timestamp opens the next bucket, - // writing a fresh ring slot on top of the per-message frontier-tree insert. + // writing a fresh ring slot. function testGasSendFirstMessageOfNewBlock() public { _send(inbox, 0); assertEq(inbox.getCurrentBucketSeq(), 1, "warmup opened bucket 1"); @@ -279,9 +274,8 @@ contract InboxBucketsTest is Test { assertEq(inbox.getCurrentBucketSeq(), 2, "rollover opened bucket 2"); } - // Gas cost of the first-ever message against a freshly deployed Inbox: the state struct, bucket 1, and the - // first frontier-tree slots are all written cold. This is the cold-storage case, not the global worst-case - // insert — later frontier indices with more levels to hash can cost more. + // Gas cost of the first-ever message against a freshly deployed Inbox: the rolling-hash slot and bucket 1 + // are written cold. This is the cold-storage case for the first message. function testGasSendFirstEverMessage() public { assertEq(inbox.getCurrentBucketSeq(), 0, "no message sent yet"); diff --git a/l1-contracts/test/InboxBucketsFuzz.t.sol b/l1-contracts/test/InboxBucketsFuzz.t.sol new file mode 100644 index 000000000000..7378a1a8e6c2 --- /dev/null +++ b/l1-contracts/test/InboxBucketsFuzz.t.sol @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2024 Aztec Labs. +pragma solidity >=0.8.27; + +import {Test} from "forge-std/Test.sol"; +import {TestERC20} from "src/mock/TestERC20.sol"; +import {IERC20} from "@oz/token/ERC20/IERC20.sol"; +import {IInbox, MAX_MSGS_PER_BUCKET} from "@aztec/core/interfaces/messagebridge/IInbox.sol"; +import {InboxHarness} from "./harnesses/InboxHarness.sol"; +import {TestConstants} from "./harnesses/TestConstants.sol"; +import {Constants} from "@aztec/core/libraries/ConstantsGen.sol"; +import {Hash} from "@aztec/core/libraries/crypto/Hash.sol"; +import {DataStructures} from "@aztec/core/libraries/DataStructures.sol"; + +/** + * Randomized coverage of the Inbox send path: batches of messages spread over several L1 blocks, with batches + * large enough to exceed the per-bucket cap and roll over. Every run re-derives the whole bucket ring from the + * message leaves the Inbox returned and the per-L1-block batch sizes, so an accumulation, rollover, or + * snapshot-boundary bug shows up as a mismatch against the model rather than as a missing assertion. The + * hand-written boundary cases live in InboxBuckets.t.sol; consumption and ring wraparound are out of scope. + */ +contract InboxBucketsFuzzTest is Test { + // Number of L1 blocks a multi-block run spans. Small enough that the bucket ring never wraps. + uint256 internal constant L1_BLOCKS = 6; + + /// Where one L1 block's batch of messages landed: the contiguous run of buckets it opened. + struct BlockBatch { + uint256 timestamp; + uint256 firstSeq; + uint256 lastSeq; + uint256 msgCount; + } + + InboxHarness internal inbox; + uint256 internal version = 0; + uint256 internal cap; + + // Every leaf the Inbox returned, in insertion order. The rolling-hash chain is recomputed from these. + bytes32[] internal leaves; + + // One entry per L1 block that sent at least one message, in block order. + BlockBatch[] internal batches; + + function setUp() public { + IERC20 feeAsset = new TestERC20("Fee Asset", "FA", address(this)); + inbox = new InboxHarness(address(this), feeAsset, version, TestConstants.AZTEC_INBOX_BUCKET_RING_SIZE); + cap = MAX_MSGS_PER_BUCKET; + } + + /// forge-config: default.fuzz.runs = 128 + function testFuzzBucketsAcrossL1Blocks( + uint8[L1_BLOCKS] memory _msgsPerBlock, + uint16[L1_BLOCKS] memory _blockGaps, + uint8 _rolloverBlock, + uint16 _rolloverCount, + uint256 _seed + ) public { + // One block per run always crosses the per-bucket cap, so every run exercises a rollover alongside the + // ordinary same-block accumulation and new-block bucket opening. + uint256 rolloverBlock = bound(_rolloverBlock, 0, L1_BLOCKS - 1); + uint256 rolloverCount = bound(_rolloverCount, cap + 1, cap + 8); + + for (uint256 b = 0; b < L1_BLOCKS; b++) { + if (b > 0) { + vm.roll(block.number + 1); + vm.warp(block.timestamp + bound(_blockGaps[b], 1, 3600)); + } + _sendBlock(b == rolloverBlock ? rolloverCount : bound(_msgsPerBlock[b], 0, 12), _seed); + } + + _assertBucketRing(); + } + + /// forge-config: default.fuzz.runs = 32 + function testFuzzRolloverChainWithinOneL1Block(uint16 _msgCount, uint256 _seed) public { + // Several buckets' worth of messages in a single L1 block: each message arriving at a full bucket opens the + // next ring slot at the same timestamp, so the block owns a run of buckets rather than one. + _sendBlock(bound(_msgCount, cap + 1, 2 * cap + 5), _seed); + _assertBucketRing(); + } + + // Sends `_count` messages in the current L1 block and checks what the batch did to the ring: it opened one + // bucket per cap-sized slice, every bucket it opened carries this block's timestamp, and the bucket the + // previous L1 block left behind is frozen. + function _sendBlock(uint256 _count, uint256 _seed) internal { + uint256 seqBefore = inbox.getCurrentBucketSeq(); + IInbox.InboxBucket memory frozen = inbox.getBucket(seqBefore); + uint256 totalBefore = leaves.length; + + for (uint256 i = 0; i < _count; i++) { + _send(_seed); + } + + uint256 seqAfter = inbox.getCurrentBucketSeq(); + assertEq(seqAfter, seqBefore + (_count + cap - 1) / cap, "buckets opened by this L1 block"); + _assertBucketEq(inbox.getBucket(seqBefore), frozen, "bucket left by the previous L1 block is frozen"); + + if (_count == 0) { + return; + } + + for (uint256 seq = seqBefore + 1; seq <= seqAfter; seq++) { + IInbox.InboxBucket memory bucket = inbox.getBucket(seq); + uint256 sliceStart = (seq - seqBefore - 1) * cap; + uint256 sliceSize = _count - sliceStart < cap ? _count - sliceStart : cap; + assertEq(bucket.msgCount, sliceSize, "messages absorbed into bucket"); + assertEq(bucket.timestamp, block.timestamp, "bucket carries the sending L1 block's timestamp"); + assertEq(bucket.totalMsgCount, totalBefore + sliceStart + sliceSize, "bucket cumulative total"); + } + + batches.push(BlockBatch({timestamp: block.timestamp, firstSeq: seqBefore + 1, lastSeq: seqAfter, msgCount: _count})); + } + + // Sends one message with fuzz-derived contents, checking the Inbox agrees on its leaf and compact index. + function _send(uint256 _seed) internal { + uint256 index = leaves.length; + DataStructures.L2Actor memory recipient = DataStructures.L2Actor({actor: _field(_seed, index, 0), version: version}); + bytes32 content = _field(_seed, index, 1); + bytes32 secretHash = _field(_seed, index, 2); + + (bytes32 leaf, uint256 insertedIndex) = inbox.sendL2Message(recipient, content, secretHash); + + // The leaf commits to the message and to the compact index the Inbox assigned it, so recomputing it from the + // sent contents pins both, and the chain recomputed from these leaves is anchored to the message stream. + bytes32 expectedLeaf = Hash.sha256ToField( + DataStructures.L1ToL2Msg({ + sender: DataStructures.L1Actor(address(this), block.chainid), + recipient: recipient, + content: content, + secretHash: secretHash, + index: index + }) + ); + assertEq(insertedIndex, index, "compact message index"); + assertEq(leaf, expectedLeaf, "message leaf"); + + leaves.push(leaf); + } + + // Re-derives every bucket in the ring from the recorded leaves and per-L1-block batches. + function _assertBucketRing() internal { + uint256 current = inbox.getCurrentBucketSeq(); + bytes32 rollingHash = 0; + uint256 counted = 0; + uint256 previousTimestamp = 0; + + for (uint256 seq = 1; seq <= current; seq++) { + IInbox.InboxBucket memory bucket = inbox.getBucket(seq); + assertGt(bucket.msgCount, 0, "bucket holds at least one message"); + assertLe(bucket.msgCount, cap, "per-bucket cap"); + + for (uint256 i = 0; i < bucket.msgCount; i++) { + rollingHash = Hash.accumulateInboxRollingHash(rollingHash, leaves[counted + i]); + } + counted += bucket.msgCount; + + assertEq(bucket.rollingHash, rollingHash, "bucket rolling hash recomputed from the message leaves"); + assertEq(bucket.totalMsgCount, counted, "cumulative total is the sum of the per-bucket counts"); + assertGe(bucket.timestamp, previousTimestamp, "bucket timestamps never go backwards"); + previousTimestamp = bucket.timestamp; + } + + assertEq(counted, leaves.length, "every message sent is in a bucket"); + assertEq(inbox.getTotalMessagesInserted(), leaves.length, "inbox total matches messages sent"); + + // Bucket boundaries align with L1 blocks: each block owns a contiguous run of buckets holding exactly the + // messages it sent, all stamped with its own timestamp, so no later block extended an earlier block's bucket. + uint256 expectedFirstSeq = 1; + for (uint256 b = 0; b < batches.length; b++) { + BlockBatch memory batch = batches[b]; + assertEq(batch.firstSeq, expectedFirstSeq, "L1 block's buckets follow the previous block's"); + + uint256 batchCount = 0; + for (uint256 seq = batch.firstSeq; seq <= batch.lastSeq; seq++) { + IInbox.InboxBucket memory bucket = inbox.getBucket(seq); + assertEq(bucket.timestamp, batch.timestamp, "bucket timestamp is its L1 block's"); + batchCount += bucket.msgCount; + } + + assertEq(batchCount, batch.msgCount, "L1 block's messages are all in that block's buckets"); + expectedFirstSeq = batch.lastSeq + 1; + } + assertEq(expectedFirstSeq - 1, current, "no buckets outside the recorded L1 blocks"); + } + + function _assertBucketEq(IInbox.InboxBucket memory _actual, IInbox.InboxBucket memory _expected, string memory _err) + internal + { + assertEq(_actual.rollingHash, _expected.rollingHash, _err); + assertEq(_actual.totalMsgCount, _expected.totalMsgCount, _err); + assertEq(_actual.timestamp, _expected.timestamp, _err); + assertEq(_actual.msgCount, _expected.msgCount, _err); + } + + // Fuzz-derived field element, distinct per message and per message field. + function _field(uint256 _seed, uint256 _index, uint256 _tag) internal pure returns (bytes32) { + return bytes32(uint256(keccak256(abi.encodePacked(_seed, _index, _tag))) % Constants.P); + } +} 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/Rollup.t.sol b/l1-contracts/test/Rollup.t.sol index 830e62d33c9f..685ee2124b95 100644 --- a/l1-contracts/test/Rollup.t.sol +++ b/l1-contracts/test/Rollup.t.sol @@ -171,18 +171,6 @@ contract RollupTest is RollupBase { function testPrune() public setUpFor("mixed_checkpoint_1") { _proposeCheckpoint("mixed_checkpoint_1", 1); - assertEq( - inbox.getInProgress(), - // Post-flip the Inbox `consume()` is no longer called at propose, so the frontier in-progress tree no longer - // advances per checkpoint; it stays at its initial value until a tree fills (AZIP-22 Fast Inbox). - Constants.INITIAL_CHECKPOINT_NUMBER + TestConstants.AZTEC_INBOX_LAG, - "Invalid in progress" - ); - - // @note Fetch the inbox root of checkpoint 2. This should be frozen when checkpoint 1 is proposed. - // Even if we end up reverting checkpoint 1, we should still see the same root in the inbox. - bytes32 inboxRoot2 = inbox.getRoot(2); - CheckpointLog memory checkpoint = rollup.getCheckpoint(1); Slot prunableAt = checkpoint.slotNumber + Epoch.wrap(2).toSlots(); @@ -193,13 +181,6 @@ contract RollupTest is RollupBase { assertEq(rollup.getProvenCheckpointNumber(), 0, "Invalid proven checkpoint number"); rollup.prune(); - assertEq( - inbox.getInProgress(), - // Post-flip the Inbox `consume()` is no longer called at propose, so the frontier in-progress tree no longer - // advances per checkpoint; it stays at its initial value until a tree fills (AZIP-22 Fast Inbox). - Constants.INITIAL_CHECKPOINT_NUMBER + TestConstants.AZTEC_INBOX_LAG, - "Invalid in progress" - ); assertEq(rollup.getPendingCheckpointNumber(), 0, "Invalid pending checkpoint number"); assertEq(rollup.getProvenCheckpointNumber(), 0, "Invalid proven checkpoint number"); @@ -211,14 +192,6 @@ contract RollupTest is RollupBase { // @note We prune the pending chain as part of the propose call. _proposeCheckpoint("empty_checkpoint_1", Slot.unwrap(prunableAt)); - assertEq( - inbox.getInProgress(), - // Post-flip the Inbox `consume()` is no longer called at propose, so the frontier in-progress tree no longer - // advances per checkpoint; it stays at its initial value until a tree fills (AZIP-22 Fast Inbox). - Constants.INITIAL_CHECKPOINT_NUMBER + TestConstants.AZTEC_INBOX_LAG, - "Invalid in progress" - ); - assertEq(inbox.getRoot(2), inboxRoot2, "Invalid inbox root"); assertEq(rollup.getPendingCheckpointNumber(), 1, "Invalid pending checkpoint number"); assertEq(rollup.getProvenCheckpointNumber(), 0, "Invalid proven checkpoint number"); } diff --git a/l1-contracts/test/RollupFieldRange.t.sol b/l1-contracts/test/RollupFieldRange.t.sol index 3a910ac3e9d6..a3d05d087ddd 100644 --- a/l1-contracts/test/RollupFieldRange.t.sol +++ b/l1-contracts/test/RollupFieldRange.t.sol @@ -164,7 +164,6 @@ contract RollupFieldRangeTest is RollupBase { vm.warp(max(block.timestamp, Timestamp.unwrap(ts))); _populateInbox(full.populate.sender, full.populate.recipient, full.populate.l1ToL2Content); - header.inHash = rollup.getInbox().getRoot(full.checkpoint.checkpointNumber); 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 4f50e6b3c4eb..cdf6ee4014a2 100644 --- a/l1-contracts/test/base/RollupBase.sol +++ b/l1-contracts/test/base/RollupBase.sol @@ -171,9 +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))); - // Legacy frontier root for the header's inHash field. Unchecked at propose post-flip, but kept because the - // fixtures were generated with it as part of the header hash. - full.checkpoint.header.inHash = rollup.getInbox().getRoot(full.checkpoint.checkpointNumber); // 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/builder/RollupBuilder.sol b/l1-contracts/test/builder/RollupBuilder.sol index 6d01ae12d3bc..e32fa8f97f58 100644 --- a/l1-contracts/test/builder/RollupBuilder.sol +++ b/l1-contracts/test/builder/RollupBuilder.sol @@ -164,11 +164,6 @@ contract RollupBuilder is Test { return this; } - function setInboxLag(uint256 _inboxLag) public returns (RollupBuilder) { - config.rollupConfigInput.inboxLag = _inboxLag; - return this; - } - function setSlotDuration(uint256 _slotDuration) public returns (RollupBuilder) { config.rollupConfigInput.aztecSlotDuration = _slotDuration; return this; 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/fee_portal/depositToAztecPublic.t.sol b/l1-contracts/test/fee_portal/depositToAztecPublic.t.sol index bdda37eb7990..0c17e677d395 100644 --- a/l1-contracts/test/fee_portal/depositToAztecPublic.t.sol +++ b/l1-contracts/test/fee_portal/depositToAztecPublic.t.sol @@ -9,8 +9,6 @@ import {IFeeJuicePortal} from "@aztec/core/interfaces/IFeeJuicePortal.sol"; import {Constants} from "@aztec/core/libraries/ConstantsGen.sol"; import {IERC20Errors} from "@oz/interfaces/draft-IERC6093.sol"; import {Rollup} from "@aztec/core/Rollup.sol"; -import {IRollup} from "@aztec/core/interfaces/IRollup.sol"; -import {TestConstants} from "../harnesses/TestConstants.sol"; import {DataStructures} from "@aztec/core/libraries/DataStructures.sol"; import {Hash} from "@aztec/core/libraries/crypto/Hash.sol"; import {IInbox} from "@aztec/core/interfaces/messagebridge/IInbox.sol"; @@ -30,23 +28,6 @@ contract DepositToAztecPublic is Test { address internal constant MAGIC_FEE_JUICE_ADDRESS = address(uint160(Constants.FEE_JUICE_ADDRESS)); - struct InboxLagTestState { - Rollup testRollup; - FeeJuicePortal testFeeJuicePortal; - TestERC20 testToken; - Inbox testInbox; - uint256 lag; - uint256 SIZE; - uint256 initialInProgress; - bytes32 to; - bytes32 secretHash1; - bytes32 secretHash2; - uint256 amount; - uint256 expectedIndex1; - uint256 expectedIndex2; - uint256 expectedInProgress; - } - function setUp() public { RollupBuilder builder = new RollupBuilder(address(this)); builder.deploy(); @@ -82,8 +63,7 @@ contract DepositToAztecPublic is Test { uint256 amount = 100 ether; Inbox inbox = Inbox(address(Rollup(address(registry.getCanonicalRollup())).getInbox())); - // Compact cumulative index (AZIP-22 Fast Inbox): the first message against a fresh Inbox has index 0 - // (equal to the number of messages inserted before it), regardless of the inbox lag. + // Compact cumulative index: the first message against a fresh Inbox has index 0. uint256 expectedIndex = 0; // The purpose of including the function selector is to make the message unique to that specific call. Note that @@ -106,11 +86,9 @@ contract DepositToAztecPublic is Test { assertEq(inbox.getTotalMessagesInserted(), 0); - bytes16 expectedHash = bytes16(keccak256(abi.encodePacked(inbox.getState().rollingHash, expectedKey))); - uint256 expectedInProgress = Constants.INITIAL_CHECKPOINT_NUMBER + TestConstants.AZTEC_INBOX_LAG; bytes32 expectedInboxRollingHash = Hash.accumulateInboxRollingHash(bytes32(0), expectedKey); vm.expectEmit(true, true, true, true, address(inbox)); - emit IInbox.MessageSent(expectedInProgress, expectedIndex, expectedKey, expectedHash, expectedInboxRollingHash, 1); + emit IInbox.MessageSent(expectedIndex, expectedKey, expectedInboxRollingHash, 1); vm.expectEmit(true, true, true, true, address(feeJuicePortal)); emit IFeeJuicePortal.DepositToAztecPublic(to, amount, secretHash, expectedKey, expectedIndex); @@ -120,112 +98,4 @@ contract DepositToAztecPublic is Test { assertEq(key, expectedKey); assertEq(index, expectedIndex); } - - function testFuzz_InboxLag(uint256 _lag) external { - // Bound lag to reasonable values (1-10) to test different lag configurations - uint256 lag = bound(_lag, 1, 10); - - RollupBuilder builder = new RollupBuilder(address(this)); - builder.setInboxLag(lag); - builder.deploy(); - - InboxLagTestState memory state = InboxLagTestState({ - testRollup: builder.getConfig().rollup, - testFeeJuicePortal: FeeJuicePortal(address(builder.getConfig().rollup.getFeeAssetPortal())), - testToken: builder.getConfig().testERC20, - testInbox: Inbox(address(builder.getConfig().rollup.getInbox())), - lag: lag, - SIZE: 2 ** Constants.L1_TO_L2_MSG_SUBTREE_HEIGHT, - initialInProgress: 0, // Will be set below - to: bytes32(0x0), - secretHash1: bytes32(uint256(0x01)), - secretHash2: bytes32(uint256(0x02)), - amount: 100 ether, - expectedIndex1: 0, // Will be set below - expectedIndex2: 0, // Will be set below - expectedInProgress: Constants.INITIAL_CHECKPOINT_NUMBER + lag - }); - - // Verify initial state: inProgress should be INITIAL_CHECKPOINT_NUMBER + lag - assertEq( - state.testInbox.getInProgress(), - state.expectedInProgress, - "Initial inProgress should be INITIAL_CHECKPOINT_NUMBER + lag" - ); - state.initialInProgress = state.testInbox.getInProgress(); - // Compact cumulative index (AZIP-22 Fast Inbox): messages are indexed by insertion order from 0, - // independent of the lag-based tree geometry. - state.expectedIndex1 = 0; - state.expectedIndex2 = 1; - - vm.prank(state.testToken.owner()); - state.testToken.mint(address(this), state.amount * 2); - state.testToken.approve(address(state.testFeeJuicePortal), state.amount * 2); - - // Send first message - (, uint256 index1) = state.testFeeJuicePortal.depositToAztecPublic(state.to, state.amount, state.secretHash1); - assertEq(index1, state.expectedIndex1, "First message index should be 0 (compact cumulative)"); - assertEq( - state.testInbox.getInProgress(), - state.expectedInProgress, - "inProgress should not change after sending first message" - ); - assertEq(state.testInbox.getTotalMessagesInserted(), 1, "Should have 1 message after first deposit"); - - // Send second message - (, uint256 index2) = state.testFeeJuicePortal.depositToAztecPublic(state.to, state.amount, state.secretHash2); - assertEq(index2, state.expectedIndex2, "Second message index should be 1 (compact cumulative)"); - assertEq( - state.testInbox.getInProgress(), - state.expectedInProgress, - "inProgress should not change after sending second message" - ); - assertEq(state.testInbox.getTotalMessagesInserted(), 2, "Should have 2 messages after second deposit"); - - // Test consume logic: consume checkpoints to verify lag behavior - // Initially: inProgress = INITIAL_CHECKPOINT_NUMBER + lag - // When checkpoint N is consumed: - // - If N + lag == inProgress, then inProgress advances to inProgress + 1 - // - Otherwise, inProgress stays the same - - // Consume checkpoints starting from INITIAL_CHECKPOINT_NUMBER - // We need to consume them sequentially to test the lag behavior - for ( - uint256 checkpointNum = Constants.INITIAL_CHECKPOINT_NUMBER; - checkpointNum < state.initialInProgress; - checkpointNum++ - ) { - uint256 inProgressBefore = state.testInbox.getInProgress(); - - // Call consume as the rollup - vm.prank(address(state.testRollup)); - bytes32 root = state.testInbox.consume(checkpointNum); - - uint256 inProgressAfter = state.testInbox.getInProgress(); - bool shouldAdvance = (checkpointNum + state.lag == inProgressBefore); - - if (shouldAdvance) { - assertEq( - inProgressAfter, inProgressBefore + 1, "inProgress should advance when checkpointNum + lag == inProgress" - ); - } else { - assertEq( - inProgressAfter, inProgressBefore, "inProgress should not advance when checkpointNum + lag != inProgress" - ); - } - - // Verify root is correct for non-initial checkpoints - if (checkpointNum > Constants.INITIAL_CHECKPOINT_NUMBER) { - assertEq(root, state.testInbox.getRoot(checkpointNum), "Root should match getRoot for checkpoint"); - } - } - - // After consuming all checkpoints up to the lag point, inProgress should have advanced - uint256 finalInProgress = state.testInbox.getInProgress(); - assertEq( - finalInProgress, - state.initialInProgress + state.lag, - "Final inProgress should advance by lag after consuming all checkpoints" - ); - } } 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/harnesses/InboxHarness.sol b/l1-contracts/test/harnesses/InboxHarness.sol index b7489a0d4a9f..983710628b48 100644 --- a/l1-contracts/test/harnesses/InboxHarness.sol +++ b/l1-contracts/test/harnesses/InboxHarness.sol @@ -4,43 +4,14 @@ pragma solidity >=0.8.27; import {Inbox} from "@aztec/core/messagebridge/Inbox.sol"; import {IERC20} from "@oz/token/ERC20/IERC20.sol"; -import {Constants} from "@aztec/core/libraries/ConstantsGen.sol"; -import {FrontierLib} from "@aztec/core/libraries/crypto/FrontierLib.sol"; contract InboxHarness is Inbox { - using FrontierLib for FrontierLib.Tree; - - constructor(address _rollup, IERC20 _feeAsset, uint256 _version, uint256 _height, uint256 _lag, uint256 _ringSize) - Inbox(_rollup, _feeAsset, _version, _height, _lag, _ringSize) + constructor(address _rollup, IERC20 _feeAsset, uint256 _version, uint256 _ringSize) + Inbox(_rollup, _feeAsset, _version, _ringSize) {} - function getSize() external view returns (uint256) { - return SIZE; - } - - function getEmptyRoot() external view returns (bytes32) { - return EMPTY_ROOT; - } - - function treeInProgressFull() external view returns (bool) { - return trees[state.inProgress].isFull(SIZE); - } - - function getToConsumeRoot(uint256 _toConsume) external view returns (bytes32) { - bytes32 root = EMPTY_ROOT; - if (_toConsume > Constants.INITIAL_CHECKPOINT_NUMBER) { - root = trees[_toConsume].root(forest, HEIGHT, SIZE); - } - return root; - } - - function getNumTrees() external view returns (uint256) { - // -INITIAL_CHECKPOINT_NUMBER because tree number INITIAL_CHECKPOINT_NUMBER is not real - return state.inProgress - Constants.INITIAL_CHECKPOINT_NUMBER; - } - function getNextMessageIndex() external view returns (uint256) { // Compact cumulative index: the next message's index is the count inserted so far. - return state.totalMessagesInserted; + return _totalMessagesInserted(); } } diff --git a/l1-contracts/test/harnesses/TestConstants.sol b/l1-contracts/test/harnesses/TestConstants.sol index 44aee456e1d4..dceae35f5020 100644 --- a/l1-contracts/test/harnesses/TestConstants.sol +++ b/l1-contracts/test/harnesses/TestConstants.sol @@ -25,7 +25,6 @@ library TestConstants { uint256 internal constant AZTEC_TARGET_COMMITTEE_SIZE = 48; uint256 internal constant AZTEC_LAG_IN_EPOCHS_FOR_VALIDATOR_SET = 3; uint256 internal constant AZTEC_LAG_IN_EPOCHS_FOR_RANDAO = 2; - uint256 internal constant AZTEC_INBOX_LAG = 2; uint256 internal constant AZTEC_INBOX_BUCKET_RING_SIZE = 1024; uint256 internal constant AZTEC_PROOF_SUBMISSION_EPOCHS = 1; uint256 internal constant AZTEC_SLASHING_QUORUM = 17; // Must be > ROUND_SIZE / 2 (ROUND_SIZE derived from @@ -133,7 +132,6 @@ library TestConstants { config.slashAmounts = slashAmounts; config.slasherEnabled = false; config.localEjectionThreshold = 0; - config.inboxLag = AZTEC_INBOX_LAG; // For the version we derive it based on the config (with a 0 version) // TODO(https://linear.app/aztec-labs/issue/TMNT-139/version-at-deployment) 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/portals/TokenPortal.t.sol b/l1-contracts/test/portals/TokenPortal.t.sol index ef51998626b8..a91f111003a5 100644 --- a/l1-contracts/test/portals/TokenPortal.t.sol +++ b/l1-contracts/test/portals/TokenPortal.t.sol @@ -11,7 +11,6 @@ import {DataStructures} from "@aztec/core/libraries/DataStructures.sol"; import {Errors} from "@aztec/core/libraries/Errors.sol"; import {Hash} from "@aztec/core/libraries/crypto/Hash.sol"; import {Epoch} from "@aztec/core/libraries/TimeLib.sol"; -import {TestConstants} from "../harnesses/TestConstants.sol"; import {Inbox} from "@aztec/core/messagebridge/Inbox.sol"; // Interfaces @@ -34,9 +33,6 @@ contract TokenPortalTest is Test { event MessageConsumed(bytes32 indexed messageHash, address indexed recipient); - uint256 internal constant FIRST_REAL_TREE_NUM = Constants.INITIAL_CHECKPOINT_NUMBER + TestConstants.AZTEC_INBOX_LAG; - uint256 internal constant L1_TO_L2_MSG_SUBTREE_SIZE = 2 ** Constants.L1_TO_L2_MSG_SUBTREE_HEIGHT; - Registry internal registry; RewardDistributor internal rewardDistributor; Inbox internal inbox; @@ -124,12 +120,11 @@ contract TokenPortalTest is Test { DataStructures.L1ToL2Msg memory expectedMessage = _createExpectedMintPrivateL1ToL2Message(expectedIndex); bytes32 expectedLeaf = expectedMessage.sha256ToField(); - bytes16 expectedHash = bytes16(keccak256(abi.encodePacked(inbox.getState().rollingHash, expectedLeaf))); bytes32 expectedInboxRollingHash = Hash.accumulateInboxRollingHash(bytes32(0), expectedLeaf); // Check the event was emitted vm.expectEmit(true, true, true, true); // event we expect - emit IInbox.MessageSent(FIRST_REAL_TREE_NUM, expectedIndex, expectedLeaf, expectedHash, expectedInboxRollingHash, 1); + emit IInbox.MessageSent(expectedIndex, expectedLeaf, expectedInboxRollingHash, 1); // event we will get // Perform op @@ -152,13 +147,12 @@ contract TokenPortalTest is Test { uint256 expectedIndex = 0; DataStructures.L1ToL2Msg memory expectedMessage = _createExpectedMintPublicL1ToL2Message(expectedIndex); bytes32 expectedLeaf = expectedMessage.sha256ToField(); - bytes16 expectedHash = bytes16(keccak256(abi.encodePacked(inbox.getState().rollingHash, expectedLeaf))); bytes32 expectedInboxRollingHash = Hash.accumulateInboxRollingHash(bytes32(0), expectedLeaf); // Check the event was emitted vm.expectEmit(true, true, true, true); // event we expect - emit IInbox.MessageSent(FIRST_REAL_TREE_NUM, expectedIndex, expectedLeaf, expectedHash, expectedInboxRollingHash, 1); + emit IInbox.MessageSent(expectedIndex, expectedLeaf, expectedInboxRollingHash, 1); // Perform op (bytes32 leaf, uint256 index) = tokenPortal.depositToAztecPublic(to, amount, secretHashForL2MessageConsumption); diff --git a/l1-contracts/test/rollup/ProposeInboxConsumption.t.sol b/l1-contracts/test/rollup/ProposeInboxConsumption.t.sol index 7d1e8a00c032..f3749c8ac3b2 100644 --- a/l1-contracts/test/rollup/ProposeInboxConsumption.t.sol +++ b/l1-contracts/test/rollup/ProposeInboxConsumption.t.sol @@ -6,11 +6,8 @@ import {Test} from "forge-std/Test.sol"; import {TestERC20} from "src/mock/TestERC20.sol"; import {IERC20} from "@oz/token/ERC20/IERC20.sol"; import {IInbox, MAX_MSGS_PER_BUCKET} from "@aztec/core/interfaces/messagebridge/IInbox.sol"; -import { - ProposeLib, - INBOX_LAG_SECONDS, - MAX_L1_TO_L2_MSGS_PER_CHECKPOINT -} from "@aztec/core/libraries/rollup/ProposeLib.sol"; +import {ProposeLib} from "@aztec/core/libraries/rollup/ProposeLib.sol"; +import {Constants} from "@aztec/core/libraries/ConstantsGen.sol"; import {TimeLib, Slot, Timestamp} from "@aztec/core/libraries/TimeLib.sol"; import {Errors} from "@aztec/core/libraries/Errors.sol"; import {DataStructures} from "@aztec/core/libraries/DataStructures.sol"; @@ -40,7 +37,6 @@ contract ProposeInboxConsumptionTest is Test { uint256 internal constant GENESIS_TIME = 100_000; uint256 internal constant SLOT_DURATION = 36; uint256 internal constant EPOCH_DURATION = 32; - uint256 internal constant HEIGHT = 10; Slot internal constant SLOT = Slot.wrap(10); @@ -51,7 +47,7 @@ contract ProposeInboxConsumptionTest is Test { // Start of the build frame for a checkpoint proposed in SLOT: it is built during the previous slot. uint256 internal buildFrameStart = GENESIS_TIME + (Slot.unwrap(SLOT) - 1) * SLOT_DURATION; // Buckets at or before the cutoff must be consumed by the checkpoint. - uint256 internal cutoff = buildFrameStart - INBOX_LAG_SECONDS; + uint256 internal cutoff = buildFrameStart - Constants.INBOX_LAG_SECONDS; function setUp() public { vm.warp(GENESIS_TIME); @@ -61,7 +57,7 @@ contract ProposeInboxConsumptionTest is Test { function _deployInbox(uint256 _ringSize) internal returns (InboxHarness) { IERC20 feeAsset = new TestERC20("Fee Asset", "FA", address(this)); - return new InboxHarness(address(rollup), feeAsset, version, HEIGHT, TestConstants.AZTEC_INBOX_LAG, _ringSize); + return new InboxHarness(address(rollup), feeAsset, version, _ringSize); } function _send(uint256 _salt) internal { @@ -126,7 +122,7 @@ contract ProposeInboxConsumptionTest is Test { // One more message than the checkpoint cap, all before the cutoff. They spill over into buckets // 1..4 of 256 (the per-bucket cap) plus bucket 5 with the single excess message. vm.warp(cutoff - 100); - _sendMany(MAX_L1_TO_L2_MSGS_PER_CHECKPOINT + 1); + _sendMany(Constants.MAX_L1_TO_L2_MSGS_PER_CHECKPOINT + 1); assertEq(inbox.getCurrentBucketSeq(), 5, "expected five buckets"); vm.warp(GENESIS_TIME + Slot.unwrap(SLOT) * SLOT_DURATION); @@ -135,12 +131,12 @@ contract ProposeInboxConsumptionTest is Test { // even though it is old. bytes32 endHash = inbox.getBucket(4).rollingHash; uint256 consumed = rollup.validateInboxConsumption(inbox, endHash, 4, SLOT, 0); - assertEq(consumed, MAX_L1_TO_L2_MSGS_PER_CHECKPOINT, "consumed the full cap"); + assertEq(consumed, Constants.MAX_L1_TO_L2_MSGS_PER_CHECKPOINT, "consumed the full cap"); } function testNoCapEscapeAtExactCap() public { vm.warp(cutoff - 100); - _sendMany(MAX_L1_TO_L2_MSGS_PER_CHECKPOINT + 1); + _sendMany(Constants.MAX_L1_TO_L2_MSGS_PER_CHECKPOINT + 1); vm.warp(GENESIS_TIME + Slot.unwrap(SLOT) * SLOT_DURATION); @@ -155,7 +151,7 @@ contract ProposeInboxConsumptionTest is Test { // Same layout as testCapEscape, but the parent checkpoint had already consumed one message: // buckets 2..5 then hold cap messages total, which fit in one checkpoint, so no escape from bucket 1. vm.warp(cutoff - 100); - _sendMany(MAX_L1_TO_L2_MSGS_PER_CHECKPOINT + 1); + _sendMany(Constants.MAX_L1_TO_L2_MSGS_PER_CHECKPOINT + 1); vm.warp(GENESIS_TIME + Slot.unwrap(SLOT) * SLOT_DURATION); @@ -289,14 +285,16 @@ contract ProposeInboxConsumptionTest is Test { // One more message than the checkpoint cap, all before the cutoff, referenced in a single proposal from // a fresh parent: the consumed delta exceeds what the circuits can insert. vm.warp(cutoff - 100); - _sendMany(MAX_L1_TO_L2_MSGS_PER_CHECKPOINT + 1); + _sendMany(Constants.MAX_L1_TO_L2_MSGS_PER_CHECKPOINT + 1); assertEq(inbox.getCurrentBucketSeq(), 5, "expected five buckets"); bytes32 endHash = inbox.getBucket(5).rollingHash; vm.warp(GENESIS_TIME + Slot.unwrap(SLOT) * SLOT_DURATION); vm.expectRevert( - abi.encodeWithSelector(Errors.Rollup__TooManyInboxMessagesConsumed.selector, MAX_L1_TO_L2_MSGS_PER_CHECKPOINT + 1) + abi.encodeWithSelector( + Errors.Rollup__TooManyInboxMessagesConsumed.selector, Constants.MAX_L1_TO_L2_MSGS_PER_CHECKPOINT + 1 + ) ); rollup.validateInboxConsumption(inbox, endHash, 5, SLOT, 0); } diff --git a/l1-contracts/test/script/DeployAztecL1Contracts.t.sol b/l1-contracts/test/script/DeployAztecL1Contracts.t.sol index b450e0a778b4..cd98934b568f 100644 --- a/l1-contracts/test/script/DeployAztecL1Contracts.t.sol +++ b/l1-contracts/test/script/DeployAztecL1Contracts.t.sol @@ -30,7 +30,6 @@ contract DeployAztecL1ContractsTest is Test { // Timing config vm.setEnv("AZTEC_SLOT_DURATION", vm.toString(json.readUint(".AZTEC_SLOT_DURATION"))); vm.setEnv("AZTEC_EPOCH_DURATION", vm.toString(json.readUint(".AZTEC_EPOCH_DURATION"))); - vm.setEnv("AZTEC_INBOX_LAG", vm.toString(json.readUint(".AZTEC_INBOX_LAG"))); vm.setEnv("AZTEC_PROOF_SUBMISSION_EPOCHS", vm.toString(json.readUint(".AZTEC_PROOF_SUBMISSION_EPOCHS"))); // Validator config diff --git a/l1-contracts/test/script/DeployRollupForUpgrade.t.sol b/l1-contracts/test/script/DeployRollupForUpgrade.t.sol index 11714808a0d3..0303ceb639f3 100644 --- a/l1-contracts/test/script/DeployRollupForUpgrade.t.sol +++ b/l1-contracts/test/script/DeployRollupForUpgrade.t.sol @@ -41,7 +41,6 @@ contract DeployRollupForUpgradeTest is Test { // Timing config vm.setEnv("AZTEC_SLOT_DURATION", vm.toString(json.readUint(".AZTEC_SLOT_DURATION"))); vm.setEnv("AZTEC_EPOCH_DURATION", vm.toString(json.readUint(".AZTEC_EPOCH_DURATION"))); - vm.setEnv("AZTEC_INBOX_LAG", vm.toString(json.readUint(".AZTEC_INBOX_LAG"))); vm.setEnv("AZTEC_PROOF_SUBMISSION_EPOCHS", vm.toString(json.readUint(".AZTEC_PROOF_SUBMISSION_EPOCHS"))); // Validator config diff --git a/l1-contracts/test/validator-selection/ValidatorSelection.t.sol b/l1-contracts/test/validator-selection/ValidatorSelection.t.sol index 19da2d10ca76..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)); - bytes32 inHash = inbox.getRoot(full.checkpoint.checkpointNumber); - header.inHash = inHash; header.gasFees.feePerL2Gas = manaMinFee; } diff --git a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/abis/l1_to_l2_message_bundle.nr b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/abis/l1_to_l2_message_bundle.nr index dda5fc4593c7..338d5106509e 100644 --- a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/abis/l1_to_l2_message_bundle.nr +++ b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/abis/l1_to_l2_message_bundle.nr @@ -1,8 +1,5 @@ use std::meta::derive; -use types::{ - constants::MAX_L1_TO_L2_MSGS_PER_BLOCK, - traits::{Deserialize, Empty, Serialize}, -}; +use types::{constants::MAX_L1_TO_L2_MSGS_PER_BLOCK, traits::{Deserialize, Empty, Serialize}}; /// A block's L1-to-L2 message bundle: the real message leaves it inserts into the L1-to-L2 message tree, and the /// count a block root needs to append them and absorb them into the message sponge. diff --git a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/abis/parity_public_inputs.nr b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/abis/parity_public_inputs.nr index af12795e2e98..b0634278a850 100644 --- a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/abis/parity_public_inputs.nr +++ b/noir-projects/fnd/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 @@ -27,7 +21,6 @@ pub struct ParityPublicInputs { impl Empty for ParityPublicInputs { fn empty() -> Self { ParityPublicInputs { - in_hash: 0, start_rolling_hash: 0, end_rolling_hash: 0, end_sponge: L1ToL2MessageSponge::empty(), diff --git a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/checkpoint_root/components/checkpoint_rollup_public_inputs_composer.nr b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/checkpoint_root/components/checkpoint_rollup_public_inputs_composer.nr index e5e7005c6e35..38e2af4568e6 100644 --- a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/checkpoint_root/components/checkpoint_rollup_public_inputs_composer.nr +++ b/noir-projects/fnd/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, @@ -112,8 +112,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, @@ -140,10 +140,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. inbox_rolling_hash: self.parity.end_rolling_hash, epoch_out_hash, slot_number: constants.slot_number, diff --git a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/checkpoint_root/tests/mod.nr b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/checkpoint_root/tests/mod.nr index 8f5d5b42ab06..1950daae8fa0 100644 --- a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/checkpoint_root/tests/mod.nr +++ b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/checkpoint_root/tests/mod.nr @@ -172,8 +172,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); @@ -423,7 +423,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/fnd/noir-protocol-circuits/crates/rollup-lib/src/parity/inbox_parity.nr b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/parity/inbox_parity.nr index 47e729aa3467..da1e61e772b2 100644 --- a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/parity/inbox_parity.nr +++ b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/parity/inbox_parity.nr @@ -16,10 +16,6 @@ pub struct InboxParityPrivateInputs { // Rolling hash of the Inbox chain before this checkpoint's messages (the previous checkpoint's end value; genesis // is zero). pub(crate) start_rolling_hash: Field, - // 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, } @@ -34,7 +30,6 @@ pub struct InboxParityPrivateInputs { /// equals the sponge the block roots accumulate). The sponge resets per checkpoint and there is one proof per /// checkpoint, so the start is always the empty sponge and is hard-coded rather than witnessed. /// - 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. /// @@ -50,7 +45,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, end_sponge, diff --git a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/parity/tests/inbox_parity_tests.nr b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/parity/tests/inbox_parity_tests.nr index 1b84dcd5242f..aa9a2cdb8f04 100644 --- a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/parity/tests/inbox_parity_tests.nr +++ b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/parity/tests/inbox_parity_tests.nr @@ -17,14 +17,11 @@ fn public_inputs_match_expected() { msgs, num_msgs: S, start_rolling_hash: 0, - 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); @@ -52,7 +49,6 @@ fn sponge_matches_block_root_real_count_absorb() { msgs, num_msgs: 2, start_rolling_hash: 0, - in_hash: 0, vk_tree_root: 42, prover_id: 7, }; @@ -76,7 +72,6 @@ fn non_zero_padding_lane_fails() { msgs, num_msgs: 2, start_rolling_hash: 0, - in_hash: 0, vk_tree_root: 42, prover_id: 7, }; @@ -89,7 +84,6 @@ fn num_msgs_greater_than_size_fails() { msgs: [0; S], num_msgs: S + 1, start_rolling_hash: 0, - in_hash: 0, vk_tree_root: 42, prover_id: 7, }; diff --git a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/tests/rollup_fixture_builder.nr b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/tests/rollup_fixture_builder.nr index 7024a296f1e9..648b18331913 100644 --- a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/tests/rollup_fixture_builder.nr +++ b/noir-projects/fnd/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. @@ -456,7 +456,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 ends at the checkpoint's accumulated sponge — the same diff --git a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-tx-merge/Prover.toml b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-tx-merge/Prover.toml index 5523912ffe8d..bdf88f58fcbb 100644 --- a/noir-projects/fnd/noir-protocol-circuits/crates/rollup-tx-merge/Prover.toml +++ b/noir-projects/fnd/noir-protocol-circuits/crates/rollup-tx-merge/Prover.toml @@ -484,27 +484,27 @@ proof = [ [inputs.previous_rollups.public_inputs] num_txs = "0x0000000000000000000000000000000000000000000000000000000000000001" - out_hash = "0x0018febbd74d861e38064a4ff9d3b5ed7a39b398576ef75e104848700819a700" + out_hash = "0x006bd7618b0cf7b40e3f107022eee2d411bcc5850fbb774dacc46a15957659c4" accumulated_fees = "0x0000000000000000000000000000000000000000000000000000000000000000" - accumulated_mana_used = "0x0000000000000000000000000000000000000000000000000000000000000000" + accumulated_mana_used = "0x000000000000000000000000000000000000000000000000000000000006b6c0" [inputs.previous_rollups.public_inputs.constants] - vk_tree_root = "0x11065543c9a42eac842466277ee9149b27403e398e94c6bba4f525931a2ac6bc" - protocol_contracts_hash = "0x24b2bd6e0456d2d2e64beb505010896a57017c6dedf7516d314d551720c3e6b4" + vk_tree_root = "0x0554f6effd553d130db46a6b8936b481c95ea2f460edde3b3d2af680b82715db" + protocol_contracts_hash = "0x08d0026a4983c49638d0910859942c6648d5ea0a3a152c8a376e4983d0a003d9" prover_id = "0x0000000000000000000000000000000000000000000000000000000000000000" [inputs.previous_rollups.public_inputs.constants.last_archive] - root = "0x26c404240cb9d3965fdd677de1d4ea1d907e460403e304ed198e839901a4cebc" - next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000002" + root = "0x0fb2945d3438d906d88a216364dbfe9760e96001343468610e01d18182d493d0" + next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000001" [inputs.previous_rollups.public_inputs.constants.l1_to_l2_tree_snapshot] - root = "0x2077efe63b8c3de3bfdbc1e1be837185a8f1d817c8321418fcfe110cd518a922" - next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000400" + root = "0x0c123047b777079e8db5f669ddaf1ffa1c7780d56b21e29f35e4772ac0f6280c" + next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000100" [inputs.previous_rollups.public_inputs.constants.global_variables] chain_id = "0x0000000000000000000000000000000000000000000000000000000000000000" version = "0x0000000000000000000000000000000000000000000000000000000000000000" - block_number = "0x0000000000000000000000000000000000000000000000000000000000000002" + block_number = "0x0000000000000000000000000000000000000000000000000000000000000001" slot_number = "0x000000000000000000000000000000000000000000000000000000000000000f" timestamp = "0x0000000000000000000000000000000000000000000000000000000000000186" @@ -531,110 +531,110 @@ root = "0x1a90881964e28a92a419f1d8361c14ac147b6f9175c04fdf57dadf0d7ba781c9" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000080" [inputs.previous_rollups.public_inputs.end_tree_snapshots.note_hash_tree] -root = "0x24543462563d01f3fa7d2995feb0568f0868807616f9135cbcec47610a688576" +root = "0x01612d24a146efc2df9d815a2f733c17486304424577ffb4232fe4cb0c94e1e7" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000040" [inputs.previous_rollups.public_inputs.end_tree_snapshots.nullifier_tree] -root = "0x0d5183688b388e23b4fe243d466e4d50acaf63d7afa00ca046fe2bf2e83db99d" +root = "0x2d9141720c810b831246b0e50c0ad9d4b935418ba2ae8a06c395bb80340ee684" next_available_leaf_index = "0x00000000000000000000000000000000000000000000000000000000000000c0" [inputs.previous_rollups.public_inputs.end_tree_snapshots.public_data_tree] -root = "0x27b8cdfd5211a289e0aa40da120fa969649354b3a0084d32d1ba1aca6b16f5b9" -next_available_leaf_index = "0x00000000000000000000000000000000000000000000000000000000000000bf" +root = "0x1a90881964e28a92a419f1d8361c14ac147b6f9175c04fdf57dadf0d7ba781c9" +next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000080" [inputs.previous_rollups.public_inputs.start_sponge_blob] - num_absorbed_fields = "0x0000000000000000000000000000000000000000000000000000000000000007" + num_absorbed_fields = "0x0000000000000000000000000000000000000000000000000000000000000000" [inputs.previous_rollups.public_inputs.start_sponge_blob.sponge] cache = [ - "0x2077efe63b8c3de3bfdbc1e1be837185a8f1d817c8321418fcfe110cd518a922", - "0x18935581a8ed73d08ffd00386fba55ba6c89f3ab848a76b8fedfa9034cee0454", - "0x1a90881964e28a92a419f1d8361c14ac147b6f9175c04fdf57dadf0d7ba781c9" + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000" ] state = [ - "0x0f19f1d5a0d014c8f9cb5070b6a72c7efb81b4971bc364738cea68076cfc9128", - "0x1e62eaea4ad0390925cb2382c5f4dc22f4d23495e0890061050f08c37ca8c9cd", - "0x143fae3a86f2ed36eca31eccf44ce149d098395a86dd8c9d541a15c155870153", - "0x2643afd339189b520e780ca47df76348b0d58825939d38dd1b1b0df9e8239947" + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000060000000000000000000" ] - cache_size = "0x0000000000000000000000000000000000000000000000000000000000000001" + cache_size = "0x0000000000000000000000000000000000000000000000000000000000000000" squeeze_mode = false [inputs.previous_rollups.public_inputs.end_sponge_blob] - num_absorbed_fields = "0x0000000000000000000000000000000000000000000000000000000000000552" + num_absorbed_fields = "0x00000000000000000000000000000000000000000000000000000000000004cd" [inputs.previous_rollups.public_inputs.end_sponge_blob.sponge] cache = [ - "0x00000000000000000000000000000000000000000000000000000000b7d1b34c", - "0x00000000000000000000000000000000000000000000000000000000b7d1b34d", - "0x00000000000000000000000000000000000000000000000000000000b7d1b34e" + "0x00000000000000000000000000000000000000000000000000000000b7d1b44d", + "0x00000000000000000000000000000000000000000000000000000000b7d1b44e", + "0x00000000000000000000000000000000000000000000000000000000b7d1b44c" ] state = [ - "0x0df722e23d212237afafa5c3ad409696de00ed5a6d040df1df5741c30783ab8d", - "0x1f51a080b417c648bdba25c5f1effbb13ef3cb2b3ad1c9f2650b842f7e08534a", - "0x0d5006c76bcd6126118b6ad75ec54e789213be33b6785d56921ac5bee5198c1e", - "0x26682892f284a033692202f2db9ee5552a727e91de309eb4aa74c20894def508" + "0x18134bd9cba7d2cfc160875a460f388c06c9be8bd2bdad23202426c0d0d985ec", + "0x181719b331f5a865a1ab500c089896a3606636dc0006adcf508a05a0ce702ccc", + "0x2099effc3095782b2705ec21faff150c85134e7cfa6b04af5868e014a4b9020c", + "0x2d212822493f22c35892994822a04497feb3a4e5ed82d4733e7972f4bed1958a" ] - cache_size = "0x0000000000000000000000000000000000000000000000000000000000000003" + cache_size = "0x0000000000000000000000000000000000000000000000000000000000000002" squeeze_mode = false [inputs.previous_rollups.vk_data] - leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000008" + leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000007" sibling_path = [ - "0x20ab775da5bb45344ffe17866237142b9321b97b2e93ccfb1c299476a262e0b5", - "0x2a9ef81b082ec4212c08eba71c46e645efc1ae573e9cca5b480785be9dcc4d21", - "0x18b9d1703288fad007e0e97212dedf18305ab172790fe8d5c7aeea5c2634673b", - "0x0481dbd69f3e084904030eb9771134ae7be848ec7b4af69e75933270ceeb544d", - "0x1fe698f1a6368c855525a9abcc3713bb49b1dec627f796bf4e23cc86473621ec", - "0x1c7b07f7b3f8344fece2e6a10bc9480dc4b90f89d2a51f344bee80ca5a4bda6e", - "0x12ed8e93726f8b5b79d1291566a72c1775861d60bba2daa19edb3f28d0ae7a3e" + "0x1fc39d0a428c8536dbca551ea79848acf67d89d090fd8c643c7d90f2e8f32340", + "0x12ce5a49a1ceca53ada7bee003f929bbd65abaa74e8072a81f304c2c96c44e31", + "0x276f60617ee2c3b406af56bfe9216a7d99be56ad316ff557cee0446f08e14135", + "0x1dab7bce1884db3c7fa09243adb445f93156a1a0546582123d8c1d6c89e7fe41", + "0x07ebb028f1e2f438160a9f6b62d7db60805be6119e0ba2d72158044a0c2a620c", + "0x0db3ab63c7556f8724d86819156701855018cc79140a052893d67fb67c99c1f1", + "0x058218a8605b041acde73bf034f139d830ed6b74dcb38f6ee06556729be2a765" ] [inputs.previous_rollups.vk_data.vk] key = [ - "0x0000000000000000000000000000000000000000000000000000000000000017", + "0x0000000000000000000000000000000000000000000000000000000000000016", "0x0000000000000000000000000000000000000000000000000000000000000042", "0x0000000000000000000000000000000000000000000000000000000000000005", - "0x0000000000000000000000000000001393476211d0127a80dce177bd2139b525", - "0x00000000000000000000000000000000000a64db7a774dad381e4034cb2457f3", - "0x000000000000000000000000000000d5fb425ef17d829bcbd2f2212397cc69ba", - "0x00000000000000000000000000000000002902ad5d5e880ce9d3ff8815ad1554", - "0x0000000000000000000000000000009b94b4b71ebf49ca1ccee622099eb37a96", - "0x0000000000000000000000000000000000217517f2a6d3c0ad2a63c69b28d392", - "0x00000000000000000000000000000038d8df60934ce84e0ed3e7f5ca09c1ee4d", - "0x000000000000000000000000000000000013384c8161e64209078ece69fcaa19", - "0x00000000000000000000000000000093c2a0c6ec49f39474bd6782c5cd6b01b2", - "0x000000000000000000000000000000000008ff5813561afc758759d27f19b55c", - "0x00000000000000000000000000000097291956465f3aa94d25b11bc567a6c287", - "0x000000000000000000000000000000000007f14a03af147b21368785d869ca98", - "0x0000000000000000000000000000006a811b91c5970c9c2acd7a754d7b7f9cb1", - "0x0000000000000000000000000000000000190a1ac15c24fdafc83b205b9a284b", - "0x000000000000000000000000000000b61bdc9c40c5c1e94e290d7b383b2ee122", - "0x00000000000000000000000000000000002f5117ca48de83b07881d30427d341", - "0x00000000000000000000000000000012095bbac1cead43059d0a230818a027b7", - "0x00000000000000000000000000000000001c7b3a018ad17b7cbb3752d56c6f80", - "0x00000000000000000000000000000018c18cff602c8434b90273cb1ff38dd751", - "0x000000000000000000000000000000000003bd5bfa0c05c005fcbde355422947", - "0x0000000000000000000000000000009dac7aa803151cb1966b812422755d8e9e", - "0x000000000000000000000000000000000010f818617b14b429abcd3acd46653c", - "0x0000000000000000000000000000007951c87d59a7b4319ddc32a19635675d94", - "0x000000000000000000000000000000000005be6b96d6eb897429b815388db99b", - "0x000000000000000000000000000000d7e7c851d3ba4e42b00b50dcb9a40159ff", - "0x00000000000000000000000000000000002750be703cf6981c927c48de088588", - "0x00000000000000000000000000000048b23b4fb077032ac487b882dec8a8c38b", - "0x000000000000000000000000000000000017150b214b8aa285fed8a5b374a4f9", - "0x000000000000000000000000000000416e099bfce67a459c5a20a4311705f6c4", - "0x00000000000000000000000000000000002b684b94d2f1f96e86dfad7af6171d", - "0x000000000000000000000000000000651160cf941fb73a15062e12bfc9e35804", - "0x000000000000000000000000000000000007a1bb9d8533d71515e6e3b5e6866d", + "0x00000000000000000000000000000002dae7e831ed78b9726034fdf56e583ace", + "0x00000000000000000000000000000000002b3f4d616e8027a20f8edc16cfbe3b", + "0x0000000000000000000000000000004c72b92cbd48ecea19ad5642f7d557f5a4", + "0x0000000000000000000000000000000000168654a45c7d8664e75623ab72ffab", + "0x00000000000000000000000000000083005b55cabaef097051546fbfd7685d43", + "0x0000000000000000000000000000000000085264859c243091d0290fee66961d", + "0x000000000000000000000000000000a3609a137f0c64e9af9d672c82bb879030", + "0x00000000000000000000000000000000002f2816311eb3afd6c30a91f05a906c", + "0x000000000000000000000000000000e4511cf49697e16a74e0d6730d364cd6e4", + "0x00000000000000000000000000000000000d9b15c1ab4aafa9870030fb1f01f1", + "0x0000000000000000000000000000006dde861e212bf26640ef1436d7bdc4f201", + "0x00000000000000000000000000000000001ffaf73c05b99d1722a697b54ea994", + "0x000000000000000000000000000000396ba274703ebf4a03e4339dbc26d099a0", + "0x0000000000000000000000000000000000022e3ee5ffc2dc0533c2bbeb9099ec", + "0x000000000000000000000000000000e6e227b60dc9057a88f8cb4471a9fea86a", + "0x0000000000000000000000000000000000246959db6c3a295438492d935eaacb", + "0x0000000000000000000000000000003191ffb83d7d42b1add187223d067540a7", + "0x00000000000000000000000000000000001c6597787b5ebaa53b48a9fecacda8", + "0x000000000000000000000000000000e9f6be3f78c38204280bc224a2fd27c65d", + "0x00000000000000000000000000000000001c3e420965e904cac372ef32dc7923", + "0x0000000000000000000000000000007da218400340a430b8cf011f7892173af8", + "0x00000000000000000000000000000000000a31a316504c8be7ae5701acb98ced", + "0x0000000000000000000000000000003cf75706f021e6a661df64ef16d9a42b55", + "0x0000000000000000000000000000000000177148b7da28c2bf2cfe5737c85963", + "0x000000000000000000000000000000e0bc19695db19d2b16f525fa63874ddd68", + "0x00000000000000000000000000000000000ad3d70c629c5446ac4b2065893923", + "0x0000000000000000000000000000001617d14d968bc2679b85973347963a6329", + "0x00000000000000000000000000000000002c77cb09381821d1d62a74c81bcafb", + "0x000000000000000000000000000000915addec4b02be7e5599bbb1f41fbb9e0f", + "0x000000000000000000000000000000000028bcf441b36723bf95e0be039533f0", + "0x000000000000000000000000000000954535ac3a2cb2b7ea19315aca8ffff3bd", + "0x00000000000000000000000000000000000473d4f91369383b78b9324f8d83a3", "0x0000000000000000000000000000001eee81b23a887f299049b14c11e98460d6", "0x00000000000000000000000000000000002a56ce41f6b0be13b9c26747621b82", "0x000000000000000000000000000000d5827d6338c78656c0d12ca1aea6ef2c7c", "0x00000000000000000000000000000000001aa98f2de3ddda547d8f6de4e725de", - "0x000000000000000000000000000000bada1572761d2d57e5d206da78afa472c5", - "0x00000000000000000000000000000000000248453a876b49502a5a8259818f44", - "0x000000000000000000000000000000ec45f0511c4ef04800c3d31e152e9265de", - "0x00000000000000000000000000000000001fff1bef8afe963c3b78c3c2549ca4", + "0x000000000000000000000000000000a0309c5acf411dabd67697d4075710b60f", + "0x0000000000000000000000000000000000017125ed54d5f16d50c2be55f8d80b", + "0x000000000000000000000000000000c3884b62c59c37f43e8acad4f15afdba7a", + "0x00000000000000000000000000000000001a3bfaf919de7e9b9af2d6281c6374", "0x0000000000000000000000000000006f206a04895661d3bd004222a1f8a7fc73", "0x00000000000000000000000000000000001b12a59a820d3aa543a594a1b9d92f", "0x0000000000000000000000000000002103559842aca1e08af33bb1f714ebc02a", @@ -655,60 +655,60 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x000000000000000000000000000000000021a0543de0bc42d6586454ec8b623b", "0x00000000000000000000000000000016bf0724359c9a09ceffdc9371a2d6e5dc", "0x00000000000000000000000000000000002fd4c13c6f5523a7f44b28ce3d342a", - "0x000000000000000000000000000000e379faf5ffdd383be5ce0f1ee38f5e73fe", - "0x00000000000000000000000000000000001429616496865cd170bc83c40d415a", - "0x00000000000000000000000000000066e08b7ad48415290d2ff590e84057e60e", - "0x000000000000000000000000000000000023a625e90ae9701e33c52be8f0d396", - "0x000000000000000000000000000000774fc954a6adaa4e4db1da6228184e1354", - "0x000000000000000000000000000000000015ca4073735e39fd5dc55567dc1068", - "0x000000000000000000000000000000fd9e5f594db01e4b2625e1e3dcfd400f6e", - "0x00000000000000000000000000000000000f4a392bbee96bddd468b2c9a18896", - "0x000000000000000000000000000000d28954aef28e666d63ee0fb401ab7bc1b6", - "0x0000000000000000000000000000000000253b48f95132d1a3183e26dbd56884", - "0x00000000000000000000000000000022887784410bf17c157fc004401281c342", - "0x00000000000000000000000000000000000a249d88427c50df84670228613493", - "0x0000000000000000000000000000000f495c15c8a9098c779fef9227f1c5a897", - "0x000000000000000000000000000000000016d74e40a54fe1961a01c0a275941e", - "0x000000000000000000000000000000a968320348365b69bc59ded07a855fb88c", - "0x000000000000000000000000000000000010c9846c3754b0fb4b4c9ab8d68cd3", - "0x00000000000000000000000000000088ee0f0a54893f8b1e8b56a7fa7bee9d4f", - "0x000000000000000000000000000000000021d14db4aa9ae9f88641b9336d9dba", - "0x0000000000000000000000000000000f46a800020405196bda0c07afe5154eac", - "0x0000000000000000000000000000000000062e98c038c271784c9fb61136242f", - "0x00000000000000000000000000000024ed87f3417bd5bbaa53682680b1b915c8", - "0x000000000000000000000000000000000003beb3d385de2c0a7a40cebe01d34a", - "0x000000000000000000000000000000f85b812b32412267c5221f3600eaa3f3d2", - "0x00000000000000000000000000000000000289ace5de4f3e7a721b5a184d1e76", - "0x0000000000000000000000000000002e30085e1c74fe70d1d2ab68faf0984ca4", - "0x00000000000000000000000000000000001adb7660db50eb3c9d70b137871b2f", - "0x0000000000000000000000000000008e05ae370a270eeb6d282e3bc2b7889cc3", - "0x000000000000000000000000000000000020e76f33b00bccd1f97095bece663a", - "0x000000000000000000000000000000d71d8a40f7c9f1984d513aa7eb1a895e6e", - "0x0000000000000000000000000000000000021aee4d83119d0af331d5893af9fe", - "0x000000000000000000000000000000dec3abe29c9aa4bf73251a0c748d11e9dc", - "0x0000000000000000000000000000000000153c1ed58f5394ad372869f471ca8b", - "0x000000000000000000000000000000bf4d6e6ed9dad1b05bd3d5cb3b3740d3e2", - "0x000000000000000000000000000000000006803b1ef715809ab5085220b7c284", - "0x00000000000000000000000000000041efece86e20c4323fb08d7aad7345c79c", - "0x0000000000000000000000000000000000096fab6acc87fe26e0eefb5660a7db", - "0x0000000000000000000000000000002d550983acbdea743f381bf4d55b8c6110", - "0x0000000000000000000000000000000000107455727dcf49837ee29622f4e9ae", - "0x000000000000000000000000000000a07bfb26d2f33a45866564f6fb630fc1b3", - "0x000000000000000000000000000000000004b58a9a3d784d5857e224b300b97a", - "0x000000000000000000000000000000c0f8d7d2fa6eac6b45b736e838db02e437", - "0x0000000000000000000000000000000000012728fef15749f6355802ac76d166", - "0x000000000000000000000000000000caa9dd21f786e491093e94a42326c20c4b", - "0x0000000000000000000000000000000000181c00a5273565ed45cde6516b7267", - "0x000000000000000000000000000000f791b0a7d2376523eebf266bd58c615a72", - "0x00000000000000000000000000000000001658c7b51e5eb76e58124a4bf26d30", - "0x0000000000000000000000000000007ffefc9b69f9b9f766f98d9390ad49a8f6", - "0x00000000000000000000000000000000002700900f023bdb2524f5ce218af8a6", - "0x0000000000000000000000000000001d12e2e9feedc5df84ac9cb8e405fc64ea", - "0x00000000000000000000000000000000001645d7c72fd8844ddf0659d2b77656", - "0x00000000000000000000000000000098eaadb9c8f156e98d8dfde6f90be22a80", - "0x000000000000000000000000000000000028291c89dafd88dced57d87f6d8ab7" + "0x000000000000000000000000000000af74c853140d3b0cf48ac5426ad44e3a5c", + "0x00000000000000000000000000000000000cb504c2196f73934d76e059397164", + "0x000000000000000000000000000000684b219a630e4770c224f7391ce903b574", + "0x000000000000000000000000000000000003d04c0a020da0ff25d4d81a4d60cc", + "0x0000000000000000000000000000003a547a2ea7e16e11451dbbeff89a46927d", + "0x00000000000000000000000000000000000fe460a80b11f07f28d016ffcbbdd5", + "0x000000000000000000000000000000ef84584cfec94dd3c5125887edc8b15648", + "0x00000000000000000000000000000000002775675418cc4ce82c6c45ee105a60", + "0x000000000000000000000000000000a78629fdc8d0021839d214273a985802a3", + "0x000000000000000000000000000000000010a06e7102f85337eb4c8b60d97c25", + "0x000000000000000000000000000000518765c5253492c980ea80aac7e9986f75", + "0x00000000000000000000000000000000000a622d7d27836ced89d7a2c1ff2967", + "0x0000000000000000000000000000006460b6c05ba8ae4c71040cd807a630f6d8", + "0x00000000000000000000000000000000002879f62ac90caf8d067aa57f0a2cb6", + "0x00000000000000000000000000000071bf565e74a124d3587a0d63adc7f29b32", + "0x0000000000000000000000000000000000122c536450106d4844375b13d9ba2a", + "0x0000000000000000000000000000009133f308fcba6ba2e78c78f1540f56e805", + "0x00000000000000000000000000000000001cabb18101f26c315098c437934f63", + "0x0000000000000000000000000000002622d42a77800505a1cb7d4d76702cf100", + "0x000000000000000000000000000000000006b02f8ebd2ac84a9c6991c39dab4d", + "0x00000000000000000000000000000018d1dbeba31941616af05128326ec86ea5", + "0x000000000000000000000000000000000007c9f24379b75927ccb4d7ad3b8c82", + "0x00000000000000000000000000000092277632f048f820823839ad073172915a", + "0x00000000000000000000000000000000000a09bb921f8ca99cb19752b1bac91d", + "0x000000000000000000000000000000d91ca36caa7bd53f0c6da30743948a8639", + "0x000000000000000000000000000000000021e7290c49f78addda4ccd9eef9c8c", + "0x00000000000000000000000000000081b433648af06686217c238ce4b13cf535", + "0x00000000000000000000000000000000002047531ed8535e381cffae559b6bb0", + "0x0000000000000000000000000000007ffdec7092f6d520c0209c640bfadc695d", + "0x00000000000000000000000000000000000a26fbb3d9d87f4ab3dd718c3bb75e", + "0x000000000000000000000000000000c636bdd1555ebce8fb6a9751c48376b7dd", + "0x0000000000000000000000000000000000017cb50edda28183e752c7e00faa40", + "0x000000000000000000000000000000ec2af0c236db172d1e5c00f7de73ae4fef", + "0x0000000000000000000000000000000000053be81fcb74e5cbb36dc0f630a49a", + "0x000000000000000000000000000000080baf1731ad04c68e93e7045d03ef4fd3", + "0x00000000000000000000000000000000001e593590b8049fe6542e11d3606aa5", + "0x000000000000000000000000000000575f11519d26bc15d34402b3331befb097", + "0x00000000000000000000000000000000001b7eed68e0e1c24fbf77cbfa588283", + "0x000000000000000000000000000000e1cdf4c6808f1f53377d276a0214b9af54", + "0x000000000000000000000000000000000002c44f6a43320eb961560c6a40262b", + "0x0000000000000000000000000000009f48843445d33387ce0134c37a9b82037a", + "0x00000000000000000000000000000000000a2852b8f7464eefbba66e39124b2f", + "0x00000000000000000000000000000077e865b0eef6500d62b5ed5b71795e1fdd", + "0x00000000000000000000000000000000001bc28e177eaebca799a38f9b96bd41", + "0x0000000000000000000000000000008a3623ef604d2b654cbc7b6f898ab36fd2", + "0x0000000000000000000000000000000000239c355843996ddcf4f3330873dc58", + "0x0000000000000000000000000000001fd35d9c3df35c0ab05822a8a7672f307d", + "0x000000000000000000000000000000000008eff9c124dc7092426dbfef4f367f", + "0x000000000000000000000000000000045c0a8172f53ba9a5c8e00908c493018e", + "0x00000000000000000000000000000000002ca001cbe15bf21d7bc13cb56f8272", + "0x0000000000000000000000000000001842209bfe3ad1cfba506322320f9c1a8f", + "0x000000000000000000000000000000000005e47c6d153729d8788d2a9c9cd993" ] - hash = "0x016a3b94e0b04a9f99ca3c31a475b4b5b1854bb2f3cea14fc49177110c9d7142" + hash = "0x035b62916d53f6a30d14dfd8245816108afd3d59028ddcf59607c26f190a855c" [[inputs.previous_rollups]] proof = [ @@ -1201,22 +1201,22 @@ proof = [ accumulated_mana_used = "0x0000000000000000000000000000000000000000000000000000000000000000" [inputs.previous_rollups.public_inputs.constants] - vk_tree_root = "0x11065543c9a42eac842466277ee9149b27403e398e94c6bba4f525931a2ac6bc" - protocol_contracts_hash = "0x24b2bd6e0456d2d2e64beb505010896a57017c6dedf7516d314d551720c3e6b4" + vk_tree_root = "0x0554f6effd553d130db46a6b8936b481c95ea2f460edde3b3d2af680b82715db" + protocol_contracts_hash = "0x08d0026a4983c49638d0910859942c6648d5ea0a3a152c8a376e4983d0a003d9" prover_id = "0x0000000000000000000000000000000000000000000000000000000000000000" [inputs.previous_rollups.public_inputs.constants.last_archive] - root = "0x26c404240cb9d3965fdd677de1d4ea1d907e460403e304ed198e839901a4cebc" - next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000002" + root = "0x0fb2945d3438d906d88a216364dbfe9760e96001343468610e01d18182d493d0" + next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000001" [inputs.previous_rollups.public_inputs.constants.l1_to_l2_tree_snapshot] - root = "0x2077efe63b8c3de3bfdbc1e1be837185a8f1d817c8321418fcfe110cd518a922" - next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000400" + root = "0x0c123047b777079e8db5f669ddaf1ffa1c7780d56b21e29f35e4772ac0f6280c" + next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000100" [inputs.previous_rollups.public_inputs.constants.global_variables] chain_id = "0x0000000000000000000000000000000000000000000000000000000000000000" version = "0x0000000000000000000000000000000000000000000000000000000000000000" - block_number = "0x0000000000000000000000000000000000000000000000000000000000000002" + block_number = "0x0000000000000000000000000000000000000000000000000000000000000001" slot_number = "0x000000000000000000000000000000000000000000000000000000000000000f" timestamp = "0x0000000000000000000000000000000000000000000000000000000000000186" @@ -1231,75 +1231,75 @@ proof = [ fee_per_l2_gas = "0x0000000000000000000000000000000000000000000000000000000000000000" [inputs.previous_rollups.public_inputs.start_tree_snapshots.note_hash_tree] -root = "0x24543462563d01f3fa7d2995feb0568f0868807616f9135cbcec47610a688576" +root = "0x01612d24a146efc2df9d815a2f733c17486304424577ffb4232fe4cb0c94e1e7" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000040" [inputs.previous_rollups.public_inputs.start_tree_snapshots.nullifier_tree] -root = "0x0d5183688b388e23b4fe243d466e4d50acaf63d7afa00ca046fe2bf2e83db99d" +root = "0x2d9141720c810b831246b0e50c0ad9d4b935418ba2ae8a06c395bb80340ee684" next_available_leaf_index = "0x00000000000000000000000000000000000000000000000000000000000000c0" [inputs.previous_rollups.public_inputs.start_tree_snapshots.public_data_tree] -root = "0x27b8cdfd5211a289e0aa40da120fa969649354b3a0084d32d1ba1aca6b16f5b9" -next_available_leaf_index = "0x00000000000000000000000000000000000000000000000000000000000000bf" +root = "0x1a90881964e28a92a419f1d8361c14ac147b6f9175c04fdf57dadf0d7ba781c9" +next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000080" [inputs.previous_rollups.public_inputs.end_tree_snapshots.note_hash_tree] -root = "0x092658df33d4badeaa54da3bee987ed4b7a973d285a96229bbd71c564cad7449" +root = "0x2326bf220c6839c1856478f0c082f0c5883b2baed0bc222a1fa5e1244184c82b" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000080" [inputs.previous_rollups.public_inputs.end_tree_snapshots.nullifier_tree] -root = "0x2fd0dfe2f0d0f4977a6c6d880237e4462686a8caf9e3eacf34b6a5159feac6f8" +root = "0x1e1c597744057b88e39a9780ed087c39b1fc42864e05ef03a59ebd9e96b70b00" next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000100" [inputs.previous_rollups.public_inputs.end_tree_snapshots.public_data_tree] -root = "0x1e18fe9a8c877ed096fe353567b6aef5b3dd4bbd987fec03c759c7cde4b3be5f" -next_available_leaf_index = "0x00000000000000000000000000000000000000000000000000000000000000fe" +root = "0x2306af8b455a9cbf87331182183be8c0759fd5e1a4f606cea8a9e24efa461759" +next_available_leaf_index = "0x00000000000000000000000000000000000000000000000000000000000000bf" [inputs.previous_rollups.public_inputs.start_sponge_blob] - num_absorbed_fields = "0x0000000000000000000000000000000000000000000000000000000000000552" + num_absorbed_fields = "0x00000000000000000000000000000000000000000000000000000000000004cd" [inputs.previous_rollups.public_inputs.start_sponge_blob.sponge] cache = [ - "0x00000000000000000000000000000000000000000000000000000000b7d1b34c", - "0x00000000000000000000000000000000000000000000000000000000b7d1b34d", - "0x00000000000000000000000000000000000000000000000000000000b7d1b34e" + "0x00000000000000000000000000000000000000000000000000000000b7d1b44d", + "0x00000000000000000000000000000000000000000000000000000000b7d1b44e", + "0x00000000000000000000000000000000000000000000000000000000b7d1b44c" ] state = [ - "0x0df722e23d212237afafa5c3ad409696de00ed5a6d040df1df5741c30783ab8d", - "0x1f51a080b417c648bdba25c5f1effbb13ef3cb2b3ad1c9f2650b842f7e08534a", - "0x0d5006c76bcd6126118b6ad75ec54e789213be33b6785d56921ac5bee5198c1e", - "0x26682892f284a033692202f2db9ee5552a727e91de309eb4aa74c20894def508" + "0x18134bd9cba7d2cfc160875a460f388c06c9be8bd2bdad23202426c0d0d985ec", + "0x181719b331f5a865a1ab500c089896a3606636dc0006adcf508a05a0ce702ccc", + "0x2099effc3095782b2705ec21faff150c85134e7cfa6b04af5868e014a4b9020c", + "0x2d212822493f22c35892994822a04497feb3a4e5ed82d4733e7972f4bed1958a" ] - cache_size = "0x0000000000000000000000000000000000000000000000000000000000000003" + cache_size = "0x0000000000000000000000000000000000000000000000000000000000000002" squeeze_mode = false [inputs.previous_rollups.public_inputs.end_sponge_blob] - num_absorbed_fields = "0x0000000000000000000000000000000000000000000000000000000000000a9d" + num_absorbed_fields = "0x0000000000000000000000000000000000000000000000000000000000000a18" [inputs.previous_rollups.public_inputs.end_sponge_blob.sponge] cache = [ - "0x00000000000000000000000000000000000000000000000000000000b7e5c34d", "0x00000000000000000000000000000000000000000000000000000000b7e5c34e", - "0x00000000000000000000000000000000000000000000000000000000b7e5c34c" + "0x00000000000000000000000000000000000000000000000000000000b7e5c34c", + "0x00000000000000000000000000000000000000000000000000000000b7e5c34d" ] state = [ - "0x0b7c9bbfac21d3c59031a865ee1f7abea9012d898a238d0b9d545e7e15be4f9a", - "0x11e47e7e5a97cace7b8cb5c63eeaf7cb076e5e19a4e89c1d3f4c3fb1faddf0b3", - "0x03a1357263e162410b5a70343adea4b373f3006660d9b40c28425be41749a8f7", - "0x100bc00d33720748142a7cd2ab57c5c140b681cdeec9b6197841f983ed05a3e2" + "0x066ed8d59656f317204a938dd07fa4468c320a6b111db7e1dca0f076722defc2", + "0x0435d82cba6a560012ad91adb17e18e1bbd0418b5829103b42df4247b06d8b58", + "0x04d968e8ea37d341ebbfb1a2135f97078e35f1bc03f478988064727a83d307ad", + "0x09d2cd576dd52ee863b4528b671d2e317262ae07136942de09eb54267049c116" ] - cache_size = "0x0000000000000000000000000000000000000000000000000000000000000002" + cache_size = "0x0000000000000000000000000000000000000000000000000000000000000001" squeeze_mode = false [inputs.previous_rollups.vk_data] leaf_index = "0x0000000000000000000000000000000000000000000000000000000000000008" sibling_path = [ - "0x20ab775da5bb45344ffe17866237142b9321b97b2e93ccfb1c299476a262e0b5", - "0x2a9ef81b082ec4212c08eba71c46e645efc1ae573e9cca5b480785be9dcc4d21", - "0x18b9d1703288fad007e0e97212dedf18305ab172790fe8d5c7aeea5c2634673b", - "0x0481dbd69f3e084904030eb9771134ae7be848ec7b4af69e75933270ceeb544d", - "0x1fe698f1a6368c855525a9abcc3713bb49b1dec627f796bf4e23cc86473621ec", - "0x1c7b07f7b3f8344fece2e6a10bc9480dc4b90f89d2a51f344bee80ca5a4bda6e", - "0x12ed8e93726f8b5b79d1291566a72c1775861d60bba2daa19edb3f28d0ae7a3e" + "0x10b6730f1d1e9c6bf8d7c4b42b64b40d2603e3ae6ddbd464c3d8fcfb9e06e6d4", + "0x11017f6408ff020bf1556ed3f4d41fba9dfe9fe312cd5430ec7c1abf7e3a9bcc", + "0x093393cd4297faa0e57bf0b8d98a1488e9dbf4e5faa27c9410505c75a590c41e", + "0x20f1c701d84b280c9f80a517272153688a9e1b92166d1000cfe7c829b7c25f69", + "0x07ebb028f1e2f438160a9f6b62d7db60805be6119e0ba2d72158044a0c2a620c", + "0x0db3ab63c7556f8724d86819156701855018cc79140a052893d67fb67c99c1f1", + "0x058218a8605b041acde73bf034f139d830ed6b74dcb38f6ee06556729be2a765" ] [inputs.previous_rollups.vk_data.vk] @@ -1307,46 +1307,46 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x0000000000000000000000000000000000000000000000000000000000000017", "0x0000000000000000000000000000000000000000000000000000000000000042", "0x0000000000000000000000000000000000000000000000000000000000000005", - "0x0000000000000000000000000000001393476211d0127a80dce177bd2139b525", - "0x00000000000000000000000000000000000a64db7a774dad381e4034cb2457f3", - "0x000000000000000000000000000000d5fb425ef17d829bcbd2f2212397cc69ba", - "0x00000000000000000000000000000000002902ad5d5e880ce9d3ff8815ad1554", - "0x0000000000000000000000000000009b94b4b71ebf49ca1ccee622099eb37a96", - "0x0000000000000000000000000000000000217517f2a6d3c0ad2a63c69b28d392", - "0x00000000000000000000000000000038d8df60934ce84e0ed3e7f5ca09c1ee4d", - "0x000000000000000000000000000000000013384c8161e64209078ece69fcaa19", - "0x00000000000000000000000000000093c2a0c6ec49f39474bd6782c5cd6b01b2", - "0x000000000000000000000000000000000008ff5813561afc758759d27f19b55c", - "0x00000000000000000000000000000097291956465f3aa94d25b11bc567a6c287", - "0x000000000000000000000000000000000007f14a03af147b21368785d869ca98", - "0x0000000000000000000000000000006a811b91c5970c9c2acd7a754d7b7f9cb1", - "0x0000000000000000000000000000000000190a1ac15c24fdafc83b205b9a284b", - "0x000000000000000000000000000000b61bdc9c40c5c1e94e290d7b383b2ee122", - "0x00000000000000000000000000000000002f5117ca48de83b07881d30427d341", - "0x00000000000000000000000000000012095bbac1cead43059d0a230818a027b7", - "0x00000000000000000000000000000000001c7b3a018ad17b7cbb3752d56c6f80", - "0x00000000000000000000000000000018c18cff602c8434b90273cb1ff38dd751", - "0x000000000000000000000000000000000003bd5bfa0c05c005fcbde355422947", - "0x0000000000000000000000000000009dac7aa803151cb1966b812422755d8e9e", - "0x000000000000000000000000000000000010f818617b14b429abcd3acd46653c", - "0x0000000000000000000000000000007951c87d59a7b4319ddc32a19635675d94", - "0x000000000000000000000000000000000005be6b96d6eb897429b815388db99b", - "0x000000000000000000000000000000d7e7c851d3ba4e42b00b50dcb9a40159ff", - "0x00000000000000000000000000000000002750be703cf6981c927c48de088588", - "0x00000000000000000000000000000048b23b4fb077032ac487b882dec8a8c38b", - "0x000000000000000000000000000000000017150b214b8aa285fed8a5b374a4f9", - "0x000000000000000000000000000000416e099bfce67a459c5a20a4311705f6c4", - "0x00000000000000000000000000000000002b684b94d2f1f96e86dfad7af6171d", - "0x000000000000000000000000000000651160cf941fb73a15062e12bfc9e35804", - "0x000000000000000000000000000000000007a1bb9d8533d71515e6e3b5e6866d", + "0x000000000000000000000000000000be3ef7df55faad770dd417b568b334bb61", + "0x0000000000000000000000000000000000020dbb5be28ee0dddbe361d4210575", + "0x0000000000000000000000000000002a9a30c0f428736335b3eb03271a7faa35", + "0x0000000000000000000000000000000000011eeab7deb21addfa7d56d1c588a1", + "0x000000000000000000000000000000cd7414d72fbdd24b1bad570941833708ea", + "0x0000000000000000000000000000000000125ab5466e467ad79893fe6fb71dac", + "0x000000000000000000000000000000275ffdc4a6a9c963cff9f4bfc97ff47ae5", + "0x000000000000000000000000000000000009b0fc1fdcf1eb4720e77df4bf579e", + "0x0000000000000000000000000000004b41b33ed09d6f2e0d1e2778bcc0a412bf", + "0x00000000000000000000000000000000002431de0fcb1f1e513962b6ddaff0cb", + "0x000000000000000000000000000000ceacbeb3107979529c12c1d0eee1557e98", + "0x000000000000000000000000000000000015157c061d1d61cf7dd3a7ce845922", + "0x0000000000000000000000000000003f7443a3cea90f0e6730b33675efbbda73", + "0x00000000000000000000000000000000002ca6b067638c1c24bbadd530a94798", + "0x000000000000000000000000000000f4d7b640687e4a07ea713f599fa766d14f", + "0x00000000000000000000000000000000001da3ebbf464dad0213d7cbb620146c", + "0x000000000000000000000000000000a5e9f66b74ff0bfcb7cb43d4ae9f154cf7", + "0x00000000000000000000000000000000001b50d6d924c13b8138405d313f1875", + "0x00000000000000000000000000000091dda1fdadf6e5bb631936d41c3e919572", + "0x0000000000000000000000000000000000218f9e93fa7efcdb8d89b8303acc50", + "0x000000000000000000000000000000f659c8815b22bacd330bee0c95d2a77cfd", + "0x00000000000000000000000000000000002aee8b729bc510e581461f3c3d3f20", + "0x000000000000000000000000000000d35405c664cefb21dd45302fa07ce6e1ae", + "0x0000000000000000000000000000000000168af130006e1ff22ea8737f0207e3", + "0x0000000000000000000000000000004b9beda85c743bfc49ac3aa711ccf95ca3", + "0x000000000000000000000000000000000028fb89c6da361c392f36475551e5c7", + "0x000000000000000000000000000000c5d8af9e91084202d30bd24e2ae1787dd1", + "0x00000000000000000000000000000000002ea1690f1652415522380675b4f298", + "0x00000000000000000000000000000004cc26069474a70fefc9e011a297d3659a", + "0x00000000000000000000000000000000002c4ca65f1f3ce11e955002f76823b7", + "0x000000000000000000000000000000caa9709b37a3f896da980148bd862f79ad", + "0x00000000000000000000000000000000001cdab1dc53ddf21433efb20a019aed", "0x0000000000000000000000000000001eee81b23a887f299049b14c11e98460d6", "0x00000000000000000000000000000000002a56ce41f6b0be13b9c26747621b82", "0x000000000000000000000000000000d5827d6338c78656c0d12ca1aea6ef2c7c", "0x00000000000000000000000000000000001aa98f2de3ddda547d8f6de4e725de", - "0x000000000000000000000000000000bada1572761d2d57e5d206da78afa472c5", - "0x00000000000000000000000000000000000248453a876b49502a5a8259818f44", - "0x000000000000000000000000000000ec45f0511c4ef04800c3d31e152e9265de", - "0x00000000000000000000000000000000001fff1bef8afe963c3b78c3c2549ca4", + "0x00000000000000000000000000000091be1f8e738e8cf74c39dcff97e574049a", + "0x0000000000000000000000000000000000253b1d48e89dea2387345d1f190cf8", + "0x00000000000000000000000000000001f523e64316ec12df1d1fed701b1fc3ff", + "0x0000000000000000000000000000000000254e8bc817fd854d25dce4ca34bbb9", "0x0000000000000000000000000000006f206a04895661d3bd004222a1f8a7fc73", "0x00000000000000000000000000000000001b12a59a820d3aa543a594a1b9d92f", "0x0000000000000000000000000000002103559842aca1e08af33bb1f714ebc02a", @@ -1367,57 +1367,57 @@ next_available_leaf_index = "0x0000000000000000000000000000000000000000000000000 "0x000000000000000000000000000000000021a0543de0bc42d6586454ec8b623b", "0x00000000000000000000000000000016bf0724359c9a09ceffdc9371a2d6e5dc", "0x00000000000000000000000000000000002fd4c13c6f5523a7f44b28ce3d342a", - "0x000000000000000000000000000000e379faf5ffdd383be5ce0f1ee38f5e73fe", - "0x00000000000000000000000000000000001429616496865cd170bc83c40d415a", - "0x00000000000000000000000000000066e08b7ad48415290d2ff590e84057e60e", - "0x000000000000000000000000000000000023a625e90ae9701e33c52be8f0d396", - "0x000000000000000000000000000000774fc954a6adaa4e4db1da6228184e1354", - "0x000000000000000000000000000000000015ca4073735e39fd5dc55567dc1068", - "0x000000000000000000000000000000fd9e5f594db01e4b2625e1e3dcfd400f6e", - "0x00000000000000000000000000000000000f4a392bbee96bddd468b2c9a18896", - "0x000000000000000000000000000000d28954aef28e666d63ee0fb401ab7bc1b6", - "0x0000000000000000000000000000000000253b48f95132d1a3183e26dbd56884", - "0x00000000000000000000000000000022887784410bf17c157fc004401281c342", - "0x00000000000000000000000000000000000a249d88427c50df84670228613493", - "0x0000000000000000000000000000000f495c15c8a9098c779fef9227f1c5a897", - "0x000000000000000000000000000000000016d74e40a54fe1961a01c0a275941e", - "0x000000000000000000000000000000a968320348365b69bc59ded07a855fb88c", - "0x000000000000000000000000000000000010c9846c3754b0fb4b4c9ab8d68cd3", - "0x00000000000000000000000000000088ee0f0a54893f8b1e8b56a7fa7bee9d4f", - "0x000000000000000000000000000000000021d14db4aa9ae9f88641b9336d9dba", - "0x0000000000000000000000000000000f46a800020405196bda0c07afe5154eac", - "0x0000000000000000000000000000000000062e98c038c271784c9fb61136242f", - "0x00000000000000000000000000000024ed87f3417bd5bbaa53682680b1b915c8", - "0x000000000000000000000000000000000003beb3d385de2c0a7a40cebe01d34a", - "0x000000000000000000000000000000f85b812b32412267c5221f3600eaa3f3d2", - "0x00000000000000000000000000000000000289ace5de4f3e7a721b5a184d1e76", - "0x0000000000000000000000000000002e30085e1c74fe70d1d2ab68faf0984ca4", - "0x00000000000000000000000000000000001adb7660db50eb3c9d70b137871b2f", - "0x0000000000000000000000000000008e05ae370a270eeb6d282e3bc2b7889cc3", - "0x000000000000000000000000000000000020e76f33b00bccd1f97095bece663a", - "0x000000000000000000000000000000d71d8a40f7c9f1984d513aa7eb1a895e6e", - "0x0000000000000000000000000000000000021aee4d83119d0af331d5893af9fe", - "0x000000000000000000000000000000dec3abe29c9aa4bf73251a0c748d11e9dc", - "0x0000000000000000000000000000000000153c1ed58f5394ad372869f471ca8b", - "0x000000000000000000000000000000bf4d6e6ed9dad1b05bd3d5cb3b3740d3e2", - "0x000000000000000000000000000000000006803b1ef715809ab5085220b7c284", - "0x00000000000000000000000000000041efece86e20c4323fb08d7aad7345c79c", - "0x0000000000000000000000000000000000096fab6acc87fe26e0eefb5660a7db", - "0x0000000000000000000000000000002d550983acbdea743f381bf4d55b8c6110", - "0x0000000000000000000000000000000000107455727dcf49837ee29622f4e9ae", - "0x000000000000000000000000000000a07bfb26d2f33a45866564f6fb630fc1b3", - "0x000000000000000000000000000000000004b58a9a3d784d5857e224b300b97a", - "0x000000000000000000000000000000c0f8d7d2fa6eac6b45b736e838db02e437", - "0x0000000000000000000000000000000000012728fef15749f6355802ac76d166", - "0x000000000000000000000000000000caa9dd21f786e491093e94a42326c20c4b", - "0x0000000000000000000000000000000000181c00a5273565ed45cde6516b7267", - "0x000000000000000000000000000000f791b0a7d2376523eebf266bd58c615a72", - "0x00000000000000000000000000000000001658c7b51e5eb76e58124a4bf26d30", - "0x0000000000000000000000000000007ffefc9b69f9b9f766f98d9390ad49a8f6", - "0x00000000000000000000000000000000002700900f023bdb2524f5ce218af8a6", - "0x0000000000000000000000000000001d12e2e9feedc5df84ac9cb8e405fc64ea", - "0x00000000000000000000000000000000001645d7c72fd8844ddf0659d2b77656", - "0x00000000000000000000000000000098eaadb9c8f156e98d8dfde6f90be22a80", - "0x000000000000000000000000000000000028291c89dafd88dced57d87f6d8ab7" + "0x00000000000000000000000000000063a793166969e30306b1a33ee3140ab639", + "0x00000000000000000000000000000000000689ae6c5351a0c7d2bddd28760eba", + "0x000000000000000000000000000000e9e058e78b28069998e0b545635c751505", + "0x000000000000000000000000000000000029d041f64f9c7a4b574afdd84eb2bc", + "0x000000000000000000000000000000de0e873aad4dce9c296b5822c49119b396", + "0x00000000000000000000000000000000001e622a80763bdce9e84c5936947ea4", + "0x000000000000000000000000000000ff979afed880f8777a1d7edb65ce1450e3", + "0x000000000000000000000000000000000012be4428cf92f37d4329f288edd6d7", + "0x000000000000000000000000000000d68061ce1bad9ac1e54cdf86b65d18b89f", + "0x000000000000000000000000000000000020fdf737ed00586f6db007044965a8", + "0x000000000000000000000000000000946bb34bc2173c2659e824f5fd377a3cbf", + "0x000000000000000000000000000000000022da84ec41699f355efa9e9550e4e4", + "0x0000000000000000000000000000008b0f4ef158c13087ed0e80688fe8373342", + "0x00000000000000000000000000000000002a4cae8c1411ba84463b850ab1844a", + "0x00000000000000000000000000000081d9bb13936c0676b987efe4a7ccd38a7e", + "0x00000000000000000000000000000000001e527e0c58ebec97285f8d47471778", + "0x000000000000000000000000000000c17602371eddd69a1e0e5b82a51441f2d3", + "0x000000000000000000000000000000000010fd74deb218f4d0d58aee589c31a6", + "0x000000000000000000000000000000dc8cd28e055e13b175149f797af895f0f4", + "0x000000000000000000000000000000000014d50f3e5a64cc7500ed82e02796e5", + "0x000000000000000000000000000000c61024b9433cc9b608636d33133501734f", + "0x0000000000000000000000000000000000080ffdff074611822ac0029ae9ac87", + "0x000000000000000000000000000000251c0f33e1f84d4365571cd965fc9b951c", + "0x00000000000000000000000000000000002e7e699ac6cc777970c0d5dd410bc0", + "0x0000000000000000000000000000009e1f4030259589018ce84f6f1dd736ae5d", + "0x000000000000000000000000000000000006154a1e0fea69f24bf7ac004afb7a", + "0x000000000000000000000000000000189020ea1ed822257549ab99ac2b33647e", + "0x00000000000000000000000000000000002a384f4831d00b56c6219abcb3b785", + "0x00000000000000000000000000000035fb02b0d2e793607a238e08539424c438", + "0x00000000000000000000000000000000001d0e75eecc618b087887f7d14ddf24", + "0x000000000000000000000000000000eae70e5ed566bcad446daa8f1059119fb5", + "0x00000000000000000000000000000000001f16d342675b048447aec1d841ea6f", + "0x000000000000000000000000000000dec8287d2ca445b6611951692aceb93f09", + "0x00000000000000000000000000000000000d237ea339d430164ddcf07c98eebb", + "0x00000000000000000000000000000069bd0705898ec802324165e180c23cca88", + "0x00000000000000000000000000000000002b2487b0a0cad2ab8b703eda79048b", + "0x000000000000000000000000000000526f89287c9fa8d0869c89341b300cd057", + "0x00000000000000000000000000000000000da86b460feb2479d72b91bad3aa4e", + "0x000000000000000000000000000000aba6055db467fa492b870fccd9251c0cb4", + "0x000000000000000000000000000000000022e849a8f64faebc36288439518d4b", + "0x000000000000000000000000000000ab679268e81d4770ddc1861ac7485ac4e8", + "0x000000000000000000000000000000000009a186caa2f074b8b5d3d26afddfc5", + "0x000000000000000000000000000000694fbc055e1f0045837b2bcf0d60f82934", + "0x00000000000000000000000000000000000ffba7e4a28991553a68d277bf2aa3", + "0x000000000000000000000000000000de4a494414853cc677e6f0de0f19a070c8", + "0x000000000000000000000000000000000004485188f222785e8455584b3632e6", + "0x0000000000000000000000000000001e68682a2d646f241b36838e8627fe4258", + "0x00000000000000000000000000000000001cd86d75f808b92947367db49e99b3", + "0x000000000000000000000000000000a21f994888d69ad0e60ea74b527f54a7ed", + "0x00000000000000000000000000000000001bbdaa75acb6eb653db71e1666d066", + "0x000000000000000000000000000000eb0694220d1a490792b0165b98d9b8bf71", + "0x000000000000000000000000000000000025729291a976e4cbed01962ee0b0d0" ] - hash = "0x016a3b94e0b04a9f99ca3c31a475b4b5b1854bb2f3cea14fc49177110c9d7142" + hash = "0x242e942739553143b651d041e96bc6bc5e09baa6142d959488ffa864ce063d69" diff --git a/noir-projects/fnd/noir-protocol-circuits/crates/types/src/abis/checkpoint_header.nr b/noir-projects/fnd/noir-protocol-circuits/crates/types/src/abis/checkpoint_header.nr index 59cb06c87a07..95443d82da75 100644 --- a/noir-projects/fnd/noir-protocol-circuits/crates/types/src/abis/checkpoint_header.nr +++ b/noir-projects/fnd/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: + // 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,8 @@ 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 +116,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 +133,8 @@ 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 +147,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 +166,8 @@ 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/fnd/noir-protocol-circuits/crates/types/src/constants.nr b/noir-projects/fnd/noir-protocol-circuits/crates/types/src/constants.nr index 24009923249f..700bc74207e7 100644 --- a/noir-projects/fnd/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/fnd/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; @@ -456,7 +451,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 */ @@ -677,12 +671,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; @@ -1445,7 +1433,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, }; @@ -1492,7 +1480,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/spartan/environments/bench-10tps.env b/spartan/environments/bench-10tps.env index 3e7899fe1916..fd3222ca3961 100644 --- a/spartan/environments/bench-10tps.env +++ b/spartan/environments/bench-10tps.env @@ -18,7 +18,6 @@ AZTEC_SLOT_DURATION=72 AZTEC_PROOF_SUBMISSION_EPOCHS=2 AZTEC_LAG_IN_EPOCHS_FOR_VALIDATOR_SET=1 AZTEC_LAG_IN_EPOCHS_FOR_RANDAO=1 -AZTEC_INBOX_LAG=2 # 2B mana target - good for about ~800 txs at 2.5M mana each AZTEC_MANA_TARGET=2000000000 diff --git a/spartan/environments/bench-inclusion-sweep.env b/spartan/environments/bench-inclusion-sweep.env index d11fa127d34c..8ae55bdf793b 100644 --- a/spartan/environments/bench-inclusion-sweep.env +++ b/spartan/environments/bench-inclusion-sweep.env @@ -19,7 +19,6 @@ AZTEC_SLOT_DURATION=72 AZTEC_PROOF_SUBMISSION_EPOCHS=100 AZTEC_LAG_IN_EPOCHS_FOR_VALIDATOR_SET=1 AZTEC_LAG_IN_EPOCHS_FOR_RANDAO=1 -AZTEC_INBOX_LAG=2 AZTEC_MANA_TARGET=2000000000 diff --git a/spartan/environments/network-defaults.yml b/spartan/environments/network-defaults.yml index 6b6bb4bf127b..a412219b9a34 100644 --- a/spartan/environments/network-defaults.yml +++ b/spartan/environments/network-defaults.yml @@ -59,8 +59,6 @@ l1-contracts: &l1-contracts-defaults #--------------------------------------------------------------------------- # Proof & Block Configuration #--------------------------------------------------------------------------- - # Checkpoints to lag in inbox (prevents sequencer DOS attacks). - AZTEC_INBOX_LAG: 2 # Epochs after end that proofs are still accepted. AZTEC_PROOF_SUBMISSION_EPOCHS: 1 # Target mana consumption per block. diff --git a/spartan/environments/next-net.env b/spartan/environments/next-net.env index de59eced52a6..4a6ba4281702 100644 --- a/spartan/environments/next-net.env +++ b/spartan/environments/next-net.env @@ -39,7 +39,6 @@ SEQ_MAX_TX_PER_CHECKPOINT=12 SEQ_BUILD_CHECKPOINT_IF_EMPTY=true SEQ_BLOCK_DURATION_MS=5500 -AZTEC_INBOX_LAG=2 VALIDATOR_REPLICAS=2 VALIDATORS_PER_NODE=24 diff --git a/spartan/environments/next-scenario.env b/spartan/environments/next-scenario.env index 1f5adb8ebf0f..b7b198419a04 100644 --- a/spartan/environments/next-scenario.env +++ b/spartan/environments/next-scenario.env @@ -7,7 +7,6 @@ DESTROY_ETH_DEVNET=true CREATE_ETH_DEVNET=true AZTEC_EPOCH_DURATION=32 AZTEC_SLOT_DURATION=36 -AZTEC_INBOX_LAG=2 ETHEREUM_CHAIN_ID=1337 LABS_INFRA_MNEMONIC="test test test test test test test test test test test junk" FUNDING_PRIVATE_KEY="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" diff --git a/spartan/environments/staging-internal.env b/spartan/environments/staging-internal.env index 69da3a5a1fd6..c3568e106c9c 100644 --- a/spartan/environments/staging-internal.env +++ b/spartan/environments/staging-internal.env @@ -36,7 +36,6 @@ SPONSORED_FPC=false # Rollup AZTEC_LAG_IN_EPOCHS_FOR_VALIDATOR_SET=2 AZTEC_LAG_IN_EPOCHS_FOR_RANDAO=2 -AZTEC_INBOX_LAG=2 AZTEC_MANA_TARGET=75000000 AZTEC_PROVING_COST_PER_MANA=12500000 diff --git a/spartan/environments/staging-public.env b/spartan/environments/staging-public.env index 7749f4f3533a..4da560112d34 100644 --- a/spartan/environments/staging-public.env +++ b/spartan/environments/staging-public.env @@ -36,7 +36,6 @@ SPONSORED_FPC=false # Rollup AZTEC_LAG_IN_EPOCHS_FOR_VALIDATOR_SET=2 AZTEC_LAG_IN_EPOCHS_FOR_RANDAO=2 -AZTEC_INBOX_LAG=2 AZTEC_MANA_TARGET=75000000 AZTEC_PROVING_COST_PER_MANA=12500000 diff --git a/spartan/scripts/deploy_network.sh b/spartan/scripts/deploy_network.sh index 5b574dfd8635..1165fc41a471 100755 --- a/spartan/scripts/deploy_network.sh +++ b/spartan/scripts/deploy_network.sh @@ -460,7 +460,6 @@ AZTEC_LAG_IN_EPOCHS_FOR_RANDAO = ${AZTEC_LAG_IN_EPOCHS_FOR_RANDAO:-null} AZTEC_SLOT_DURATION = ${AZTEC_SLOT_DURATION:-null} AZTEC_EPOCH_DURATION = ${AZTEC_EPOCH_DURATION:-null} AZTEC_TARGET_COMMITTEE_SIZE = ${AZTEC_TARGET_COMMITTEE_SIZE:-null} -AZTEC_INBOX_LAG = ${AZTEC_INBOX_LAG:-null} AZTEC_PROOF_SUBMISSION_EPOCHS = ${AZTEC_PROOF_SUBMISSION_EPOCHS:-null} AZTEC_ACTIVATION_THRESHOLD = ${AZTEC_ACTIVATION_THRESHOLD:-null} AZTEC_EJECTION_THRESHOLD = ${AZTEC_EJECTION_THRESHOLD:-null} diff --git a/spartan/terraform/deploy-rollup-contracts/main.tf b/spartan/terraform/deploy-rollup-contracts/main.tf index 690ef063b2e3..d1dd567cad99 100644 --- a/spartan/terraform/deploy-rollup-contracts/main.tf +++ b/spartan/terraform/deploy-rollup-contracts/main.tf @@ -43,7 +43,6 @@ locals { AZTEC_SLOT_DURATION = var.AZTEC_SLOT_DURATION AZTEC_EPOCH_DURATION = var.AZTEC_EPOCH_DURATION AZTEC_TARGET_COMMITTEE_SIZE = var.AZTEC_TARGET_COMMITTEE_SIZE - AZTEC_INBOX_LAG = var.AZTEC_INBOX_LAG AZTEC_PROOF_SUBMISSION_EPOCHS = var.AZTEC_PROOF_SUBMISSION_EPOCHS AZTEC_ACTIVATION_THRESHOLD = var.AZTEC_ACTIVATION_THRESHOLD AZTEC_EJECTION_THRESHOLD = var.AZTEC_EJECTION_THRESHOLD diff --git a/spartan/terraform/deploy-rollup-contracts/variables.tf b/spartan/terraform/deploy-rollup-contracts/variables.tf index f0031b7768ed..c40b8a2ef02a 100644 --- a/spartan/terraform/deploy-rollup-contracts/variables.tf +++ b/spartan/terraform/deploy-rollup-contracts/variables.tf @@ -86,12 +86,6 @@ variable "AZTEC_PROOF_SUBMISSION_EPOCHS" { nullable = true } -variable "AZTEC_INBOX_LAG" { - description = "Checkpoints to lag in inbox (prevents sequencer DOS attacks)" - type = string - nullable = true -} - variable "AZTEC_ACTIVATION_THRESHOLD" { description = "Aztec activation threshold" type = string diff --git a/yarn-project/archiver/README.md b/yarn-project/archiver/README.md index 959f16f819dc..29735e31f2ea 100644 --- a/yarn-project/archiver/README.md +++ b/yarn-project/archiver/README.md @@ -44,9 +44,9 @@ Two independent syncpoints track progress on L1: ### L1-to-L2 Messages -Messages are synced from the Inbox contract. The sync compares local state (message count and rolling hash) against the Inbox contract state on L1, downloads any missing messages, and verifies consistency afterwards. On success, the syncpoint advances to the current L1 block. On failure (L1 reorg or inconsistency), the syncpoint rolls back to the last known-good message and the operation retries (up to 3 times within the same sync iteration). +Messages are synced from the Inbox contract. The sync compares local state (message count and consensus rolling hash) against the Inbox's current rolling-hash bucket on L1, downloads any missing messages, and verifies consistency afterwards. On success, the syncpoint advances to the current L1 block. On failure (L1 reorg or inconsistency), the syncpoint rolls back to the last known-good message and the operation retries (up to 3 times within the same sync iteration). -1. Query Inbox state at the current L1 block (message count + rolling hash) +1. Query the Inbox's current bucket at the current L1 block (cumulative message count + consensus rolling hash) 2. Compare local state against remote 3. If they match, advance syncpoint and return 4. If mismatch, fetch `MessageSent` events in batches and store them @@ -55,7 +55,7 @@ Messages are synced from the Inbox contract. The sync compares local state (mess - If still mismatched (e.g., messages missed due to a concurrent L1 reorg), rollback and retry 6. On success, advance the syncpoint -The syncpoint and the `inboxTreeInProgress` marker (which tracks which checkpoint's messages are currently being filled on L1) are updated atomically. The marker is only advanced after messages are stored, so concurrent reads don't see an unsealed checkpoint as readable before its messages are available. +Messages are stored with compact (unpadded) global indices matching the Inbox's insertion order, and each carries the consensus rolling hash (a truncated sha256 chain) and the sequence of the Inbox bucket it was absorbed into (AZIP-22 Fast Inbox). Bucket snapshots let the sequencer and validator resolve message bundles per block. ### Checkpoints @@ -70,13 +70,12 @@ Checkpoints are synced from the Rollup contract via `handleCheckpoints()`: - Verify archive matches (checkpoint still in chain) - Validate attestations (2/3 + 1 committee signatures required) - Skip invalid checkpoints (see "Invalid Checkpoints" in Edge Cases) - - Verify `inHash` matches expected value (see below) - Store valid checkpoints with their blocks 6. Update proven checkpoint again (may have advanced after storing new checkpoints) 7. Handle epoch prune if applicable 8. Check for checkpoints behind syncpoint (L1 reorg case) -The `inHash` is a hash of all L1-to-L2 messages consumed by a checkpoint. The archiver computes the expected `inHash` from locally stored messages and compares it against the checkpoint header. A mismatch indicates a bug (messages out of sync with checkpoints) and causes a fatal error. +L1 enforces at propose time that a checkpoint header's consensus rolling hash matches the Inbox bucket the checkpoint consumes through (AZIP-22 Fast Inbox), so the archiver does not re-derive or cross-check a per-checkpoint message hash while syncing. The `blocksSynchedTo` syncpoint is updated: - When checkpoints are stored: set to the L1 block of the last stored checkpoint diff --git a/yarn-project/archiver/src/archiver-sync.test.ts b/yarn-project/archiver/src/archiver-sync.test.ts index 3cfffde67ee8..05120d1a9798 100644 --- a/yarn-project/archiver/src/archiver-sync.test.ts +++ b/yarn-project/archiver/src/archiver-sync.test.ts @@ -441,22 +441,22 @@ describe('Archiver Sync', () => { logger.warn('Initial sync'); await archiver.syncImmediate(); - expect(inboxContract.getState).toHaveBeenCalledTimes(1); + expect(inboxContract.getCurrentBucket).toHaveBeenCalledTimes(1); expect(rollupContract.status).toHaveBeenCalledTimes(1); - inboxContract.getState.mockClear(); + inboxContract.getCurrentBucket.mockClear(); rollupContract.status.mockClear(); // We sync again, but since chain didn't move, no new calls should be expected logger.warn('Sync with no L1 advancement'); await archiver.syncImmediate(); - expect(inboxContract.getState).toHaveBeenCalledTimes(0); + expect(inboxContract.getCurrentBucket).toHaveBeenCalledTimes(0); expect(rollupContract.status).toHaveBeenCalledTimes(0); // Advance the chain and we should see calls again fake.setL1BlockNumber(150n); logger.warn('Sync after L1 advancement'); await archiver.syncImmediate(); - expect(inboxContract.getState).toHaveBeenCalledTimes(1); + expect(inboxContract.getCurrentBucket).toHaveBeenCalledTimes(1); expect(rollupContract.status).toHaveBeenCalledTimes(1); }); diff --git a/yarn-project/archiver/src/archiver.ts b/yarn-project/archiver/src/archiver.ts index c642e8b3851f..199ae4f89c2a 100644 --- a/yarn-project/archiver/src/archiver.ts +++ b/yarn-project/archiver/src/archiver.ts @@ -765,14 +765,14 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra `Removing checkpoints after checkpoint ${targetCheckpointNumber} (target block ${targetL2BlockNumber})`, ); await this.updater.removeCheckpointsAfter(targetCheckpointNumber); - this.log.info(`Rolling back L1 to L2 messages to checkpoint ${targetCheckpointNumber}`); - await this.stores.messages.rollbackL1ToL2MessagesToCheckpoint(targetCheckpointNumber); + this.log.info(`Rolling back L1 to L2 messages inserted after L1 block ${targetL1BlockNumber}`); + await this.stores.messages.rollbackL1ToL2MessagesAfterL1Block(targetL1BlockNumber); this.log.info(`Setting L1 syncpoints to ${targetL1BlockNumber}`); await this.stores.blocks.setSynchedL1BlockNumber(targetL1BlockNumber); - await this.stores.messages.setMessageSyncState( - { l1BlockNumber: targetL1BlockNumber, l1BlockHash: targetL1BlockHash }, - undefined, - ); + await this.stores.messages.setMessageSyncState({ + l1BlockNumber: targetL1BlockNumber, + l1BlockHash: targetL1BlockHash, + }); if (targetL2BlockNumber < currentProvenBlock) { this.log.info(`Rolling back proven L2 checkpoint to ${targetCheckpointNumber}`); await this.updater.setProvenCheckpointNumber(targetCheckpointNumber); diff --git a/yarn-project/archiver/src/errors.ts b/yarn-project/archiver/src/errors.ts index 29f4de4e84b6..60661d9ea296 100644 --- a/yarn-project/archiver/src/errors.ts +++ b/yarn-project/archiver/src/errors.ts @@ -102,20 +102,6 @@ export class BlockAlreadyCheckpointedError extends Error { } } -/** Thrown when L1 to L2 messages are requested for a checkpoint whose message tree hasn't been sealed yet. */ -export class L1ToL2MessagesNotReadyError extends Error { - constructor( - public readonly checkpointNumber: number, - public readonly inboxTreeInProgress: bigint, - ) { - super( - `Cannot get L1 to L2 messages for checkpoint ${checkpointNumber}: ` + - `inbox tree in progress is ${inboxTreeInProgress}, messages not yet sealed`, - ); - this.name = 'L1ToL2MessagesNotReadyError'; - } -} - /** * Thrown when a query names an Inbox bucket this archiver has not synced. Distinguishes "not synced yet, retry once * L1 sync catches up" from a genuinely empty result. diff --git a/yarn-project/archiver/src/l1/calldata_retriever.test.ts b/yarn-project/archiver/src/l1/calldata_retriever.test.ts index 10a2103cc253..ce2fa5c3f3c3 100644 --- a/yarn-project/archiver/src/l1/calldata_retriever.test.ts +++ b/yarn-project/archiver/src/l1/calldata_retriever.test.ts @@ -140,7 +140,7 @@ describe('CalldataRetriever', () => { archive, oracleInput: { feeAssetPriceModifier: BigInt(0) }, header: viemHeader, - bucketHint: 0n, + bucketHint: BigInt(0), }, attestations, signers, @@ -369,7 +369,7 @@ describe('CalldataRetriever', () => { archive, oracleInput: { feeAssetPriceModifier }, header, - bucketHint: 0n, + bucketHint: BigInt(0), }, attestations, [], // signers @@ -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/l1/data_retrieval.ts b/yarn-project/archiver/src/l1/data_retrieval.ts index c6f7c8b8941b..7f0602f06fb4 100644 --- a/yarn-project/archiver/src/l1/data_retrieval.ts +++ b/yarn-project/archiver/src/l1/data_retrieval.ts @@ -363,7 +363,7 @@ export async function retrieveL1ToL2Message( * @param inbox - The inbox contract wrapper. * @param searchStartBlock - The block number to use for starting the search. * @param searchEndBlock - The highest block number that we should search up to. - * @returns An array of InboxLeaf and next eth block to search from. + * @returns The L1 to L2 messages retrieved from the Inbox, as an array of InboxMessage. */ export async function retrieveL1ToL2Messages( inbox: InboxContract, @@ -391,8 +391,6 @@ function mapLogInboxMessage(log: MessageSentLog): InboxMessage { leaf: log.args.leaf, l1BlockNumber: log.l1BlockNumber, l1BlockHash: log.l1BlockHash, - checkpointNumber: log.args.checkpointNumber, - rollingHash: log.args.rollingHash, inboxRollingHash: log.args.inboxRollingHash, bucketSeq: log.args.bucketSeq, bucketTimestamp: log.l1BlockTimestamp, diff --git a/yarn-project/archiver/src/modules/data_source_base.ts b/yarn-project/archiver/src/modules/data_source_base.ts index 9d2256c7baa6..9e407d9e6765 100644 --- a/yarn-project/archiver/src/modules/data_source_base.ts +++ b/yarn-project/archiver/src/modules/data_source_base.ts @@ -316,10 +316,6 @@ export abstract class ArchiverDataSourceBase return this.stores.functionNames.register(signatures); } - public getL1ToL2Messages(checkpointNumber: CheckpointNumber): Promise { - return this.stores.messages.getL1ToL2Messages(checkpointNumber); - } - public getL1ToL2MessageIndex(l1ToL2Message: Fr): Promise { return this.stores.messages.getL1ToL2MessageIndex(l1ToL2Message); } diff --git a/yarn-project/archiver/src/modules/l1_synchronizer.ts b/yarn-project/archiver/src/modules/l1_synchronizer.ts index ba5876f77500..be1bbf0b2d17 100644 --- a/yarn-project/archiver/src/modules/l1_synchronizer.ts +++ b/yarn-project/archiver/src/modules/l1_synchronizer.ts @@ -1,13 +1,13 @@ import type { BlobClientInterface } from '@aztec/blob-client/client'; import { EpochCache } from '@aztec/epoch-cache'; -import { InboxContract, type InboxContractState, RollupContract } from '@aztec/ethereum/contracts'; +import { InboxContract, type InboxContractBucket, RollupContract } from '@aztec/ethereum/contracts'; import type { L1BlockId } from '@aztec/ethereum/l1-types'; import { getFinalizedL1Block } from '@aztec/ethereum/queries'; import type { ViemPublicClient, ViemPublicDebugClient } from '@aztec/ethereum/types'; import { asyncPool } from '@aztec/foundation/async-pool'; import { maxBigint } from '@aztec/foundation/bigint'; import { BlockNumber, CheckpointNumber, EpochNumber } from '@aztec/foundation/branded-types'; -import { Buffer16, Buffer32 } from '@aztec/foundation/buffer'; +import { Buffer32 } from '@aztec/foundation/buffer'; import { compactArray, partition, pick } from '@aztec/foundation/collection'; import { Fr } from '@aztec/foundation/curves/bn254'; import { EthAddress } from '@aztec/foundation/eth-address'; @@ -430,16 +430,14 @@ export class ArchiverL1Synchronizer implements Traceable { return true; } - // Compare local message store state with the remote. If they match, we just advance the match pointer. - const remoteMessagesState = await this.inbox.getState({ blockNumber: currentL1BlockNumber }); + // Compare local message store state with the remote. If they match, we just advance the match pointer. The + // remote state is the Inbox's current bucket: its cumulative total and consensus rolling + // hash are the Inbox's live chain position. + const remoteBucket = await this.inbox.getCurrentBucket({ blockNumber: currentL1BlockNumber }); const localLastMessage = await this.stores.messages.getLastMessage(); - if (await this.localStateMatches(localLastMessage, remoteMessagesState)) { + if (await this.localStateMatches(localLastMessage, remoteBucket)) { this.log.trace(`Local L1 to L2 messages are already in sync with remote at L1 block ${currentL1BlockNumber}`); - await this.stores.messages.setMessageSyncState( - currentL1Block, - remoteMessagesState.treeInProgress, - finalizedL1Block, - ); + await this.stores.messages.setMessageSyncState(currentL1Block, finalizedL1Block); return true; } @@ -455,7 +453,7 @@ export class ArchiverL1Synchronizer implements Traceable { `Failed to store L1 to L2 messages retrieved from L1: ${error.message}. Rolling back syncpoint to retry.`, { inboxMessage: error.inboxMessage }, ); - await this.rollbackL1ToL2Messages(remoteMessagesState); + await this.rollbackL1ToL2Messages(remoteBucket); return false; } throw error; @@ -465,32 +463,28 @@ export class ArchiverL1Synchronizer implements Traceable { // we'd notice by comparing our local state with the remote one again, and seeing they don't match even after // our sync attempt. In this case, we also rollback our syncpoint, and trigger a retry. const localLastMessageAfterSync = await this.stores.messages.getLastMessage(); - if (!(await this.localStateMatches(localLastMessageAfterSync, remoteMessagesState))) { + if (!(await this.localStateMatches(localLastMessageAfterSync, remoteBucket))) { this.log.warn( `Local L1 to L2 messages state does not match remote after sync attempt. Rolling back syncpoint to retry.`, - { localLastMessageAfterSync, remoteMessagesState }, + { localLastMessageAfterSync, remoteBucket }, ); - await this.rollbackL1ToL2Messages(remoteMessagesState); + await this.rollbackL1ToL2Messages(remoteBucket); return false; } // Advance the syncpoint after a successful sync - await this.stores.messages.setMessageSyncState( - currentL1Block, - remoteMessagesState.treeInProgress, - finalizedL1Block, - ); + await this.stores.messages.setMessageSyncState(currentL1Block, finalizedL1Block); return true; } - /** Checks if the local rolling hash and message count matches the remote state */ - private async localStateMatches(localLastMessage: InboxMessage | undefined, remoteState: InboxContractState) { + /** Checks if the local consensus rolling hash and message count match the remote Inbox current bucket. */ + private async localStateMatches(localLastMessage: InboxMessage | undefined, remoteBucket: InboxContractBucket) { const localMessageCount = await this.stores.messages.getTotalL1ToL2MessageCount(); - this.log.trace(`Comparing local and remote inbox state`, { localMessageCount, localLastMessage, remoteState }); + this.log.trace(`Comparing local and remote inbox state`, { localMessageCount, localLastMessage, remoteBucket }); return ( - remoteState.totalMessagesInserted === localMessageCount && - remoteState.messagesRollingHash.equals(localLastMessage?.rollingHash ?? Buffer16.ZERO) + remoteBucket.totalMsgCount === localMessageCount && + remoteBucket.rollingHash.equals(localLastMessage?.inboxRollingHash ?? Fr.ZERO) ); } @@ -521,10 +515,10 @@ export class ArchiverL1Synchronizer implements Traceable { } while (searchEndBlock < toL1Block); if (messageCount > 0) { - this.log.info( - `Retrieved ${messageCount} new L1 to L2 messages up to message with index ${lastMessage?.index} for checkpoint ${lastMessage?.checkpointNumber}`, - { lastMessage, messageCount }, - ); + this.log.info(`Retrieved ${messageCount} new L1 to L2 messages up to message with index ${lastMessage?.index}`, { + lastMessage, + messageCount, + }); } } @@ -532,8 +526,8 @@ export class ArchiverL1Synchronizer implements Traceable { * Rolls back local L1 to L2 messages to the last common message with L1, and updates the syncpoint to the L1 block of that message. * If no common message is found, rolls back all messages and sets the syncpoint to the start block. */ - private async rollbackL1ToL2Messages(remoteMessagesState: InboxContractState): Promise { - const { treeInProgress: remoteTreeInProgress, messagesRollingHash: remoteRollingHash } = remoteMessagesState; + private async rollbackL1ToL2Messages(remoteBucket: InboxContractBucket): Promise { + const remoteRollingHash = remoteBucket.rollingHash; const messagesFinalizedL1Block = await this.stores.messages.getMessagesFinalizedL1Block(); const finalizedL1BlockNumber = messagesFinalizedL1Block?.l1BlockNumber; @@ -545,12 +539,12 @@ export class ArchiverL1Synchronizer implements Traceable { let messagesToDelete = 0; this.log.verbose(`Searching most recent common L1 to L2 message`); for await (const localMsg of this.stores.messages.iterateL1ToL2Messages({ reverse: true })) { - const logCtx = { remoteMsg: undefined as InboxMessage | undefined, localMsg, remoteMessagesState }; + const logCtx = { remoteMsg: undefined as InboxMessage | undefined, localMsg, remoteBucket }; // First check if the local message rolling hash matches the current rolling hash of the inbox contract, // which means we just need to rollback some local messages and we should be back in sync. This means there // was an L1 reorg that removed some of the messages we had, but no new messages were added compared. - if (localMsg.rollingHash.equals(remoteRollingHash)) { + if (localMsg.inboxRollingHash.equals(remoteRollingHash)) { this.log.info( `Found common L1 to L2 message at index ${localMsg.index} on L1 block ${localMsg.l1BlockNumber} matching current remote state`, logCtx, @@ -573,7 +567,7 @@ export class ArchiverL1Synchronizer implements Traceable { // an archival rpc node, since the message could be from a long time ago if we're catching up with syncing. const remoteMsg = await retrieveL1ToL2Message(this.inbox, localMsg); logCtx.remoteMsg = remoteMsg; - if (remoteMsg && remoteMsg.rollingHash.equals(localMsg.rollingHash)) { + if (remoteMsg && remoteMsg.inboxRollingHash.equals(localMsg.inboxRollingHash)) { this.log.info( `Found most recent common L1 to L2 message at index ${localMsg.index} on L1 block ${localMsg.l1BlockNumber}`, logCtx, @@ -615,10 +609,9 @@ export class ArchiverL1Synchronizer implements Traceable { : await this.getL1BlockHash(syncPointL1BlockNumber); const messagesSyncPoint = { l1BlockNumber: syncPointL1BlockNumber, l1BlockHash: syncPointL1BlockHash }; - await this.stores.messages.setMessageSyncState(messagesSyncPoint, remoteTreeInProgress); + await this.stores.messages.setMessageSyncState(messagesSyncPoint); this.log.verbose(`Updated messages syncpoint to L1 block ${messagesSyncPoint.l1BlockNumber}`, { ...messagesSyncPoint, - remoteTreeInProgress, }); return messagesSyncPoint; } diff --git a/yarn-project/archiver/src/store/data_stores.ts b/yarn-project/archiver/src/store/data_stores.ts index f463e06735ba..0db4c23fca7c 100644 --- a/yarn-project/archiver/src/store/data_stores.ts +++ b/yarn-project/archiver/src/store/data_stores.ts @@ -13,7 +13,7 @@ import { FunctionNamesCache } from './function_names_cache.js'; import { LogStore } from './log_store.js'; import { MessageStore } from './message_store.js'; -export const ARCHIVER_DB_VERSION = 8; +export const ARCHIVER_DB_VERSION = 9; /** * Represents the latest L1 block processed by the archiver for various objects in L2. diff --git a/yarn-project/archiver/src/store/message_store.test.ts b/yarn-project/archiver/src/store/message_store.test.ts index 440a8866a268..b60745673c09 100644 --- a/yarn-project/archiver/src/store/message_store.test.ts +++ b/yarn-project/archiver/src/store/message_store.test.ts @@ -1,5 +1,5 @@ import { CheckpointNumber } from '@aztec/foundation/branded-types'; -import { Buffer16, Buffer32 } from '@aztec/foundation/buffer'; +import { Buffer32 } from '@aztec/foundation/buffer'; import { Fr } from '@aztec/foundation/curves/bn254'; import { toArray } from '@aztec/foundation/iterable'; import { openTmpStore } from '@aztec/kv-store/lmdb-v2'; @@ -8,7 +8,7 @@ import { updateInboxRollingHash } from '@aztec/stdlib/messaging'; import '@aztec/stdlib/testing/jest'; import { InboxBucketBoundaryNotSyncedError, InboxBucketNotSyncedError } from '../errors.js'; -import { type InboxMessage, updateRollingHash } from '../structs/inbox_message.js'; +import type { InboxMessage } from '../structs/inbox_message.js'; import { makeInboxMessage, makeInboxMessages, @@ -78,9 +78,9 @@ describe('MessageStore', () => { it('returns the L1 block set via setMessageSyncState', async () => { const l1BlockHash = Buffer32.random(); const l1BlockNumber = 10n; - await messageStore.setMessageSyncState({ l1BlockNumber, l1BlockHash }, 1n); + await messageStore.setMessageSyncState({ l1BlockNumber, l1BlockHash }); await messageStore.addL1ToL2MessageBuckets([ - makeInboxMessage(Buffer16.ZERO, { l1BlockNumber: 5n, l1BlockHash: Buffer32.random() }), + makeInboxMessage(Fr.ZERO, { l1BlockNumber: 5n, l1BlockHash: Buffer32.random() }), ]); await expect(getSynchPoint(blockStore, messageStore)).resolves.toEqual({ blocksSynchedTo: undefined, @@ -90,8 +90,6 @@ describe('MessageStore', () => { }); describe('L1 to L2 Messages', () => { - const initialCheckpointNumber = CheckpointNumber(13); - const checkMessages = async (msgs: InboxMessage[]) => { expect(await messageStore.getLastMessage()).toEqual(msgs.at(-1)); expect(await toArray(messageStore.iterateL1ToL2Messages())).toEqual(msgs); @@ -99,42 +97,48 @@ describe('MessageStore', () => { }; it('stores first message ever', async () => { - const msg = makeInboxMessage(Buffer16.ZERO, { index: 0n, checkpointNumber: CheckpointNumber(1) }); - await messageStore.addL1ToL2MessageBuckets([msg]); - - await checkMessages([msg]); - }); - - it('stores single message', async () => { - const msg = makeInboxMessage(Buffer16.ZERO, { checkpointNumber: CheckpointNumber(2) }); + const msg = makeInboxMessage(Fr.ZERO, { index: 0n }); await messageStore.addL1ToL2MessageBuckets([msg]); await checkMessages([msg]); }); - it('stores messages across different blocks', async () => { - const msgs = makeInboxMessages(5, { initialCheckpointNumber }); + it('stores and returns messages across different blocks', async () => { + const msgs = makeInboxMessages(5); await messageStore.addL1ToL2MessageBuckets(msgs); await checkMessages(msgs); }); it('stores the same messages again', async () => { - const msgs = makeInboxMessages(5, { initialCheckpointNumber }); + const msgs = makeInboxMessages(5); await messageStore.addL1ToL2MessageBuckets(msgs); await messageStore.addL1ToL2MessageBuckets(msgs.slice(2)); await checkMessages(msgs); }); - it('stores messages with block numbers larger than a byte', async () => { - const msgs = makeInboxMessages(5, { initialCheckpointNumber: CheckpointNumber(1000) }); + it('stores messages added in two chained batches', async () => { + const msgs1 = makeInboxMessages(3); + const msgs2 = makeInboxMessages(3, { + initialInboxHash: msgs1.at(-1)!.inboxRollingHash, + initialIndex: BigInt(msgs1.length), + }); + + await messageStore.addL1ToL2MessageBuckets(msgs1); + await messageStore.addL1ToL2MessageBuckets(msgs2); + + await checkMessages([...msgs1, ...msgs2]); + }); + + it('stores and returns messages with block numbers larger than a byte', async () => { + const msgs = makeInboxMessages(5, { overrideFn: (msg, i) => ({ ...msg, l1BlockNumber: BigInt(1000 + i) }) }); await messageStore.addL1ToL2MessageBuckets(msgs); await checkMessages(msgs); }); - it('stores multiple messages per block', async () => { + it('stores and returns multiple messages per block', async () => { const msgs = makeInboxMessagesWithFullBlocks(4); await messageStore.addL1ToL2MessageBuckets(msgs); @@ -142,7 +146,7 @@ describe('MessageStore', () => { }); it('stores messages in multiple operations', async () => { - const msgs = makeInboxMessages(20, { initialCheckpointNumber }); + const msgs = makeInboxMessages(20); await messageStore.addL1ToL2MessageBuckets(msgs.slice(0, 10)); await messageStore.addL1ToL2MessageBuckets(msgs.slice(10, 20)); @@ -150,7 +154,7 @@ describe('MessageStore', () => { }); it('iterates over messages from start index', async () => { - const msgs = makeInboxMessages(10, { initialCheckpointNumber }); + const msgs = makeInboxMessages(10); await messageStore.addL1ToL2MessageBuckets(msgs); const iterated = await toArray(messageStore.iterateL1ToL2Messages({ start: msgs[3].index })); @@ -158,7 +162,7 @@ describe('MessageStore', () => { }); it('iterates over messages in reverse', async () => { - const msgs = makeInboxMessages(10, { initialCheckpointNumber }); + const msgs = makeInboxMessages(10); await messageStore.addL1ToL2MessageBuckets(msgs); const iterated = await toArray(messageStore.iterateL1ToL2Messages({ reverse: true, end: msgs[3].index })); @@ -170,33 +174,47 @@ describe('MessageStore', () => { await expect(messageStore.addL1ToL2MessageBuckets(msgs)).rejects.toThrow(MessageStoreError); }); - it('throws if rolling hash is not correct', async () => { + it('throws if index is not contiguous with the previous message', async () => { const msgs = makeInboxMessages(5); - msgs[1].rollingHash = Buffer16.random(); + msgs.at(-1)!.index += 100n; await expect(messageStore.addL1ToL2MessageBuckets(msgs)).rejects.toThrow(MessageStoreError); }); - it('throws if rolling hash for first message is not correct', async () => { + it('throws if there is a gap in the indices', async () => { const msgs = makeInboxMessages(4); - msgs[2].rollingHash = Buffer16.random(); - await messageStore.addL1ToL2MessageBuckets(msgs.slice(0, CheckpointNumber(2))); + msgs[2].index++; + await expect(messageStore.addL1ToL2MessageBuckets(msgs)).rejects.toThrow(MessageStoreError); + }); + + it('throws if the first index of a batch does not follow the last stored message', async () => { + const msgs = makeInboxMessages(4); + await messageStore.addL1ToL2MessageBuckets(msgs.slice(0, 2)); + msgs[2].index++; await expect(messageStore.addL1ToL2MessageBuckets(msgs.slice(2, 4))).rejects.toThrow(MessageStoreError); }); - it('throws if index skips ahead', async () => { - const msgs = makeInboxMessages(5, { initialCheckpointNumber }); - msgs.at(-1)!.index += 100n; + it('throws if the consensus rolling hash is not correct', async () => { + const msgs = makeInboxMessages(5); + msgs[1].inboxRollingHash = Fr.random(); await expect(messageStore.addL1ToL2MessageBuckets(msgs)).rejects.toThrow(MessageStoreError); }); - it('throws if index does not follow previous one', async () => { - const msgs = makeInboxMessages(4, { initialCheckpointNumber }); - msgs[2].index++; - await expect(messageStore.addL1ToL2MessageBuckets(msgs)).rejects.toThrow(MessageStoreError); + it('removes messages inserted after a given L1 block', async () => { + // Two messages per L1 block: [100, 100, 101, 101, 102, 102]. + const msgs = makeInboxMessages(6, { + overrideFn: (msg, i) => ({ ...msg, l1BlockNumber: BigInt(100 + Math.floor(i / 2)) }), + }); + await messageStore.addL1ToL2MessageBuckets(msgs); + await checkMessages(msgs); + + await messageStore.rollbackL1ToL2MessagesAfterL1Block(101n); + + // Only the messages inserted at or before L1 block 101 survive. + await checkMessages(msgs.slice(0, 4)); }); it('removes messages starting with the given index', async () => { - const msgs = makeInboxMessagesWithFullBlocks(4, { initialCheckpointNumber: CheckpointNumber(1) }); + const msgs = makeInboxMessagesWithFullBlocks(4); await messageStore.addL1ToL2MessageBuckets(msgs); await messageStore.removeL1ToL2Messages(msgs[13].index); @@ -205,16 +223,16 @@ describe('MessageStore', () => { }); describe('Inbox buckets', () => { - // Builds `count` consecutive valid messages in a single checkpoint, then reassigns their bucket sequence and - // timestamp per the given per-message spec so we can exercise multi-message and rollover buckets. - const makeBucketedMessages = (spec: { seq: bigint; timestamp: bigint }[]): InboxMessage[] => { - const msgs = makeInboxMessages(spec.length, { - initialCheckpointNumber: CheckpointNumber(1), - messagesPerCheckpoint: spec.length, - }); + // Builds `count` consecutive valid messages, then reassigns their bucket sequence and timestamp per the given + // per-message spec so we can exercise multi-message and rollover buckets. + const makeBucketedMessages = ( + spec: { seq: bigint; timestamp: bigint; l1BlockNumber?: bigint }[], + ): InboxMessage[] => { + const msgs = makeInboxMessages(spec.length); msgs.forEach((msg, i) => { msg.bucketSeq = spec[i].seq; msg.bucketTimestamp = spec[i].timestamp; + msg.l1BlockNumber = spec[i].l1BlockNumber ?? msg.l1BlockNumber; }); return msgs; }; @@ -226,7 +244,6 @@ describe('MessageStore', () => { ...previous, leaf, index: previous.index + 1n, - rollingHash: updateRollingHash(previous.rollingHash, leaf), inboxRollingHash: updateInboxRollingHash(previous.inboxRollingHash, leaf), bucketSeq: bucket.seq, bucketTimestamp: bucket.timestamp, @@ -491,6 +508,27 @@ describe('MessageStore', () => { expect((await messageStore.getLatestInboxBucketAtOrBefore(150n))!.seq).toEqual(1n); }); + it('rolls back whole buckets past an L1 block', async () => { + // Bucket 1 fills L1 block 100, bucket 2 fills L1 block 101 and bucket 3 fills L1 block 102. + const msgs = makeBucketedMessages([ + { seq: 1n, timestamp: 100n, l1BlockNumber: 100n }, + { seq: 1n, timestamp: 100n, l1BlockNumber: 100n }, + { seq: 1n, timestamp: 100n, l1BlockNumber: 100n }, + { seq: 2n, timestamp: 200n, l1BlockNumber: 101n }, + { seq: 2n, timestamp: 200n, l1BlockNumber: 101n }, + { seq: 3n, timestamp: 300n, l1BlockNumber: 102n }, + ]); + await messageStore.addL1ToL2MessageBuckets(msgs); + + await messageStore.rollbackL1ToL2MessagesAfterL1Block(100n); + + expect(await toArray(messageStore.iterateL1ToL2Messages())).toEqual(msgs.slice(0, 3)); + expect(await messageStore.getInboxBucket(2n)).toBeUndefined(); + expect(await messageStore.getInboxBucket(3n)).toBeUndefined(); + expect(await messageStore.getInboxBucket(1n)).toMatchObject({ msgCount: 3, totalMsgCount: 3n }); + expect((await messageStore.getLatestInboxBucketAtOrBefore(300n))!.seq).toEqual(1n); + }); + it('clears all buckets when every message is removed', async () => { const msgs = makeBucketedMessages(threeBucketSpec); await messageStore.addL1ToL2MessageBuckets(msgs); diff --git a/yarn-project/archiver/src/store/message_store.ts b/yarn-project/archiver/src/store/message_store.ts index da9351708e4d..0590a060f4f2 100644 --- a/yarn-project/archiver/src/store/message_store.ts +++ b/yarn-project/archiver/src/store/message_store.ts @@ -1,6 +1,5 @@ import type { L1BlockId } from '@aztec/ethereum/l1-types'; -import { CheckpointNumber } from '@aztec/foundation/branded-types'; -import { Buffer16, Buffer32 } from '@aztec/foundation/buffer'; +import { Buffer32 } from '@aztec/foundation/buffer'; import { Fr } from '@aztec/foundation/curves/bn254'; import { toArray } from '@aztec/foundation/iterable'; import { createLogger } from '@aztec/foundation/log'; @@ -13,19 +12,10 @@ import { type CustomRange, mapRange, } from '@aztec/kv-store'; -import { type InboxBucket, InboxLeaf, updateInboxRollingHash } from '@aztec/stdlib/messaging'; +import { type InboxBucket, updateInboxRollingHash } from '@aztec/stdlib/messaging'; -import { - InboxBucketBoundaryNotSyncedError, - InboxBucketNotSyncedError, - L1ToL2MessagesNotReadyError, -} from '../errors.js'; -import { - type InboxMessage, - deserializeInboxMessage, - serializeInboxMessage, - updateRollingHash, -} from '../structs/inbox_message.js'; +import { InboxBucketBoundaryNotSyncedError, InboxBucketNotSyncedError } from '../errors.js'; +import { type InboxMessage, deserializeInboxMessage, serializeInboxMessage } from '../structs/inbox_message.js'; /** * Persisted snapshot of an Inbox rolling-hash bucket. Mirrors the fields the on-chain Inbox tracks per bucket, plus @@ -121,8 +111,6 @@ export class MessageStore { #lastSynchedL1Block: AztecAsyncSingleton; /** Stores total messages stored */ #totalMessageCount: AztecAsyncSingleton; - /** Stores the checkpoint number whose message tree is currently being filled on L1. */ - #inboxTreeInProgress: AztecAsyncSingleton; /** Stores the L1 finalized block as of the last successful message sync. */ #messagesFinalizedL1Block: AztecAsyncSingleton; /** Maps from Inbox bucket sequence number to its serialized snapshot. */ @@ -141,7 +129,6 @@ export class MessageStore { this.#l1ToL2MessageIndices = db.openMap('archiver_l1_to_l2_message_indices'); this.#lastSynchedL1Block = db.openSingleton('archiver_last_l1_block_id'); this.#totalMessageCount = db.openSingleton('archiver_l1_to_l2_message_count'); - this.#inboxTreeInProgress = db.openSingleton('archiver_inbox_tree_in_progress'); this.#messagesFinalizedL1Block = db.openSingleton('archiver_messages_finalized_l1_block'); this.#inboxBuckets = db.openMap('archiver_inbox_buckets'); this.#bucketTimestampToSeq = db.openMultiMap('archiver_inbox_bucket_timestamps'); @@ -218,7 +205,7 @@ export class MessageStore { // Check messages are inserted in increasing order, but allow reinserting messages. if (lastMessage && message.index <= lastMessage.index) { const existing = await this.#l1ToL2Messages.getAsync(this.indexToKey(message.index)); - if (existing && deserializeInboxMessage(existing).rollingHash.equals(message.rollingHash)) { + if (existing && deserializeInboxMessage(existing).inboxRollingHash.equals(message.inboxRollingHash)) { // We reinsert instead of skipping in case the message was re-orged and got added in a different L1 block. this.#log.trace(`Reinserting message with index ${message.index} in the store`); await this.#l1ToL2Messages.set(this.indexToKey(message.index), serializeInboxMessage(message)); @@ -231,20 +218,19 @@ export class MessageStore { ); } - // Check rolling hash is valid. - const previousRollingHash = lastMessage?.rollingHash ?? Buffer16.ZERO; - const expectedRollingHash = updateRollingHash(previousRollingHash, message.leaf); - if (!expectedRollingHash.equals(message.rollingHash)) { + // Check the compact-indexed messages arrive contiguously: the global insertion index of + // each message is exactly one past the previous one. + const expectedIndex = lastMessage === undefined ? 0n : lastMessage.index + 1n; + if (message.index !== expectedIndex) { throw new MessageStoreError( - `Invalid rolling hash for incoming L1 to L2 message ${message.leaf.toString()} ` + - `with index ${message.index} ` + - `(expected ${expectedRollingHash.toString()} from previous hash ${previousRollingHash} but got ${message.rollingHash.toString()})`, + `Invalid index ${message.index} for incoming L1 to L2 message ${message.leaf.toString()} ` + + `(expected ${expectedIndex})`, message, ); } - // Check the full-width consensus rolling hash is valid (AZIP-22 Fast Inbox). Runs alongside the legacy - // 128-bit check above until the streaming inbox flips on and the legacy hash is removed. + // Check the consensus rolling-hash chain is valid: each message's rolling hash must + // continue the chain from the previously inserted message. const previousInboxRollingHash = lastMessage?.inboxRollingHash ?? Fr.ZERO; const expectedInboxRollingHash = updateInboxRollingHash(previousInboxRollingHash, message.leaf); if (!expectedInboxRollingHash.equals(message.inboxRollingHash)) { @@ -257,18 +243,6 @@ export class MessageStore { ); } - // Check the compact-indexed messages arrive contiguously (AZIP-22 Fast Inbox): the global insertion index of - // each message is exactly one past the previous one, independent of the checkpoint it landed in. The flipped - // Inbox emits this compact totalMessagesInserted index, so the legacy per-checkpoint range no longer applies. - const expectedIndex = lastMessage === undefined ? 0n : lastMessage.index + 1n; - if (message.index !== expectedIndex) { - throw new MessageStoreError( - `Invalid index ${message.index} for incoming L1 to L2 message ${message.leaf.toString()} ` + - `(expected ${expectedIndex})`, - message, - ); - } - // Perform the insertions. await this.#l1ToL2Messages.set(this.indexToKey(message.index), serializeInboxMessage(message)); await this.#l1ToL2MessageIndices.set(this.leafToIndexKey(message.leaf), message.index); @@ -356,63 +330,19 @@ export class MessageStore { return msg ? deserializeInboxMessage(msg) : undefined; } - /** Returns the inbox tree-in-progress checkpoint number from L1, or undefined if not yet set. */ - public getInboxTreeInProgress(): Promise { - return this.#inboxTreeInProgress.getAsync(); - } - /** - * Atomically updates the message sync state: the L1 sync point, the inbox tree-in-progress marker, and - * (optionally) the L1 finalized block as of this sync. The finalized block is advanced monotonically. + * Atomically updates the message sync state: the L1 sync point and (optionally) the L1 finalized block as of this + * sync. The finalized block is advanced monotonically. */ - public setMessageSyncState( - l1Block: L1BlockId, - treeInProgress: bigint | undefined, - finalizedL1Block?: L1BlockId, - ): Promise { + public setMessageSyncState(l1Block: L1BlockId, finalizedL1Block?: L1BlockId): Promise { return this.db.transactionAsync(async () => { await this.setSynchedL1Block(l1Block); - if (treeInProgress !== undefined) { - await this.#inboxTreeInProgress.set(treeInProgress); - } else { - await this.#inboxTreeInProgress.delete(); - } if (finalizedL1Block !== undefined) { await this.maybeAdvanceFinalizedL1Block(finalizedL1Block); } }); } - public async getL1ToL2Messages(checkpointNumber: CheckpointNumber): Promise { - const treeInProgress = await this.#inboxTreeInProgress.getAsync(); - if (treeInProgress !== undefined && BigInt(checkpointNumber) >= treeInProgress) { - throw new L1ToL2MessagesNotReadyError(checkpointNumber, treeInProgress); - } - - const messages: Fr[] = []; - - const [startIndex, endIndex] = InboxLeaf.indexRangeForCheckpoint(checkpointNumber); - let lastIndex = startIndex - 1n; - - for await (const msgBuffer of this.#l1ToL2Messages.valuesAsync({ - start: this.indexToKey(startIndex), - end: this.indexToKey(endIndex), - })) { - const msg = deserializeInboxMessage(msgBuffer); - if (msg.checkpointNumber !== checkpointNumber) { - throw new Error( - `L1 to L2 message with index ${msg.index} has invalid checkpoint number ${msg.checkpointNumber}`, - ); - } else if (msg.index !== lastIndex + 1n) { - throw new Error(`Expected L1 to L2 message with index ${lastIndex + 1n} but got ${msg.index}`); - } - lastIndex = msg.index; - messages.push(msg.leaf); - } - - return messages; - } - public async *iterateL1ToL2Messages(range: CustomRange = {}): AsyncIterableIterator { const entriesRange = mapRange(range, this.indexToKey); for await (const msgBuffer of this.#l1ToL2Messages.valuesAsync(entriesRange)) { @@ -649,10 +579,26 @@ export class MessageStore { return Number(timestamp); } - public rollbackL1ToL2MessagesToCheckpoint(targetCheckpointNumber: CheckpointNumber): Promise { - this.#log.debug(`Deleting L1 to L2 messages up to target checkpoint ${targetCheckpointNumber}`); - const startIndex = InboxLeaf.smallestIndexForCheckpoint(CheckpointNumber(targetCheckpointNumber + 1)); - return this.removeL1ToL2Messages(startIndex); + /** + * Removes every L1 to L2 message inserted after the given L1 block, so the message store matches the L1 Inbox state + * as of that block. Used when rolling the archiver back to an earlier checkpoint, whose L1 block is passed here. + * + * A bucket lives entirely within one L1 block, so the cut always falls on a bucket boundary and can be found from + * the bucket snapshots alone, without reading the messages being removed. + */ + public async rollbackL1ToL2MessagesAfterL1Block(l1BlockNumber: bigint): Promise { + this.#log.debug(`Deleting L1 to L2 messages inserted after L1 block ${l1BlockNumber}`); + let removeFromIndex: bigint | undefined; + for await (const snapBuffer of this.#inboxBuckets.valuesAsync({ reverse: true })) { + const snapshot = deserializeBucketSnapshot(snapBuffer); + if (snapshot.l1BlockNumber <= l1BlockNumber) { + break; + } + removeFromIndex = snapshot.firstMessageIndex; + } + if (removeFromIndex !== undefined) { + await this.removeL1ToL2Messages(removeFromIndex); + } } private indexToKey(index: bigint): number { diff --git a/yarn-project/archiver/src/structs/inbox_message.ts b/yarn-project/archiver/src/structs/inbox_message.ts index 137ee14b1044..a27192e6cd32 100644 --- a/yarn-project/archiver/src/structs/inbox_message.ts +++ b/yarn-project/archiver/src/structs/inbox_message.ts @@ -1,17 +1,12 @@ -import { CheckpointNumber } from '@aztec/foundation/branded-types'; -import { Buffer16, Buffer32 } from '@aztec/foundation/buffer'; -import { keccak256 } from '@aztec/foundation/crypto/keccak'; +import { Buffer32 } from '@aztec/foundation/buffer'; import { Fr } from '@aztec/foundation/curves/bn254'; -import { BufferReader, bigintToUInt64BE, numToUInt32BE, serializeToBuffer } from '@aztec/foundation/serialize'; +import { BufferReader, bigintToUInt64BE, serializeToBuffer } from '@aztec/foundation/serialize'; export type InboxMessage = { index: bigint; leaf: Fr; - checkpointNumber: CheckpointNumber; l1BlockNumber: bigint; l1BlockHash: Buffer32; - /** Legacy 128-bit keccak rolling hash of all messages inserted up to and including this one. */ - rollingHash: Buffer16; /** Consensus rolling hash (truncated sha256 chain) of all messages up to and including this one. */ inboxRollingHash: Fr; /** Sequence number of the Inbox bucket this message was absorbed into. */ @@ -20,19 +15,12 @@ export type InboxMessage = { bucketTimestamp: bigint; }; -export function updateRollingHash(currentRollingHash: Buffer16, leaf: Fr): Buffer16 { - const input = Buffer.concat([currentRollingHash.toBuffer(), leaf.toBuffer()]); - return Buffer16.fromBuffer(keccak256(input)); -} - export function serializeInboxMessage(message: InboxMessage): Buffer { return serializeToBuffer([ bigintToUInt64BE(message.index), message.leaf, message.l1BlockHash, bigintToUInt64BE(message.l1BlockNumber), - numToUInt32BE(message.checkpointNumber), - message.rollingHash, message.inboxRollingHash, bigintToUInt64BE(message.bucketSeq), bigintToUInt64BE(message.bucketTimestamp), @@ -45,8 +33,6 @@ export function deserializeInboxMessage(buffer: Buffer): InboxMessage { const leaf = reader.readObject(Fr); const l1BlockHash = reader.readObject(Buffer32); const l1BlockNumber = reader.readUInt64(); - const checkpointNumber = CheckpointNumber(reader.readNumber()); - const rollingHash = reader.readObject(Buffer16); const inboxRollingHash = reader.readObject(Fr); const bucketSeq = reader.readUInt64(); const bucketTimestamp = reader.readUInt64(); @@ -55,8 +41,6 @@ export function deserializeInboxMessage(buffer: Buffer): InboxMessage { leaf, l1BlockHash, l1BlockNumber, - checkpointNumber, - rollingHash, inboxRollingHash, bucketSeq, bucketTimestamp, diff --git a/yarn-project/archiver/src/test/fake_l1_state.ts b/yarn-project/archiver/src/test/fake_l1_state.ts index e7354880213d..101248149525 100644 --- a/yarn-project/archiver/src/test/fake_l1_state.ts +++ b/yarn-project/archiver/src/test/fake_l1_state.ts @@ -1,11 +1,10 @@ import type { BlobClientInterface } from '@aztec/blob-client/client'; import { type Blob, getBlobsPerL1Block, getPrefixedEthBlobCommitments } from '@aztec/blob-lib'; -import { INITIAL_CHECKPOINT_NUMBER } from '@aztec/constants'; import type { CheckpointProposedLog, InboxContract, MessageSentLog, RollupContract } from '@aztec/ethereum/contracts'; import { MULTI_CALL_3_ADDRESS } from '@aztec/ethereum/contracts'; import type { ViemPublicClient } from '@aztec/ethereum/types'; import { type BlockNumber, CheckpointNumber, SlotNumber } from '@aztec/foundation/branded-types'; -import { Buffer16, Buffer32 } from '@aztec/foundation/buffer'; +import { Buffer32 } from '@aztec/foundation/buffer'; import { Secp256k1Signer } from '@aztec/foundation/crypto/secp256k1-signer'; import { Fr } from '@aztec/foundation/curves/bn254'; import { EthAddress } from '@aztec/foundation/eth-address'; @@ -31,8 +30,6 @@ import { toHex, } from 'viem'; -import { updateRollingHash } from '../structs/inbox_message.js'; - /** Configuration for the fake L1 state. */ export type FakeL1StateConfig = { /** Genesis archive root. */ @@ -105,10 +102,8 @@ type CheckpointData = { /** Data stored for a message. */ type MessageData = { l1BlockNumber: bigint; - checkpointNumber: CheckpointNumber; index: bigint; leaf: Fr; - rollingHash: Buffer16; inboxRollingHash: Fr; bucketSeq: bigint; }; @@ -142,7 +137,6 @@ export class FakeL1State { private l1BlockNumber: bigint; private checkpoints: CheckpointData[] = []; private messages: MessageData[] = []; - private messagesRollingHash: Buffer16 = Buffer16.ZERO; // Consensus rolling-hash and bucket-ring state, mirroring the on-chain Inbox. private messagesConsensusRollingHash: Fr = Fr.ZERO; private currentBucketSeq: bigint = 0n; @@ -175,20 +169,17 @@ export class FakeL1State { * Note: For most use cases, use `addCheckpoint` which creates both checkpoint and messages. * Use this method only when you need to add messages without creating a checkpoint (e.g., for reorg tests). */ - addMessages(checkpointNumber: CheckpointNumber, l1BlockNumber: bigint, messageLeaves: Fr[]): void { + addMessages(_checkpointNumber: CheckpointNumber, l1BlockNumber: bigint, messageLeaves: Fr[]): void { const timestamp = this.getTimestampAtL1Block(l1BlockNumber); messageLeaves.forEach(leaf => { - // Compact global insertion index (AZIP-22 Fast Inbox): position in the Inbox's insertion order. + // Compact global insertion index: the position in the Inbox's insertion order. const index = BigInt(this.messages.length); - this.messagesRollingHash = updateRollingHash(this.messagesRollingHash, leaf); const { bucketSeq, inboxRollingHash } = this.absorbIntoBucket(leaf, timestamp); this.messages.push({ l1BlockNumber, - checkpointNumber, index, leaf, - rollingHash: this.messagesRollingHash, inboxRollingHash, bucketSeq, }); @@ -213,20 +204,16 @@ export class FakeL1State { /** Rebuilds all per-message derived state (rolling hashes and bucket assignments) after the message set changes. */ private recomputeDerivedMessageState(): void { - this.messagesRollingHash = Buffer16.ZERO; this.messagesConsensusRollingHash = Fr.ZERO; this.currentBucketSeq = 0n; this.currentBucketTimestamp = 0n; this.currentBucketMsgCount = 0; this.messages.forEach((msg, i) => { - this.messagesRollingHash = updateRollingHash(this.messagesRollingHash, msg.leaf); const { bucketSeq, inboxRollingHash } = this.absorbIntoBucket( msg.leaf, this.getTimestampAtL1Block(msg.l1BlockNumber), ); - // Keep the compact global insertion index contiguous after the message set changes (e.g. reorgs). msg.index = BigInt(i); - msg.rollingHash = this.messagesRollingHash; msg.inboxRollingHash = inboxRollingHash; msg.bucketSeq = bucketSeq; }); @@ -469,11 +456,6 @@ export class FakeL1State { return this.checkpoints.findLast(cpData => cpData.checkpointNumber === checkpointNumber)?.checkpoint; } - /** Gets messages for a checkpoint. */ - getMessages(checkpointNumber: CheckpointNumber): Fr[] { - return this.messages.filter(m => m.checkpointNumber === checkpointNumber).map(m => m.leaf); - } - /** Gets the blobs for a checkpoint. */ getCheckpointBlobs(checkpointNumber: CheckpointNumber): Blob[] { return this.checkpoints.findLast(cpData => cpData.checkpointNumber === checkpointNumber)?.blobs ?? []; @@ -519,33 +501,26 @@ export class FakeL1State { const mockInbox = mock(); mockInbox.getState.mockImplementation((opts: { blockTag?: string; blockNumber?: bigint } = {}) => { - // Filter messages visible at the given block number (or all if not specified) const blockNumber = opts.blockNumber ?? this.l1BlockNumber; const visibleMessages = this.messages.filter(m => m.l1BlockNumber <= blockNumber); + return Promise.resolve({ totalMessagesInserted: BigInt(visibleMessages.length) }); + }); - // treeInProgress must be > any sealed checkpoint. On L1, a checkpoint can only be proposed - // after its messages are sealed, so treeInProgress > checkpointNumber for all published checkpoints. - const maxFromMessages = - visibleMessages.length > 0 ? Math.max(...visibleMessages.map(m => Number(m.checkpointNumber))) + 1 : 0; - const maxFromCheckpoints = - this.checkpoints.length > 0 - ? Math.max( - ...this.checkpoints - .filter(cp => !cp.pruned && cp.l1BlockNumber <= blockNumber) - .map(cp => Number(cp.checkpointNumber)), - 0, - ) + 1 - : 0; - const treeInProgress = Math.max(maxFromMessages, maxFromCheckpoints, INITIAL_CHECKPOINT_NUMBER); - - // Compute rolling hash only for visible messages - const rollingHash = - visibleMessages.length > 0 ? visibleMessages[visibleMessages.length - 1].rollingHash : Buffer16.ZERO; - + // Mirror the on-chain Inbox current bucket: its consensus rolling hash and cumulative total are the live chain + // position the archiver's message sync compares against. + mockInbox.getCurrentBucket.mockImplementation((opts: { blockTag?: string; blockNumber?: bigint } = {}) => { + const blockNumber = opts.blockNumber ?? this.l1BlockNumber; + const visibleMessages = this.messages.filter(m => m.l1BlockNumber <= blockNumber); + const last = visibleMessages.at(-1); + if (last === undefined) { + return Promise.resolve({ rollingHash: Fr.ZERO, totalMsgCount: 0n, timestamp: 0n, msgCount: 0 }); + } + const msgCount = visibleMessages.filter(m => m.bucketSeq === last.bucketSeq).length; return Promise.resolve({ - messagesRollingHash: rollingHash, - totalMessagesInserted: BigInt(visibleMessages.length), - treeInProgress: BigInt(treeInProgress), + rollingHash: last.inboxRollingHash, + totalMsgCount: BigInt(visibleMessages.length), + timestamp: this.getTimestampAtL1Block(last.l1BlockNumber), + msgCount, }); }); @@ -676,10 +651,8 @@ export class FakeL1State { l1TransactionHash: `0x${msg.l1BlockNumber.toString(16)}` as `0x${string}`, l1BlockTimestamp: this.getTimestampAtL1Block(msg.l1BlockNumber), args: { - checkpointNumber: msg.checkpointNumber, index: msg.index, leaf: msg.leaf, - rollingHash: msg.rollingHash, inboxRollingHash: msg.inboxRollingHash, bucketSeq: msg.bucketSeq, }, @@ -702,10 +675,8 @@ export class FakeL1State { l1TransactionHash: `0x${msg.l1BlockNumber.toString(16)}` as `0x${string}`, l1BlockTimestamp: this.getTimestampAtL1Block(msg.l1BlockNumber), args: { - checkpointNumber: msg.checkpointNumber, index: msg.index, leaf: msg.leaf, - rollingHash: msg.rollingHash, inboxRollingHash: msg.inboxRollingHash, bucketSeq: msg.bucketSeq, }, diff --git a/yarn-project/archiver/src/test/mock_archiver.ts b/yarn-project/archiver/src/test/mock_archiver.ts index 029ea7f8a205..357c09d673e0 100644 --- a/yarn-project/archiver/src/test/mock_archiver.ts +++ b/yarn-project/archiver/src/test/mock_archiver.ts @@ -1,4 +1,3 @@ -import type { CheckpointNumber } from '@aztec/foundation/branded-types'; import { Fr } from '@aztec/foundation/curves/bn254'; import type { L2BlockSource } from '@aztec/stdlib/block'; import type { Checkpoint } from '@aztec/stdlib/checkpoint'; @@ -13,18 +12,10 @@ import { MockL2BlockSource } from './mock_l2_block_source.js'; export class MockArchiver extends MockL2BlockSource implements L2BlockSource, L1ToL2MessageSource { private messageSource = new MockL1ToL2MessageSource(0); - public setL1ToL2Messages(checkpointNumber: CheckpointNumber, msgs: Fr[]) { - this.messageSource.setL1ToL2Messages(checkpointNumber, msgs); - } - public setInboxBucket(bucket: InboxBucket, msgs: Fr[] = []) { this.messageSource.setInboxBucket(bucket, msgs); } - getL1ToL2Messages(checkpointNumber: CheckpointNumber): Promise { - return this.messageSource.getL1ToL2Messages(checkpointNumber); - } - getL1ToL2MessageIndex(_l1ToL2Message: Fr): Promise { return this.messageSource.getL1ToL2MessageIndex(_l1ToL2Message); } @@ -63,12 +54,11 @@ export class MockPrefilledArchiver extends MockArchiver { } public setPrefilled(prefilled: { checkpoint: Checkpoint; messages: Fr[] }[]) { - for (const { checkpoint, messages } of prefilled) { + for (const { checkpoint } of prefilled) { this.prefilled[checkpoint.number - 1] = checkpoint; if (checkpoint.blocks.length !== 1) { throw new Error('Prefilled checkpoint must only have 1 block at the moment.'); } - this.setL1ToL2Messages(checkpoint.number, messages); } for (const { checkpoint, messages } of prefilled) { diff --git a/yarn-project/archiver/src/test/mock_l1_to_l2_message_source.ts b/yarn-project/archiver/src/test/mock_l1_to_l2_message_source.ts index 7a8940a12a23..53b652f8d279 100644 --- a/yarn-project/archiver/src/test/mock_l1_to_l2_message_source.ts +++ b/yarn-project/archiver/src/test/mock_l1_to_l2_message_source.ts @@ -7,16 +7,11 @@ import type { InboxBucket, L1ToL2MessageSource } from '@aztec/stdlib/messaging'; * A mocked implementation of L1ToL2MessageSource to be used in tests. */ export class MockL1ToL2MessageSource implements L1ToL2MessageSource { - private messagesPerCheckpoint = new Map(); private buckets = new Map(); private messagesPerBucket = new Map(); constructor(private blockNumber: number) {} - public setL1ToL2Messages(checkpointNumber: CheckpointNumber, msgs: Fr[]) { - this.messagesPerCheckpoint.set(checkpointNumber, msgs); - } - public setInboxBucket(bucket: InboxBucket, msgs: Fr[] = []) { this.buckets.set(bucket.seq, bucket); this.messagesPerBucket.set(bucket.seq, msgs); @@ -26,10 +21,6 @@ export class MockL1ToL2MessageSource implements L1ToL2MessageSource { this.blockNumber = blockNumber; } - getL1ToL2Messages(checkpointNumber: CheckpointNumber): Promise { - return Promise.resolve(this.messagesPerCheckpoint.get(checkpointNumber) ?? []); - } - getL1ToL2MessageIndex(_l1ToL2Message: Fr): Promise { throw new Error('Method not implemented.'); } diff --git a/yarn-project/archiver/src/test/mock_structs.ts b/yarn-project/archiver/src/test/mock_structs.ts index 5cc54cfe9bad..5e6539ce0c59 100644 --- a/yarn-project/archiver/src/test/mock_structs.ts +++ b/yarn-project/archiver/src/test/mock_structs.ts @@ -1,13 +1,9 @@ -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_BLOCK, 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'; +import { Buffer32 } from '@aztec/foundation/buffer'; import { times, timesParallel } from '@aztec/foundation/collection'; -import { randomBigInt, randomInt } from '@aztec/foundation/crypto/random'; +import { randomBigInt } from '@aztec/foundation/crypto/random'; import type { Secp256k1Signer } from '@aztec/foundation/crypto/secp256k1-signer'; import { Fr } from '@aztec/foundation/curves/bn254'; import { EthAddress } from '@aztec/foundation/eth-address'; @@ -22,20 +18,18 @@ import { makeCheckpointAttestationFromCheckpoint } from '@aztec/stdlib/testing'; import { AppendOnlyTreeSnapshot } from '@aztec/stdlib/trees'; import { PartialStateReference, StateReference, TxEffect } from '@aztec/stdlib/tx'; -import { type InboxMessage, updateRollingHash } from '../structs/inbox_message.js'; +import type { InboxMessage } from '../structs/inbox_message.js'; export function makeInboxMessage( - previousRollingHash = Buffer16.ZERO, + previousInboxRollingHash = Fr.ZERO, overrides: Partial = {}, ): InboxMessage { - const { checkpointNumber = CheckpointNumber(randomInt(100) + 1) } = overrides; const { l1BlockNumber = randomBigInt(100n) + 1n } = overrides; const { l1BlockHash = Buffer32.random() } = overrides; const { leaf = Fr.random() } = overrides; - const { rollingHash = updateRollingHash(previousRollingHash, leaf) } = overrides; // Compact global insertion index: defaults to the first slot. const { index = 0n } = overrides; - const { inboxRollingHash = updateInboxRollingHash(Fr.ZERO, leaf) } = overrides; + const { inboxRollingHash = updateInboxRollingHash(previousInboxRollingHash, leaf) } = overrides; // Default each message to its own bucket, keyed monotonically off its global index. const { bucketSeq = index + 1n } = overrides; const { bucketTimestamp = index + 1n } = overrides; @@ -43,75 +37,55 @@ export function makeInboxMessage( return { index, leaf, - checkpointNumber, l1BlockNumber, l1BlockHash, - rollingHash, inboxRollingHash, bucketSeq, bucketTimestamp, }; } +/** + * Builds a contiguous run of `totalCount` inbox messages with compact global indices starting at `initialIndex` + * and a chained consensus rolling hash starting from `initialInboxHash`. + */ export function makeInboxMessages( totalCount: number, opts: { - initialHash?: Buffer16; initialInboxHash?: Fr; initialIndex?: bigint; - initialCheckpointNumber?: CheckpointNumber; - messagesPerCheckpoint?: number; overrideFn?: (msg: InboxMessage, index: number) => InboxMessage; } = {}, ): InboxMessage[] { - const { - initialHash = Buffer16.ZERO, - initialInboxHash = Fr.ZERO, - initialIndex = 0n, - overrideFn = msg => msg, - initialCheckpointNumber = CheckpointNumber(1), - messagesPerCheckpoint = 1, - } = opts; + const { initialInboxHash = Fr.ZERO, initialIndex = 0n, overrideFn = msg => msg } = opts; const messages: InboxMessage[] = []; - let rollingHash = initialHash; let inboxRollingHash = initialInboxHash; for (let i = 0; i < totalCount; i++) { - const checkpointNumber = CheckpointNumber.fromBigInt( - BigInt(initialCheckpointNumber) + BigInt(i) / BigInt(messagesPerCheckpoint), - ); const leaf = Fr.random(); - // Compact global insertion index (AZIP-22 Fast Inbox): contiguous from initialIndex, independent of checkpoint. + inboxRollingHash = updateInboxRollingHash(inboxRollingHash, leaf); const message = overrideFn( - makeInboxMessage(rollingHash, { + makeInboxMessage(Fr.ZERO, { leaf, - checkpointNumber, index: initialIndex + BigInt(i), - inboxRollingHash: updateInboxRollingHash(inboxRollingHash, leaf), + inboxRollingHash, }), i, ); - rollingHash = message.rollingHash; - inboxRollingHash = message.inboxRollingHash; messages.push(message); } return messages; } -/** Creates inbox messages distributed across multiple blocks with proper checkpoint numbering. */ -export function makeInboxMessagesWithFullBlocks( - blockCount: number, - opts: { initialCheckpointNumber?: CheckpointNumber } = {}, -): InboxMessage[] { - const { initialCheckpointNumber = CheckpointNumber(13) } = opts; - return makeInboxMessages(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP * 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. +/** + * Creates `blockCount` full buckets of `MAX_L1_TO_L2_MSGS_PER_BLOCK` inbox messages each, with compact indices and one + * bucket sequence per block. + */ +export function makeInboxMessagesWithFullBlocks(blockCount: number): InboxMessage[] { + return makeInboxMessages(MAX_L1_TO_L2_MSGS_PER_BLOCK * blockCount, { overrideFn: (msg, i) => { - const checkpointNumber = CheckpointNumber( - initialCheckpointNumber + Math.floor(i / NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP), - ); - return { ...msg, checkpointNumber }; + const bucketSeq = BigInt(Math.floor(i / MAX_L1_TO_L2_MSGS_PER_BLOCK)) + 1n; + return { ...msg, bucketSeq, bucketTimestamp: bucketSeq }; }, }); } diff --git a/yarn-project/aztec-node/src/aztec-node/node_public_calls_simulator.test.ts b/yarn-project/aztec-node/src/aztec-node/node_public_calls_simulator.test.ts index 3b42be8a113a..3fc1008c01ee 100644 --- a/yarn-project/aztec-node/src/aztec-node/node_public_calls_simulator.test.ts +++ b/yarn-project/aztec-node/src/aztec-node/node_public_calls_simulator.test.ts @@ -1,4 +1,3 @@ -import { L1ToL2MessagesNotReadyError } from '@aztec/archiver'; import type { EpochCacheInterface } from '@aztec/epoch-cache'; import { type FeeHeader, RollupContract } from '@aztec/ethereum/contracts'; import { @@ -24,9 +23,10 @@ import { } from '@aztec/stdlib/block'; import type { ProposedCheckpointData } from '@aztec/stdlib/checkpoint'; import type { ContractDataSource } from '@aztec/stdlib/contract'; +import { EmptyL1RollupConstants } from '@aztec/stdlib/epoch-helpers'; import { GasFees } from '@aztec/stdlib/gas'; import type { MerkleTreeWriteOperations, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server'; -import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging'; +import type { InboxBucket, L1ToL2MessageSource } from '@aztec/stdlib/messaging'; import { CheckpointHeader } from '@aztec/stdlib/rollup'; import { mockTx } from '@aztec/stdlib/testing'; import { AppendOnlyTreeSnapshot, MerkleTreeId } from '@aztec/stdlib/trees'; @@ -114,6 +114,26 @@ describe('NodePublicCallsSimulator', () => { gasFees: GasFees.empty(), }); + /** + * Mocks the Inbox so the next-block prediction selects a two-message bundle: the fork's message total (0) + * resolves to bucket 0, and bucket 1 is lag-eligible and holds both messages. + */ + const mockInboxSelection = () => { + const makeBucket = (seq: bigint, totalMsgCount: bigint): InboxBucket => ({ + seq, + inboxRollingHash: Fr.ZERO, + totalMsgCount, + timestamp: 0n, + msgCount: Number(totalMsgCount), + lastMessageIndex: totalMsgCount === 0n ? 0n : totalMsgCount - 1n, + }); + const bundle = [new Fr(0x1234), new Fr(0x5678)]; + l1ToL2MessageSource.getInboxBucketByTotalMsgCount.mockResolvedValue(makeBucket(0n, 0n)); + l1ToL2MessageSource.getLatestInboxBucketAtOrBefore.mockResolvedValue(makeBucket(1n, 2n)); + l1ToL2MessageSource.getL1ToL2MessagesBetweenBuckets.mockResolvedValue(bundle); + return bundle; + }; + const lowGasTx = () => mockTx(0x10000, { numberOfNonRevertiblePublicCallRequests: 0, @@ -140,9 +160,18 @@ describe('NodePublicCallsSimulator', () => { (merkleTreeFork as unknown as { [Symbol.asyncDispose]: () => Promise })[Symbol.asyncDispose] = () => Promise.resolve(); worldStateSynchronizer.fork.mockResolvedValue(merkleTreeFork); - l1ToL2MessageSource.getL1ToL2Messages.mockResolvedValue([]); + merkleTreeFork.getTreeInfo.mockResolvedValue({ + treeId: MerkleTreeId.L1_TO_L2_MESSAGE_TREE, + root: Buffer.alloc(32), + size: 0n, + depth: 16, + }); blockSource.getPendingChainValidationStatus.mockResolvedValue({ valid: true }); blockSource.getProposedCheckpointData.mockResolvedValue(undefined); + // No Inbox bucket resolves to the fork's message total by default, so the next-block message prediction + // bails out and tests see the bare tip state unless they opt into it. + l1ToL2MessageSource.getInboxBucketByTotalMsgCount.mockResolvedValue(undefined); + epochCache.getL1Constants.mockReturnValue(EmptyL1RollupConstants); globalVariableBuilder.buildCheckpointGlobalVariables.mockImplementation((_c, _f, slotNumber) => Promise.resolve(checkpointGlobals(slotNumber)), @@ -222,17 +251,31 @@ describe('NodePublicCallsSimulator', () => { expect(rollupContract.getManaTarget).not.toHaveBeenCalled(); }); - it('does not insert L1-to-L2 messages', async () => { + it('appends the message bundle the next block would consume', async () => { const tx = await lowGasTx(); setupMidCheckpoint(); blockSource.getBlockData.mockImplementation((query: BlockQuery) => Promise.resolve('number' in query ? makeBlockData(query.number, SlotNumber(42)) : undefined), ); mockNextL1Slot(SlotNumber(100)); + const bundle = mockInboxSelection(); await simulator.simulate(tx); - expect(l1ToL2MessageSource.getL1ToL2Messages).not.toHaveBeenCalled(); + expect(merkleTreeFork.appendLeaves).toHaveBeenCalledWith(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, bundle); + }); + + it('simulates against the tip when the parent Inbox bucket is not synced', async () => { + const tx = await lowGasTx(); + setupMidCheckpoint(); + blockSource.getBlockData.mockImplementation((query: BlockQuery) => + Promise.resolve('number' in query ? makeBlockData(query.number, SlotNumber(42)) : undefined), + ); + mockNextL1Slot(SlotNumber(100)); + // Default mock: no bucket resolves the fork's message total. + + await expect(simulator.simulate(tx)).resolves.toBeDefined(); + expect(merkleTreeFork.appendLeaves).not.toHaveBeenCalled(); }); @@ -245,8 +288,7 @@ describe('NodePublicCallsSimulator', () => { await expect(simulator.simulate(tx)).rejects.toThrow(); - // Must not treat the next block as opening a new checkpoint and re-insert the ongoing checkpoint's messages. - expect(l1ToL2MessageSource.getL1ToL2Messages).not.toHaveBeenCalled(); + // Must not treat the next block as opening a new checkpoint. expect(merkleTreeFork.appendLeaves).not.toHaveBeenCalled(); expect(globalVariableBuilder.buildCheckpointGlobalVariables).not.toHaveBeenCalled(); }); @@ -281,36 +323,19 @@ describe('NodePublicCallsSimulator', () => { expect(plan?.chainTipsOverride).toEqual({ pending: CheckpointNumber(1), proven: CheckpointNumber(1) }); }); - it('inserts L1-to-L2 messages for the next checkpoint', async () => { - const tx = await lowGasTx(); - const messages = [Fr.fromString('0x1234'), Fr.fromString('0x5678')]; - blockSource.getL2Tips.mockResolvedValue(setupBoundary()); - blockSource.getBlockData.mockImplementation((query: BlockQuery) => - Promise.resolve('number' in query ? makeBlockData(query.number, SlotNumber(5)) : undefined), - ); - mockNextL1Slot(SlotNumber(20)); - l1ToL2MessageSource.getL1ToL2Messages.mockResolvedValue(messages); - - await simulator.simulate(tx); - - // targetCheckpoint = proposedCheckpoint.number + 1 - expect(l1ToL2MessageSource.getL1ToL2Messages).toHaveBeenCalledWith(CheckpointNumber(2)); - const [treeId, appended] = merkleTreeFork.appendLeaves.mock.calls[0]; - expect(treeId).toEqual(MerkleTreeId.L1_TO_L2_MESSAGE_TREE); - expect(appended.slice(0, 2)).toEqual(messages); - }); - - it('tolerates L1ToL2MessagesNotReadyError and simulates without messages', async () => { + it('appends the message bundle the next block would consume', async () => { const tx = await lowGasTx(); blockSource.getL2Tips.mockResolvedValue(setupBoundary()); blockSource.getBlockData.mockImplementation((query: BlockQuery) => Promise.resolve('number' in query ? makeBlockData(query.number, SlotNumber(5)) : undefined), ); mockNextL1Slot(SlotNumber(20)); - l1ToL2MessageSource.getL1ToL2Messages.mockRejectedValue(new L1ToL2MessagesNotReadyError(CheckpointNumber(2), 0n)); + const bundle = mockInboxSelection(); await expect(simulator.simulate(tx)).resolves.toBeDefined(); - expect(merkleTreeFork.appendLeaves).not.toHaveBeenCalled(); + + // Only the first block's worth of messages: a fresh checkpoint starts its per-checkpoint budget at the tip. + expect(merkleTreeFork.appendLeaves).toHaveBeenCalledWith(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, bundle); }); it('targets parentSlot + 1 and carries the parent overrides when pipelining on a proposed checkpoint', async () => { diff --git a/yarn-project/aztec-node/src/aztec-node/node_public_calls_simulator.ts b/yarn-project/aztec-node/src/aztec-node/node_public_calls_simulator.ts index a563ac5432c1..07c9ed0adde9 100644 --- a/yarn-project/aztec-node/src/aztec-node/node_public_calls_simulator.ts +++ b/yarn-project/aztec-node/src/aztec-node/node_public_calls_simulator.ts @@ -1,4 +1,4 @@ -import { L1ToL2MessagesNotReadyError } from '@aztec/archiver'; +import { INBOX_LAG_SECONDS, MAX_L1_TO_L2_MSGS_PER_BLOCK, MAX_L1_TO_L2_MSGS_PER_CHECKPOINT } from '@aztec/constants'; import { PROPOSER_PIPELINING_SLOT_OFFSET } from '@aztec/epoch-cache'; import type { EpochCacheInterface } from '@aztec/epoch-cache'; import { @@ -8,21 +8,21 @@ import { } from '@aztec/ethereum/contracts'; import { BlockNumber, CheckpointNumber, SlotNumber } from '@aztec/foundation/branded-types'; import { compactArray } from '@aztec/foundation/collection'; -import { Fr } from '@aztec/foundation/curves/bn254'; import { EthAddress } from '@aztec/foundation/eth-address'; import { BadRequestError } from '@aztec/foundation/json-rpc'; import { type Logger, createLogger } from '@aztec/foundation/log'; import { DateProvider } from '@aztec/foundation/timer'; -import { isErrorClass } from '@aztec/foundation/types'; +import { type InboxBucketSource, selectInboxBucketForBlock } from '@aztec/sequencer-client'; import { type AvmSimulator, PublicContractsDB, PublicProcessorFactory } from '@aztec/simulator/server'; import { CollectionLimitsConfig, PublicSimulatorConfig } from '@aztec/stdlib/avm'; import { AztecAddress } from '@aztec/stdlib/aztec-address'; import type { L2BlockSource, L2Tips } from '@aztec/stdlib/block'; import { type ProposedCheckpointData, buildCheckpointSimulationOverridesPlan } from '@aztec/stdlib/checkpoint'; import type { ContractDataSource } from '@aztec/stdlib/contract'; -import type { WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server'; -import { type L1ToL2MessageSource, appendL1ToL2MessagesToTree } from '@aztec/stdlib/messaging'; +import type { MerkleTreeWriteOperations, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server'; +import { type L1ToL2MessageSource, appendL1ToL2MessagesToTree, getInboxCutoffTimestamp } from '@aztec/stdlib/messaging'; import type { CoordinationSignatureContext } from '@aztec/stdlib/p2p'; +import { MerkleTreeId } from '@aztec/stdlib/trees'; import { type GlobalVariableBuilder, GlobalVariables, @@ -34,6 +34,9 @@ import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-clien import { applyPublicDataOverrides } from './public_data_overrides.js'; +/** Inbox queries the simulator needs to predict the message bundle the next block would consume. */ +type SimulatorInboxSource = InboxBucketSource & Pick; + /** Config fields the simulator needs — a narrow subset of `AztecNodeConfig`. */ export interface NodePublicCallsSimulatorConfig { /** Maximum total gas limit accepted for an incoming simulation. */ @@ -46,7 +49,8 @@ export interface NodePublicCallsSimulatorConfig { export interface NodePublicCallsSimulatorDeps { blockSource: L2BlockSource; worldStateSynchronizer: WorldStateSynchronizer; - l1ToL2MessageSource: L1ToL2MessageSource; + /** Inbox bucket queries, used to predict the L1-to-L2 messages the next block will consume. */ + l1ToL2MessageSource: SimulatorInboxSource; contractDataSource: ContractDataSource; globalVariableBuilder: GlobalVariableBuilder; /** @@ -78,16 +82,20 @@ export interface NodePublicCallsSimulatorDeps { * - **When the next block continues an in-progress checkpoint** (the latest proposed block is ahead of * the proposed-checkpoint frontier): every block in a checkpoint shares the same * `CheckpointGlobalVariables`, so we copy the latest proposed block's globals verbatim and only - * bump the block number. No L1 calls, no L1-to-L2 message insertion. + * bump the block number. No L1 calls. * - **When the next block opens a new checkpoint** (the latest proposed block coincides with the * proposed-checkpoint frontier): we compute fresh globals for the slot the next block will land in, * applying the same `SimulationOverridesPlan` the sequencer applies so the simulated mana min fee * matches what the sequencer will write into the block header. + * + * Either way it also predicts the L1-to-L2 message bundle the next block would consume and appends it + * to the fork, so a transaction consuming a message that is in the Inbox but not yet in a block + * simulates against the state it will actually run in. */ export class NodePublicCallsSimulator { private readonly blockSource: L2BlockSource; private readonly worldStateSynchronizer: WorldStateSynchronizer; - private readonly l1ToL2MessageSource: L1ToL2MessageSource; + private readonly l1ToL2MessageSource: SimulatorInboxSource; private readonly contractDataSource: ContractDataSource; private readonly globalVariableBuilder: GlobalVariableBuilder; private readonly rollupContract: RollupContract | undefined; @@ -97,6 +105,7 @@ export class NodePublicCallsSimulator { private readonly avmSimulator?: AvmSimulator; private readonly telemetry: TelemetryClient; private readonly log: Logger; + private readonly dateProvider = new DateProvider(); constructor(deps: NodePublicCallsSimulatorDeps) { this.blockSource = deps.blockSource; @@ -157,9 +166,7 @@ export class NodePublicCallsSimulator { // the proposed-checkpoint terminating block; it opens a new checkpoint when they coincide. const atCheckpointBoundary = proposedCheckpointLastBlock === l2Tips.proposed.number; - // `targetCheckpoint` is the checkpoint whose L1-to-L2 messages must be inserted into the fork - // before simulation. Only set when opening a new checkpoint, where the next block is its first block. - const { globalVariables: newGlobalVariables, targetCheckpoint } = atCheckpointBoundary + const { globalVariables: newGlobalVariables } = atCheckpointBoundary ? await this.buildGlobalVariablesForNewCheckpoint(l2Tips, proposedCheckpointData, blockNumber) : { globalVariables: await this.copyGlobalVariablesFromLatestProposedBlock(latestBlockNumber, blockNumber) }; @@ -169,7 +176,7 @@ export class NodePublicCallsSimulator { const publicProcessorFactory = new PublicProcessorFactory( this.contractDataSource, this.avmSimulator, - new DateProvider(), + this.dateProvider, this.telemetry, this.log.getBindings(), ); @@ -184,18 +191,14 @@ export class NodePublicCallsSimulator { // Ensure world-state has caught up with the latest block we loaded from the archiver await this.worldStateSynchronizer.syncImmediate(latestBlockNumber); - const nextCheckpointMessages = await this.getNextCheckpointMessages(targetCheckpoint); - - // Request a new fork of the world state at the latest block number, and apply any overrides and next checkpoint messages to it before simulation + // Request a new fork of the world state at the latest block number, then apply the next block's predicted + // L1-to-L2 message bundle and any caller overrides to it before simulation. await using merkleTreeFork = await this.worldStateSynchronizer.fork(latestBlockNumber); - if (nextCheckpointMessages !== undefined) { - this.log.debug( - `Appending ${nextCheckpointMessages.length} L1-to-L2 messages to the world state tree for the next checkpoint`, - { checkpointNumber: targetCheckpoint }, - ); - await appendL1ToL2MessagesToTree(merkleTreeFork, nextCheckpointMessages); - } + await this.appendPredictedL1ToL2Messages(merkleTreeFork, { + slotNumber: newGlobalVariables.slotNumber, + checkpointStartBlock: atCheckpointBoundary ? undefined : proposedCheckpointLastBlock, + }); await applyPublicDataOverrides(merkleTreeFork, overrides?.publicStorage); @@ -236,47 +239,88 @@ export class NodePublicCallsSimulator { } /** - * Fetches the next checkpoint's L1-to-L2 messages to insert into the fork before simulation. Only set - * when opening a new checkpoint; when continuing an in-progress checkpoint the ongoing checkpoint's - * messages were already applied when its first block synced, so inserting here would double-count them - * — which is why a missing header for the latest proposed block throws rather than falling through to - * this path. A not-ready or failed fetch degrades to simulating without the messages rather than - * failing the request. + * Appends the L1-to-L2 message bundle the next block would consume to the simulation fork, so a transaction + * consuming a message that has reached the Inbox but no block yet simulates against the state it will run in. + * Runs the same bucket selection the sequencer runs (lag eligibility plus the per-block and per-checkpoint caps), + * treating the next block as non-final: the censorship cutoff only widens consumption on a checkpoint's last + * block, and the node cannot know whether the next block is it. + * + * Best-effort. Any failure — Inbox buckets not synced yet, a torn archiver snapshot — leaves the fork at the tip + * state, which is what the transaction sees if the next block consumes nothing. */ - private async getNextCheckpointMessages(targetCheckpoint: CheckpointNumber | undefined): Promise { - if (targetCheckpoint === undefined) { - return undefined; - } + private async appendPredictedL1ToL2Messages( + fork: MerkleTreeWriteOperations, + opts: { + /** Slot the next block lands in; anchors the censorship cutoff. */ + slotNumber: SlotNumber; + /** Last block of the checkpoint the next block extends; undefined when the next block opens a checkpoint. */ + checkpointStartBlock: BlockNumber | undefined; + }, + ): Promise { try { - return await this.l1ToL2MessageSource.getL1ToL2Messages(targetCheckpoint); - } catch (err) { - if (isErrorClass(err, L1ToL2MessagesNotReadyError)) { - this.log.warn( - `L1-to-L2 messages for checkpoint ${targetCheckpoint} are not ready yet (simulating without them)`, - { checkpointNumber: targetCheckpoint }, - ); - } else { - this.log.error( - `Failed to get L1-to-L2 messages for checkpoint ${targetCheckpoint} (simulating without them)`, - err, - { checkpointNumber: targetCheckpoint }, - ); + const parentTotalMsgCount = (await fork.getTreeInfo(MerkleTreeId.L1_TO_L2_MESSAGE_TREE)).size; + const parentBucket = await this.l1ToL2MessageSource.getInboxBucketByTotalMsgCount(parentTotalMsgCount); + if (parentBucket === undefined) { + this.log.debug(`Inbox bucket at message total ${parentTotalMsgCount} not synced; simulating against the tip`, { + parentTotalMsgCount, + }); + return; } - return undefined; + + // Origin of the per-checkpoint cap: the total consumed as of the checkpoint's parent. A block extending an + // in-progress checkpoint reads it off that checkpoint's parent block; a block opening one starts from the tip. + const checkpointStartTotalMsgCount = + opts.checkpointStartBlock === undefined + ? parentTotalMsgCount + : await this.getConsumedMessageTotal(opts.checkpointStartBlock); + if (checkpointStartTotalMsgCount === undefined) { + this.log.debug(`Block ${opts.checkpointStartBlock} has no header on this node; simulating against the tip`); + return; + } + + const selection = await selectInboxBucketForBlock({ + messageSource: this.l1ToL2MessageSource, + now: BigInt(Math.floor(this.dateProvider.now() / 1000)), + lagSeconds: BigInt(INBOX_LAG_SECONDS), + parent: { seq: parentBucket.seq, totalMsgCount: parentBucket.totalMsgCount }, + checkpointStartTotalMsgCount, + perBlockCap: MAX_L1_TO_L2_MSGS_PER_BLOCK, + perCheckpointCap: MAX_L1_TO_L2_MSGS_PER_CHECKPOINT, + isLastBlock: false, + cutoffTimestamp: getInboxCutoffTimestamp(opts.slotNumber, this.epochCache.getL1Constants(), INBOX_LAG_SECONDS), + }); + if (!selection.consume || selection.bundle.length === 0) { + return; + } + + await appendL1ToL2MessagesToTree(fork, selection.bundle); + this.log.debug(`Appended ${selection.bundle.length} predicted L1-to-L2 messages to the simulation fork`, { + bucketSeq: selection.bucket.seq, + messageCount: selection.bundle.length, + }); + } catch (err) { + this.log.verbose(`Could not predict the next block's L1-to-L2 messages, simulating against the tip: ${err}`); + } + } + + /** Cumulative Inbox message total consumed as of `blockNumber`, i.e. its L1-to-L2 message tree leaf count. */ + private async getConsumedMessageTotal(blockNumber: BlockNumber): Promise { + if (blockNumber === BlockNumber.ZERO) { + return 0n; } + const block = await this.blockSource.getBlockData({ number: blockNumber }); + return block === undefined ? undefined : BigInt(block.header.state.l1ToL2MessageTree.nextAvailableLeafIndex); } /** * Continues an in-progress checkpoint: the next block extends the checkpoint the latest proposed * block belongs to. Every block in a checkpoint shares the same `CheckpointGlobalVariables`, so the * next block's globals are the latest proposed block's globals with only the block number bumped — - * including the proposer's real coinbase/feeRecipient. No L1 reads and no L1-to-L2 message insertion - * happen here. + * including the proposer's real coinbase/feeRecipient. No L1 reads happen here. * * A missing header means the archiver reported a proposed tip via `getL2Tips` but no longer has its * data (a torn snapshot). We throw a transient/retryable error rather than treating the next block as - * opening a new checkpoint: the fork at `latestBlockNumber` already contains the ongoing checkpoint's - * L1-to-L2 messages, so inserting the next checkpoint's messages would append them a second time. + * opening a new checkpoint, whose globals would be built for the wrong slot. */ private async copyGlobalVariablesFromLatestProposedBlock( latestBlockNumber: BlockNumber, @@ -298,19 +342,14 @@ export class NodePublicCallsSimulator { * sequencer applies so the simulated mana min fee matches what the sequencer will write into the * block header. Coinbase and fee recipient stay zero (we cannot know the future proposer's payout * addresses), unlike continuing an in-progress checkpoint which inherits the real ones from the - * proposed header. Returns the target checkpoint so the caller inserts that checkpoint's L1-to-L2 - * messages into the fork. + * proposed header. */ private async buildGlobalVariablesForNewCheckpoint( l2Tips: L2Tips, proposedCheckpointData: ProposedCheckpointData | undefined, blockNumber: BlockNumber, - ): Promise<{ globalVariables: GlobalVariables; targetCheckpoint: CheckpointNumber }> { + ): Promise<{ globalVariables: GlobalVariables }> { const checkpointedCheckpointNumber = l2Tips.checkpointed.checkpoint.number; - // The new checkpoint sits on top of the proposed one when pipelining, otherwise on the - // checkpointed tip. The target slot and the overrides plan both derive from the single - // `proposedCheckpointData` read, so they cannot disagree about the proposed parent. - const proposedCheckpointNumber = proposedCheckpointData?.checkpointNumber ?? checkpointedCheckpointNumber; const targetSlot = this.computeTargetSlot(proposedCheckpointData); const plan = await this.buildSimulationOverridesPlan(proposedCheckpointData, checkpointedCheckpointNumber); @@ -324,7 +363,6 @@ export class NodePublicCallsSimulator { return { globalVariables: GlobalVariables.from({ blockNumber, ...checkpointGlobalVariables }), - targetCheckpoint: CheckpointNumber(proposedCheckpointNumber + 1), }; } diff --git a/yarn-project/aztec-node/src/aztec-node/server.test.ts b/yarn-project/aztec-node/src/aztec-node/server.test.ts index a74fd09489fe..c0ea4f2d1746 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.test.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.test.ts @@ -1568,27 +1568,19 @@ describe('aztec node', () => { }); }); - describe('getL1ToL2MessageCheckpoint', () => { - it('returns the checkpoint of the block that consumed the message', async () => { + describe('getL1ToL2MessageIndex', () => { + it('returns the compact leaf index the node assigned to the message', async () => { const msg = Fr.random(); - l1ToL2MessageSource.getL1ToL2MessageIndex.mockResolvedValue(0n); - // The first block (checkpoint 1) consumed message index 0: its L1-to-L2 tree leaf count is 1 > 0. - l2BlockSource.getBlockNumber.mockResolvedValue(BlockNumber(1)); - l2BlockSource.getBlockData.mockResolvedValue({ - checkpointNumber: CheckpointNumber(1), - header: { state: { l1ToL2MessageTree: { nextAvailableLeafIndex: 1 } } }, - } as unknown as BlockData); + l1ToL2MessageSource.getL1ToL2MessageIndex.mockResolvedValue(7n); - const result = await node.getL1ToL2MessageCheckpoint(msg); - expect(result).toEqual(CheckpointNumber(1)); + expect(await node.getL1ToL2MessageIndex(msg)).toEqual(7n); }); it('returns undefined when the message is not found', async () => { const msg = Fr.random(); l1ToL2MessageSource.getL1ToL2MessageIndex.mockResolvedValue(undefined); - const result = await node.getL1ToL2MessageCheckpoint(msg); - expect(result).toBeUndefined(); + expect(await node.getL1ToL2MessageIndex(msg)).toBeUndefined(); }); }); diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index b3a3ee8deb64..b7768e5416f9 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -674,10 +674,6 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb return this.worldStateQueries.getL1ToL2MessageMembershipWitness(referenceBlock, l1ToL2Message); } - public getL1ToL2MessageCheckpoint(l1ToL2Message: Fr): Promise { - return this.worldStateQueries.getL1ToL2MessageCheckpoint(l1ToL2Message); - } - public getL1ToL2MessageIndex(l1ToL2Message: Fr): Promise { return this.worldStateQueries.getL1ToL2MessageIndex(l1ToL2Message); } diff --git a/yarn-project/aztec-node/src/modules/node_world_state_queries.ts b/yarn-project/aztec-node/src/modules/node_world_state_queries.ts index d7ed5e9ae89f..0bbdefb67302 100644 --- a/yarn-project/aztec-node/src/modules/node_world_state_queries.ts +++ b/yarn-project/aztec-node/src/modules/node_world_state_queries.ts @@ -1,10 +1,5 @@ -import { - ARCHIVE_HEIGHT, - INITIAL_L2_BLOCK_NUM, - type L1_TO_L2_MSG_TREE_HEIGHT, - type NOTE_HASH_TREE_HEIGHT, -} from '@aztec/constants'; -import { BlockNumber, type CheckpointNumber, type EpochNumber } from '@aztec/foundation/branded-types'; +import { ARCHIVE_HEIGHT, type L1_TO_L2_MSG_TREE_HEIGHT, type NOTE_HASH_TREE_HEIGHT } from '@aztec/constants'; +import { BlockNumber, type EpochNumber } from '@aztec/foundation/branded-types'; import { chunkBy } from '@aztec/foundation/collection'; import { Fr } from '@aztec/foundation/curves/bn254'; import { type Logger, createLogger } from '@aztec/foundation/log'; @@ -12,7 +7,6 @@ import { sleep } from '@aztec/foundation/sleep'; import { MembershipWitness, type SiblingPath } from '@aztec/foundation/trees'; import type { AztecAddress } from '@aztec/stdlib/aztec-address'; import { - type BlockData, type BlockHash, type BlockParameter, type DataInBlock, @@ -187,39 +181,6 @@ export class NodeWorldStateQueries { return this.l1ToL2MessageSource.getL1ToL2MessageIndex(l1ToL2Message); } - public async getL1ToL2MessageCheckpoint(l1ToL2Message: Fr): Promise { - const messageIndex = await this.l1ToL2MessageSource.getL1ToL2MessageIndex(l1ToL2Message); - if (messageIndex === undefined) { - return undefined; - } - // Post-flip, an L1-to-L2 message at compact leaf index `i` is consumed by the first block whose L1-to-L2 tree leaf - // count exceeds `i` (leaf counts are monotonic in block number); that block's checkpoint is the answer, sourced - // from the stored block records rather than 1024-per-checkpoint index arithmetic (AZIP-22 Fast Inbox). - const block = await this.#findBlockConsumingL1ToL2MessageIndex(messageIndex); - return block?.checkpointNumber; - } - - /** Binary-searches the block records for the first block whose L1-to-L2 tree leaf count exceeds `messageIndex`. */ - async #findBlockConsumingL1ToL2MessageIndex(messageIndex: bigint): Promise { - let lo: number = INITIAL_L2_BLOCK_NUM; - let hi: number = await this.blockSource.getBlockNumber(); - let result: BlockData | undefined; - while (lo <= hi) { - const mid = lo + Math.floor((hi - lo) / 2); - const block = await this.blockSource.getBlockData({ number: BlockNumber(mid) }); - if (block === undefined) { - break; - } - if (BigInt(block.header.state.l1ToL2MessageTree.nextAvailableLeafIndex) > messageIndex) { - result = block; - hi = mid - 1; - } else { - lo = mid + 1; - } - } - return result; - } - /** * Returns all the L2 to L1 messages in an epoch (empty array if the epoch is not found). The public * `AztecNodeService.getL2ToL1Messages` that delegates here is deprecated in favor of diff --git a/yarn-project/aztec.js/src/utils/cross_chain.test.ts b/yarn-project/aztec.js/src/utils/cross_chain.test.ts index 573c6453ce2a..839cc1a47c3d 100644 --- a/yarn-project/aztec.js/src/utils/cross_chain.test.ts +++ b/yarn-project/aztec.js/src/utils/cross_chain.test.ts @@ -1,4 +1,3 @@ -import { CheckpointNumber } from '@aztec/foundation/branded-types'; import { Fr } from '@aztec/foundation/curves/bn254'; import type { BlockData } from '@aztec/stdlib/block'; import type { AztecNode } from '@aztec/stdlib/interfaces/client'; @@ -8,19 +7,20 @@ import { type MockProxy, mock } from 'jest-mock-extended'; import { isL1ToL2MessageReady } from './cross_chain.js'; describe('isL1ToL2MessageReady', () => { - let node: MockProxy>; + let node: MockProxy>; let messageHash: Fr; - const blockAtCheckpoint = (checkpointNumber: number) => - ({ checkpointNumber: CheckpointNumber(checkpointNumber) }) as BlockData; + /** A block whose L1-to-L2 message tree holds `leafCount` leaves, i.e. leaf indices 0..leafCount-1. */ + const blockWithMessageLeaves = (leafCount: number) => + ({ header: { state: { l1ToL2MessageTree: { nextAvailableLeafIndex: leafCount } } } }) as BlockData; beforeEach(() => { node = mock(); messageHash = Fr.random(); }); - it('returns false when the message is not yet in any checkpoint', async () => { - node.getL1ToL2MessageCheckpoint.mockResolvedValue(undefined); + it('returns false when the node has not seen the message yet', async () => { + node.getL1ToL2MessageIndex.mockResolvedValue(undefined); expect(await isL1ToL2MessageReady(node, messageHash)).toBe(false); expect(node.getBlockData).not.toHaveBeenCalled(); @@ -28,24 +28,24 @@ describe('isL1ToL2MessageReady', () => { describe('latest fallback (no chain tip)', () => { beforeEach(() => { - node.getL1ToL2MessageCheckpoint.mockResolvedValue(CheckpointNumber(5)); + node.getL1ToL2MessageIndex.mockResolvedValue(5n); }); - it('checks readiness against the latest block', async () => { - node.getBlockData.mockResolvedValue(blockAtCheckpoint(5)); + it('returns true once the latest block has consumed the message leaf', async () => { + node.getBlockData.mockResolvedValue(blockWithMessageLeaves(6)); expect(await isL1ToL2MessageReady(node, messageHash)).toBe(true); expect(node.getBlockData).toHaveBeenCalledWith('latest'); }); - it('returns true once the latest block reaches the message checkpoint', async () => { - node.getBlockData.mockResolvedValue(blockAtCheckpoint(6)); + it('returns false when the latest block stops exactly at the message leaf', async () => { + node.getBlockData.mockResolvedValue(blockWithMessageLeaves(5)); - expect(await isL1ToL2MessageReady(node, messageHash)).toBe(true); + expect(await isL1ToL2MessageReady(node, messageHash)).toBe(false); }); - it('returns false when the latest block is behind the message checkpoint', async () => { - node.getBlockData.mockResolvedValue(blockAtCheckpoint(4)); + it('returns false when the latest block is behind the message leaf', async () => { + node.getBlockData.mockResolvedValue(blockWithMessageLeaves(4)); expect(await isL1ToL2MessageReady(node, messageHash)).toBe(false); }); @@ -59,13 +59,13 @@ describe('isL1ToL2MessageReady', () => { describe('with an explicit chain tip', () => { beforeEach(() => { - node.getL1ToL2MessageCheckpoint.mockResolvedValue(CheckpointNumber(5)); + node.getL1ToL2MessageIndex.mockResolvedValue(5n); }); it('compares against the requested tip instead of latest', async () => { - // The proven tip lags behind latest: the message is in checkpoint 5 but proven is only at 4. + // The proven tip lags behind latest: latest consumed the message leaf, proven has not. node.getBlockData.mockImplementation(param => - Promise.resolve(param === 'proven' ? blockAtCheckpoint(4) : blockAtCheckpoint(6)), + Promise.resolve(param === 'proven' ? blockWithMessageLeaves(5) : blockWithMessageLeaves(7)), ); expect(await isL1ToL2MessageReady(node, messageHash, 'latest')).toBe(true); @@ -73,9 +73,9 @@ describe('isL1ToL2MessageReady', () => { expect(node.getBlockData).toHaveBeenLastCalledWith('proven'); }); - it('returns true once the requested tip reaches the message checkpoint', async () => { + it('returns true once the requested tip has consumed the message leaf', async () => { node.getBlockData.mockImplementation(param => - Promise.resolve(param === 'proven' ? blockAtCheckpoint(5) : blockAtCheckpoint(7)), + Promise.resolve(param === 'proven' ? blockWithMessageLeaves(6) : blockWithMessageLeaves(8)), ); expect(await isL1ToL2MessageReady(node, messageHash, 'proven')).toBe(true); diff --git a/yarn-project/aztec.js/src/utils/cross_chain.ts b/yarn-project/aztec.js/src/utils/cross_chain.ts index 3010e6eab40a..9a0fec7dd9bd 100644 --- a/yarn-project/aztec.js/src/utils/cross_chain.ts +++ b/yarn-project/aztec.js/src/utils/cross_chain.ts @@ -10,7 +10,7 @@ import type { AztecNode } from '@aztec/stdlib/interfaces/client'; * @param opts - Options */ export function waitForL1ToL2MessageReady( - node: Pick, + node: Pick, l1ToL2MessageHash: Fr, opts: { /** Timeout for the operation in seconds */ timeoutSeconds: number; @@ -39,16 +39,17 @@ export function waitForL1ToL2MessageReady( * @returns True if the message is ready to be consumed, false otherwise */ export async function isL1ToL2MessageReady( - node: Pick, + node: Pick, l1ToL2MessageHash: Fr, chainTip: BlockTag = 'latest', ): Promise { - const messageCheckpointNumber = await node.getL1ToL2MessageCheckpoint(l1ToL2MessageHash); - if (messageCheckpointNumber === undefined) { + const messageIndex = await node.getL1ToL2MessageIndex(l1ToL2MessageHash); + if (messageIndex === undefined) { return false; } - // L1 to L2 messages are included in the first block of a checkpoint + // Blocks consume L1-to-L2 messages in Inbox order into consecutive leaves of the message tree, so the message is + // available at a tip exactly when that tip's tree has grown past the message's leaf index. const block = await node.getBlockData(chainTip); - return block !== undefined && block.checkpointNumber >= messageCheckpointNumber; + return block !== undefined && messageIndex < BigInt(block.header.state.l1ToL2MessageTree.nextAvailableLeafIndex); } diff --git a/yarn-project/end-to-end/src/bench/node_rpc_perf.test.ts b/yarn-project/end-to-end/src/bench/node_rpc_perf.test.ts index 098e033ad634..b484d886b69f 100644 --- a/yarn-project/end-to-end/src/bench/node_rpc_perf.test.ts +++ b/yarn-project/end-to-end/src/bench/node_rpc_perf.test.ts @@ -434,12 +434,10 @@ describe('e2e_node_rpc_perf', () => { }); describe('message APIs', () => { - it('benchmarks getL1ToL2MessageCheckpoint', async () => { + it('benchmarks getL1ToL2MessageIndex', async () => { const l1ToL2Message = Fr.random(); - const { stats } = await benchmark('getL1ToL2MessageCheckpoint', () => - aztecNode.getL1ToL2MessageCheckpoint(l1ToL2Message), - ); - addResult('getL1ToL2MessageCheckpoint', stats); + const { stats } = await benchmark('getL1ToL2MessageIndex', () => aztecNode.getL1ToL2MessageIndex(l1ToL2Message)); + addResult('getL1ToL2MessageIndex', stats); expect(stats.avg).toBeLessThan(2000); }); diff --git a/yarn-project/end-to-end/src/composed/web3signer/e2e_multi_validator_node_key_store.test.ts b/yarn-project/end-to-end/src/composed/web3signer/e2e_multi_validator_node_key_store.test.ts index 9990541e8dda..87cfd0da01a0 100644 --- a/yarn-project/end-to-end/src/composed/web3signer/e2e_multi_validator_node_key_store.test.ts +++ b/yarn-project/end-to-end/src/composed/web3signer/e2e_multi_validator_node_key_store.test.ts @@ -376,7 +376,6 @@ describe('e2e_multi_validator_node', () => { blockHeader: BlockHeader, checkpointNumber: CheckpointNumber, indexWithinCheckpoint: number, - inHash: Fr, archive: Fr, txs: Tx[], proposerAddress: EthAddress | undefined, @@ -397,7 +396,6 @@ describe('e2e_multi_validator_node', () => { blockHeader, checkpointNumber, IndexWithinCheckpoint(indexWithinCheckpoint), - inHash, archive, txs, proposerAddress, diff --git a/yarn-project/end-to-end/src/fixtures/fixtures.ts b/yarn-project/end-to-end/src/fixtures/fixtures.ts index 217bc1ccf645..6bfd955cc976 100644 --- a/yarn-project/end-to-end/src/fixtures/fixtures.ts +++ b/yarn-project/end-to-end/src/fixtures/fixtures.ts @@ -30,8 +30,6 @@ export const PIPELINED_FEE_PADDING = TEST_FEE_PADDING; * * The preset runs the production Sequencer with the always-enforced timetable at real (wall-clock) * timing, yielding exactly 2 blocks per slot. It sets: - * - `inboxLag: 2` so the sequencer sources L1->L2 messages from checkpoint N-1 (already sealed), - * avoiding `L1ToL2MessagesNotReadyError` when building for slot N during slot N-1. * - `minTxsPerBlock: 0` so empty checkpoints land even when a tx arrives late in the build window * (otherwise the chain stalls on alternating slots). * - `aztecSlotDuration: 12` / `ethereumSlotDuration: 4` so the pipelined cycle fits inside the @@ -44,7 +42,6 @@ export const PIPELINED_FEE_PADDING = TEST_FEE_PADDING; * - `walletMinFeePadding: PIPELINED_FEE_PADDING` (30x) to absorb the wider fee evolution window. */ export const PIPELINING_SETUP_OPTS = { - inboxLag: 2, minTxsPerBlock: 0, aztecSlotDuration: 12, ethereumSlotDuration: 4, @@ -75,7 +72,6 @@ export const PIPELINING_SETUP_OPTS = { */ export const AUTOMINE_E2E_OPTS = { useAutomineSequencer: true, - inboxLag: 1, minTxsPerBlock: 0, aztecSlotDuration: 12, ethereumSlotDuration: 4, diff --git a/yarn-project/end-to-end/src/multi-node/block-production/multi_validator_node.parallel.test.ts b/yarn-project/end-to-end/src/multi-node/block-production/multi_validator_node.parallel.test.ts index 88c57c5d9197..4f1022ccc347 100644 --- a/yarn-project/end-to-end/src/multi-node/block-production/multi_validator_node.parallel.test.ts +++ b/yarn-project/end-to-end/src/multi-node/block-production/multi_validator_node.parallel.test.ts @@ -57,7 +57,6 @@ describe('multi-node/block-production/multi_validator_node', () => { anvilSlotsInAnEpoch: 4, blockDurationMs: 2000, minTxsPerBlock: 0, - inboxLag: 2, }); // All 5 validators in a single physical node. diff --git a/yarn-project/end-to-end/src/multi-node/block-production/redistribution.parallel.test.ts b/yarn-project/end-to-end/src/multi-node/block-production/redistribution.parallel.test.ts index d2eeb78036b8..4f3dcfe90048 100644 --- a/yarn-project/end-to-end/src/multi-node/block-production/redistribution.parallel.test.ts +++ b/yarn-project/end-to-end/src/multi-node/block-production/redistribution.parallel.test.ts @@ -56,7 +56,6 @@ describe('multi-node/block-production/redistribution', () => { const test = await MultiNodeTestContext.setup({ numberOfAccounts: 0, initialValidators: validators, - inboxLag: 2, mockGossipSubNetwork: true, startProverNode: true, aztecEpochDuration: 4, diff --git a/yarn-project/end-to-end/src/multi-node/block-production/setup.ts b/yarn-project/end-to-end/src/multi-node/block-production/setup.ts index 595ae29cdea5..802d3f12f2de 100644 --- a/yarn-project/end-to-end/src/multi-node/block-production/setup.ts +++ b/yarn-project/end-to-end/src/multi-node/block-production/setup.ts @@ -171,7 +171,6 @@ export async function setupBlockProductionWithProver(opts: { ...setupOpts, pxeOpts: { syncChainTip }, skipInitialSequencer: true, - inboxLag: 2, }, nodeOpts: (index: number) => ({ dontStartSequencer: true, diff --git a/yarn-project/end-to-end/src/multi-node/governance/add_rollup.test.ts b/yarn-project/end-to-end/src/multi-node/governance/add_rollup.test.ts index 59ff485e4c5d..4f6c25d556c6 100644 --- a/yarn-project/end-to-end/src/multi-node/governance/add_rollup.test.ts +++ b/yarn-project/end-to-end/src/multi-node/governance/add_rollup.test.ts @@ -89,11 +89,8 @@ describe('multi-node/governance/add_rollup', () => { aztecTargetCommitteeSize: NUM_VALIDATORS, governanceProposerRoundSize: 10, // Allow validators to build empty checkpoints so the chain keeps advancing while we wait for - // L1->L2 messages to land in the next checkpoint's inbox tree. + // L1->L2 messages to be consumed from the streaming Inbox. minTxsPerBlock: 0, - // inboxLag: 2 sources L1->L2 messages from an already-sealed checkpoint under pipelining, avoiding - // L1ToL2MessagesNotReadyError. - inboxLag: 2, // Fund the bridging accounts (and the sponsored FPC) at genesis. Skip the hardcoded-account // fast-path so our additionallyFundedAccounts are not clobbered. skipHardcodedAccount: true, @@ -160,7 +157,6 @@ describe('multi-node/governance/add_rollup', () => { aztecTargetCommitteeSize: context.aztecNodeConfig.aztecTargetCommitteeSize, lagInEpochsForValidatorSet: context.aztecNodeConfig.lagInEpochsForValidatorSet, lagInEpochsForRandao: context.aztecNodeConfig.lagInEpochsForRandao, - inboxLag: context.aztecNodeConfig.inboxLag, aztecProofSubmissionEpochs: context.aztecNodeConfig.aztecProofSubmissionEpochs, slashingQuorum: context.aztecNodeConfig.slashingQuorum, slashingRoundSizeInEpochs: context.aztecNodeConfig.slashingRoundSizeInEpochs, diff --git a/yarn-project/end-to-end/src/multi-node/governance/upgrade_governance_proposer.test.ts b/yarn-project/end-to-end/src/multi-node/governance/upgrade_governance_proposer.test.ts index 36a6dd07bf23..d81717bb62b1 100644 --- a/yarn-project/end-to-end/src/multi-node/governance/upgrade_governance_proposer.test.ts +++ b/yarn-project/end-to-end/src/multi-node/governance/upgrade_governance_proposer.test.ts @@ -38,7 +38,6 @@ describe('multi-node/governance/upgrade_governance_proposer', () => { governanceProposerRoundSize: 10, activationThreshold: 10n ** 22n, ejectionThreshold: 5n ** 22n, - inboxLag: 2, minTxsPerBlock: 0, initialValidators: buildMockGossipValidators(NUM_VALIDATORS), }); diff --git a/yarn-project/end-to-end/src/multi-node/slashing/broadcasted_invalid_block_proposal_slash.test.ts b/yarn-project/end-to-end/src/multi-node/slashing/broadcasted_invalid_block_proposal_slash.test.ts index d1876afe573e..2a97f16d648f 100644 --- a/yarn-project/end-to-end/src/multi-node/slashing/broadcasted_invalid_block_proposal_slash.test.ts +++ b/yarn-project/end-to-end/src/multi-node/slashing/broadcasted_invalid_block_proposal_slash.test.ts @@ -49,7 +49,6 @@ describe('multi-node/slashing/broadcasted_invalid_block_proposal_slash', () => { sentinelEnabled: false, // reuse only the fast 8s-slot timing; this test does not use the sentinel blockDurationMs: 2000, aztecTargetCommitteeSize: COMMITTEE_SIZE, - inboxLag: 2, aztecProofSubmissionEpochs: 1024, // effectively do not reorg slashInactivityConsecutiveEpochThreshold: 32, // effectively do not slash for inactivity minTxsPerBlock: 0, // always be building diff --git a/yarn-project/end-to-end/src/multi-node/slashing/broadcasted_invalid_checkpoint_proposal_slash.parallel.test.ts b/yarn-project/end-to-end/src/multi-node/slashing/broadcasted_invalid_checkpoint_proposal_slash.parallel.test.ts index b207200e5a0a..89e9d8da96b4 100644 --- a/yarn-project/end-to-end/src/multi-node/slashing/broadcasted_invalid_checkpoint_proposal_slash.parallel.test.ts +++ b/yarn-project/end-to-end/src/multi-node/slashing/broadcasted_invalid_checkpoint_proposal_slash.parallel.test.ts @@ -240,7 +240,6 @@ describe('multi-node/slashing/broadcasted_invalid_checkpoint_proposal_slash', () aztecTargetCommitteeSize: COMMITTEE_SIZE, aztecProofSubmissionEpochs: 1024, minTxsPerBlock: 0, - inboxLag: 2, slashingQuorum: SLASHING_QUORUM, slashingRoundSizeInEpochs: SLASHING_ROUND_SIZE / SENTINEL_TIMING.aztecEpochDuration, slashAmountSmall: slashingUnit, diff --git a/yarn-project/end-to-end/src/multi-node/slashing/data_withholding_slash.test.ts b/yarn-project/end-to-end/src/multi-node/slashing/data_withholding_slash.test.ts index ea146ea02982..da03696d2fa2 100644 --- a/yarn-project/end-to-end/src/multi-node/slashing/data_withholding_slash.test.ts +++ b/yarn-project/end-to-end/src/multi-node/slashing/data_withholding_slash.test.ts @@ -91,7 +91,6 @@ describe('multi-node/slashing/data_withholding_slash', () => { slashDataWithholdingToleranceSlots: TOLERANCE_SLOTS, slashDataWithholdingPenalty: slashingAmount, minTxsPerBlock: 1, - inboxLag: 2, initialValidators: buildMockGossipValidators(NUM_VALIDATORS), }); }); diff --git a/yarn-project/end-to-end/src/multi-node/slashing/inactivity_setup.ts b/yarn-project/end-to-end/src/multi-node/slashing/inactivity_setup.ts index beec45ff503b..073e28f94955 100644 --- a/yarn-project/end-to-end/src/multi-node/slashing/inactivity_setup.ts +++ b/yarn-project/end-to-end/src/multi-node/slashing/inactivity_setup.ts @@ -48,7 +48,6 @@ export class InactivityTest { private async run(opts: { slashInactivityConsecutiveEpochThreshold: number; inactiveNodeCount: number }) { this.test = await MultiNodeTestContext.setup({ ...SLASHER_ENABLED_MULTI_VALIDATOR_OPTS, - inboxLag: 2, anvilSlotsInAnEpoch: 4, // A fake prover node is started by the context (realProofs:false); give it the multi-epoch // proving delay the inactivity scenario relied on, and keep enough broker history. diff --git a/yarn-project/end-to-end/src/multi-node/slashing/multiple_validators_sentinel.parallel.test.ts b/yarn-project/end-to-end/src/multi-node/slashing/multiple_validators_sentinel.parallel.test.ts index 8bfb047e9d5a..df1d4cc42312 100644 --- a/yarn-project/end-to-end/src/multi-node/slashing/multiple_validators_sentinel.parallel.test.ts +++ b/yarn-project/end-to-end/src/multi-node/slashing/multiple_validators_sentinel.parallel.test.ts @@ -47,7 +47,6 @@ describe('multi-node/slashing/multiple_validators_sentinel', () => { minTxsPerBlock: 0, slashingRoundSizeInEpochs: 2, slashInactivityPenalty: 0n, // Set to 0 to disable - inboxLag: 2, initialValidators: buildMockGossipValidators(NUM_VALIDATORS), }); diff --git a/yarn-project/end-to-end/src/multi-node/slashing/sentinel_status_slash.parallel.test.ts b/yarn-project/end-to-end/src/multi-node/slashing/sentinel_status_slash.parallel.test.ts index 47b64bf660f9..d2f14d5c46a5 100644 --- a/yarn-project/end-to-end/src/multi-node/slashing/sentinel_status_slash.parallel.test.ts +++ b/yarn-project/end-to-end/src/multi-node/slashing/sentinel_status_slash.parallel.test.ts @@ -73,7 +73,6 @@ describe('multi-node/slashing/sentinel_status_slash', () => { blockDurationMs: 2000, aztecProofSubmissionEpochs: 1024, minTxsPerBlock: 0, - inboxLag: 2, // A single proposer-fault slot in an epoch gives missed/total = 1/6 ≈ 0.167; threshold // 0.1 lets that single fault trip inactivity. slashInactivityTargetPercentage: 0.1, diff --git a/yarn-project/end-to-end/src/multi-node/slashing/slash_veto_demo.test.ts b/yarn-project/end-to-end/src/multi-node/slashing/slash_veto_demo.test.ts index 6ab8d440b5cf..fe2c35371c34 100644 --- a/yarn-project/end-to-end/src/multi-node/slashing/slash_veto_demo.test.ts +++ b/yarn-project/end-to-end/src/multi-node/slashing/slash_veto_demo.test.ts @@ -71,7 +71,6 @@ describe('veto slash', () => { blockDurationMs: 2000, aztecProofSubmissionEpochs: 1024, // effectively do not reorg minTxsPerBlock: 0, - inboxLag: 2, aztecTargetCommitteeSize: NUM_VALIDATORS, slashSelfAllowed: true, slashingOffsetInRounds: SLASH_OFFSET_IN_ROUNDS, diff --git a/yarn-project/end-to-end/src/multi-node/slashing/validators_sentinel.parallel.test.ts b/yarn-project/end-to-end/src/multi-node/slashing/validators_sentinel.parallel.test.ts index 09c9a6b1870e..fd40dafb2e8a 100644 --- a/yarn-project/end-to-end/src/multi-node/slashing/validators_sentinel.parallel.test.ts +++ b/yarn-project/end-to-end/src/multi-node/slashing/validators_sentinel.parallel.test.ts @@ -42,7 +42,6 @@ describe('multi-node/slashing/validators_sentinel', () => { minTxsPerBlock: 0, slashingRoundSizeInEpochs: 2, slashInactivityPenalty: 0n, // Set to 0 to disable - inboxLag: 2, initialValidators: buildMockGossipValidators(NUM_VALIDATORS), }); diff --git a/yarn-project/end-to-end/src/p2p/fee_asset_price_oracle_gossip.test.ts b/yarn-project/end-to-end/src/p2p/fee_asset_price_oracle_gossip.test.ts index 21c010085ac3..5e4b1b15fc87 100644 --- a/yarn-project/end-to-end/src/p2p/fee_asset_price_oracle_gossip.test.ts +++ b/yarn-project/end-to-end/src/p2p/fee_asset_price_oracle_gossip.test.ts @@ -46,9 +46,6 @@ describe('e2e_p2p_network', () => { slashingRoundSizeInEpochs: 2, slashingQuorum: 5, listenAddress: '127.0.0.1', - // Pipelining: target-slot is one ahead of build-slot; inboxLag sources L1->L2 - // messages from the previous checkpoint to avoid L1ToL2MessagesNotReadyError. - inboxLag: 2, }, }); diff --git a/yarn-project/end-to-end/src/p2p/gossip_network.test.ts b/yarn-project/end-to-end/src/p2p/gossip_network.test.ts index a1df69dce8cd..46f7409e5515 100644 --- a/yarn-project/end-to-end/src/p2p/gossip_network.test.ts +++ b/yarn-project/end-to-end/src/p2p/gossip_network.test.ts @@ -65,7 +65,6 @@ describe('e2e_p2p_network', () => { slashingRoundSizeInEpochs: 2, slashingQuorum: 5, listenAddress: '127.0.0.1', - inboxLag: 2, }, }); @@ -160,7 +159,6 @@ describe('e2e_p2p_network', () => { // Without this, no blocks are built until txs arrive, and a failed checkpoint during tx // submission causes block pruning that invalidates tx references. minTxsPerBlock: 0, - inboxLag: 2, }, }); diff --git a/yarn-project/end-to-end/src/p2p/late_prover_tx_collection.test.ts b/yarn-project/end-to-end/src/p2p/late_prover_tx_collection.test.ts index 0c46f4693c49..2baea1ce1db1 100644 --- a/yarn-project/end-to-end/src/p2p/late_prover_tx_collection.test.ts +++ b/yarn-project/end-to-end/src/p2p/late_prover_tx_collection.test.ts @@ -52,7 +52,6 @@ describe('e2e_p2p_late_prover_tx_collection', () => { // Only build blocks that actually carry txs, so the chain idles after our block is mined and // the late prover is never auto-triggered to collect for a different block. minTxsPerBlock: 1, - inboxLag: 2, }, }); diff --git a/yarn-project/end-to-end/src/p2p/preferred_gossip_network.test.ts b/yarn-project/end-to-end/src/p2p/preferred_gossip_network.test.ts index b2242e916abb..d1f1c64f21e8 100644 --- a/yarn-project/end-to-end/src/p2p/preferred_gossip_network.test.ts +++ b/yarn-project/end-to-end/src/p2p/preferred_gossip_network.test.ts @@ -136,7 +136,6 @@ describe('e2e_p2p_preferred_network', () => { // Just for testing be aggressive here, don't allow any auth handshake failures p2pMaxFailedAuthAttemptsAllowed: 0, minTxsPerBlock: 0, - inboxLag: 2, }, }); diff --git a/yarn-project/end-to-end/src/p2p/rediscovery.test.ts b/yarn-project/end-to-end/src/p2p/rediscovery.test.ts index 7eed53674dd9..f44c24eff919 100644 --- a/yarn-project/end-to-end/src/p2p/rediscovery.test.ts +++ b/yarn-project/end-to-end/src/p2p/rediscovery.test.ts @@ -33,7 +33,6 @@ describe('e2e_p2p_rediscovery', () => { aztecSlotDuration: 24, blockDurationMs: BLOCK_DURATION_MS, listenAddress: '127.0.0.1', - inboxLag: 2, }, }); await t.setup(); diff --git a/yarn-project/end-to-end/src/p2p/reqresp/utils.ts b/yarn-project/end-to-end/src/p2p/reqresp/utils.ts index e8e69cd7902c..535d884c9c14 100644 --- a/yarn-project/end-to-end/src/p2p/reqresp/utils.ts +++ b/yarn-project/end-to-end/src/p2p/reqresp/utils.ts @@ -43,9 +43,6 @@ export async function createReqrespTest(options: ReqrespOptions = {}): Promise

L2 - // messages from the previous checkpoint to avoid L1ToL2MessagesNotReadyError. - inboxLag: 2, }, }); await t.setup(); diff --git a/yarn-project/end-to-end/src/single-node/block-building/debug_trace.test.ts b/yarn-project/end-to-end/src/single-node/block-building/debug_trace.test.ts index fcb86aab36b1..99edbf848904 100644 --- a/yarn-project/end-to-end/src/single-node/block-building/debug_trace.test.ts +++ b/yarn-project/end-to-end/src/single-node/block-building/debug_trace.test.ts @@ -59,7 +59,6 @@ describe('single-node/block-building/debug_trace', () => { // enough to reach it, where the proposer selection changes and the propose silently reverts. aztecEpochDuration: 32, aztecProofSubmissionEpochs: NO_REORG_SUBMISSION_EPOCHS, - inboxLag: 2, }); ({ aztecNode, logger, aztecNodeAdmin, config } = test.context); sequencer = test.context.sequencer! as TestSequencerClient; diff --git a/yarn-project/end-to-end/src/single-node/bot/bot.test.ts b/yarn-project/end-to-end/src/single-node/bot/bot.test.ts index 75a54e5b92f2..b6b55845d16d 100644 --- a/yarn-project/end-to-end/src/single-node/bot/bot.test.ts +++ b/yarn-project/end-to-end/src/single-node/bot/bot.test.ts @@ -2,7 +2,6 @@ import { getInitialTestAccountsData } from '@aztec/accounts/testing'; import { Fr } from '@aztec/aztec.js/fields'; import type { AztecNode } from '@aztec/aztec.js/node'; import { MinedTxReceipt, type TxReceipt } from '@aztec/aztec.js/tx'; -import type { CheatCodes } from '@aztec/aztec/testing'; import { AmmBot, Bot, @@ -29,15 +28,13 @@ import { NO_REORG_SUBMISSION_EPOCHS } from '../setup.js'; // Tests the transaction bot implementations (transfer bot, AMM bot, cross-chain bot). // Uses setup(0, PIPELINING_SETUP_OPTS + aztecProofSubmissionEpochs:NO_REORG_SUBMISSION_EPOCHS) with one node, production // sequencer (ethereumSlotDuration=4s, aztecSlotDuration=12s, proofSubEpochs=NO_REORG_SUBMISSION_EPOCHS, minTxsPerBlock=0; -// aztecEpochDuration is the setup() default). The bridge-resume, setup-via-bridging, and -// cross-chain-bot subsuites actively drive L1 cross-chain bridging: fee-juice portal deposits, -// advanceInboxInProgress, and L2→L1 messages via CrossChainBot. +// aztecEpochDuration is the setup() default). The bridge-resume and cross-chain-bot subsuites actively +// drive L1 cross-chain bridging: fee-juice portal deposits and L2→L1 messages via CrossChainBot. describe('single-node/bot/bot', () => { let wallet: EmbeddedWallet; let aztecNode: AztecNode; let teardown: () => Promise; let aztecNodeAdmin: AztecNodeAdmin | undefined; - let cheatCodes: CheatCodes; let config: BotConfig; let l1RpcUrls: string[]; @@ -52,7 +49,6 @@ describe('single-node/bot/bot', () => { teardown, aztecNode, aztecNodeAdmin, - cheatCodes, config: { l1RpcUrls }, } = setupResult); wallet = await testSpan('setup:wallet', () => EmbeddedWallet.create(aztecNode, { ephemeral: true })); @@ -270,30 +266,6 @@ describe('single-node/bot/bot', () => { }); }); - // Tests that Bot.create succeeds after the inbox drifts away from the rollup contract. - // Actively drives L1 via advanceInboxInProgress. - describe('setup via bridging funds cross-chain', () => { - beforeAll(() => { - config = { - ...getBotDefaultConfig(), - followChain: 'PROPOSED', - botMode: 'transfer', - senderPrivateKey: new SecretValue(Fr.random()), - l1PrivateKey: getPrivateKey(), - l1RpcUrls, - flushSetupTransactions: true, - }; - }); - - // See 'can consume L1 to L2 message in %s after inbox drifts away from the rollup' - // in end-to-end/src/e2e_cross_chain_messaging/l1_to_l2.test.ts for context on this test. - // Advances inbox 4 slots then creates Bot; verifies it completes setup without error. - it('creates bot after inbox drift', async () => { - await cheatCodes.rollup.advanceInboxInProgress(4); - await Bot.create(config, wallet, aztecNode, aztecNodeAdmin, new BotStore(await openTmpStore('bot'))); - }, 300_000); - }); - // Tests the CrossChainBot: seeds L1→L2 messages and on each tick consumes one while seeding // a replacement. Actively drives L1 portal contracts. describe('cross-chain-bot', () => { diff --git a/yarn-project/end-to-end/src/single-node/cross-chain/message_test_helpers.ts b/yarn-project/end-to-end/src/single-node/cross-chain/message_test_helpers.ts index afacbaf22788..df2cf9ddd491 100644 --- a/yarn-project/end-to-end/src/single-node/cross-chain/message_test_helpers.ts +++ b/yarn-project/end-to-end/src/single-node/cross-chain/message_test_helpers.ts @@ -4,7 +4,7 @@ import type { Logger } from '@aztec/aztec.js/log'; import { isL1ToL2MessageReady } from '@aztec/aztec.js/messaging'; import type { AztecNode } from '@aztec/aztec.js/node'; import type { Wallet } from '@aztec/aztec.js/wallet'; -import type { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types'; +import type { BlockNumber } from '@aztec/foundation/branded-types'; import { retryUntil } from '@aztec/foundation/retry'; import { ExecutionPayload } from '@aztec/stdlib/tx'; @@ -33,7 +33,7 @@ export interface L1ToL2MessageHelpers { secretHash: Fr; }): ReturnType; advanceBlock(): Promise; - waitForMessageFetched(msgHash: Fr): Promise; + waitForMessageIndexed(msgHash: Fr): Promise; waitForMessageReady( msgHash: Fr, scope: L1ToL2MessageScope, @@ -72,22 +72,25 @@ export function createL1ToL2MessageHelpers(deps: L1ToL2MessageHelperDeps): L1ToL return newBlock; }; - // Waits until the message is fetched by the archiver of the node and returns the msg target checkpoint. - // Advances a block on each retry because an L1->L2 message is only indexed once further L2 blocks build. - const waitForMessageFetched = async (msgHash: Fr) => { + // Waits until the node's archiver has ingested the message from the Inbox and returns its message-tree leaf index. + // Advances a block on each retry to keep the chain moving while the archiver catches up with L1. + const waitForMessageIndexed = async (msgHash: Fr) => { log.warn(`Waiting until the message is fetched by the node`); - return await retryUntil( + // Wrapped in an object because `retryUntil` resolves on truthiness: the very first message of a + // chain has leaf index 0, which as a bare bigint would look like "not found" and spin until timeout. + const { messageIndex } = await retryUntil( async () => { - const checkpoint = await aztecNode.getL1ToL2MessageCheckpoint(msgHash); - if (checkpoint !== undefined) { - return checkpoint; + const messageIndex = await aztecNode.getL1ToL2MessageIndex(msgHash); + if (messageIndex !== undefined) { + return { messageIndex }; } await advanceBlock(); return undefined; }, - 'get msg checkpoint', + 'get msg index', 60, ); + return messageIndex; }; // Waits until the message is ready to be consumed on L2 as it's been added to the world state @@ -96,9 +99,9 @@ export function createL1ToL2MessageHelpers(deps: L1ToL2MessageHelperDeps): L1ToL scope: L1ToL2MessageScope, onNotReady?: (blockNumber: BlockNumber) => Promise, ) => { - const msgCheckpoint = await waitForMessageFetched(msgHash); + const msgIndex = await waitForMessageIndexed(msgHash); log.warn( - `Waiting until L2 reaches the first block of msg checkpoint ${msgCheckpoint} (current is ${await aztecNode.getCheckpointNumber()})`, + `Waiting until L2 consumes msg leaf index ${msgIndex} (checkpoint is ${await aztecNode.getCheckpointNumber()})`, ); await retryUntil( async () => { @@ -109,17 +112,17 @@ export function createL1ToL2MessageHelpers(deps: L1ToL2MessageHelperDeps): L1ToL const witness = await aztecNode.getL1ToL2MessageMembershipWitness('latest', msgHash); const isReady = await isL1ToL2MessageReady(aztecNode, msgHash, t.pxeSyncChainTip); log.info( - `Block is ${blockNumber}, checkpoint is ${checkpointNumber}. Message checkpoint is ${msgCheckpoint}. Witness ${!!witness}. Ready ${isReady}.`, + `Block is ${blockNumber}, checkpoint is ${checkpointNumber}. Message leaf index is ${msgIndex}. Witness ${!!witness}. Ready ${isReady}.`, ); if (!isReady) { await (onNotReady ? onNotReady(blockNumber) : advanceBlock()); } return isReady; }, - `wait for rollup to reach msg checkpoint ${msgCheckpoint}`, + `wait for rollup to consume msg leaf index ${msgIndex}`, 240, ); }; - return { sendMessageToL2, advanceBlock, waitForMessageFetched, waitForMessageReady }; + return { sendMessageToL2, advanceBlock, waitForMessageIndexed, waitForMessageReady }; } diff --git a/yarn-project/end-to-end/src/single-node/fees/fee_settings.test.ts b/yarn-project/end-to-end/src/single-node/fees/fee_settings.test.ts index 53b062cff8ce..79bc06be39bf 100644 --- a/yarn-project/end-to-end/src/single-node/fees/fee_settings.test.ts +++ b/yarn-project/end-to-end/src/single-node/fees/fee_settings.test.ts @@ -76,7 +76,6 @@ describe('single-node/fees/fee_settings', () => { // (Test-body txs explicitly call `wallet.setMinFeePadding(...)` so they don't use the wallet default.) const AZTEC_SLOT_DURATION = 12; const t = new FeesTest('fee_juice', 1, { - inboxLag: 2, minTxsPerBlock: 0, aztecSlotDuration: AZTEC_SLOT_DURATION, ethereumSlotDuration: 4, diff --git a/yarn-project/end-to-end/src/single-node/prover/server/full.test.ts b/yarn-project/end-to-end/src/single-node/prover/server/full.test.ts index c257110853de..a75cfa2e5dd7 100644 --- a/yarn-project/end-to-end/src/single-node/prover/server/full.test.ts +++ b/yarn-project/end-to-end/src/single-node/prover/server/full.test.ts @@ -321,7 +321,10 @@ describe('single-node/prover/full', () => { }), ); - // For the commented out circuits, run the tests in orchestrator_single_checkpoint.test.ts to generate the sample inputs. + // Regenerates the private-kernel Prover.toml sample inputs plus the transaction-base rollup + // samples, which all need real client-proved transactions. The block-root, block-merge, + // checkpoint, tx-merge, and root rollup Prover.tomls are regenerated instead by the prover-client + // suite `regenerate_rollup_sample_inputs.test.ts`, so they are intentionally not written here. ( [ 'private-kernel-init', @@ -339,15 +342,6 @@ describe('single-node/prover/full', () => { 'private-kernel-reset-tail-to-public', 'rollup-tx-base-private', 'rollup-tx-base-public', - // 'rollup-tx-merge', - 'rollup-block-root', - 'rollup-block-root-single-tx', - // 'rollup-block-root-no-txs', - // 'rollup-block-merge', - // 'rollup-checkpoint-root', - 'rollup-checkpoint-root-single-block', - 'rollup-checkpoint-merge', - 'rollup-root', ] satisfies CircuitName[] ).forEach(circuitName => { const data = getTestData(circuitName); diff --git a/yarn-project/end-to-end/src/single-node/sequencer/escape_hatch_vote_only.test.ts b/yarn-project/end-to-end/src/single-node/sequencer/escape_hatch_vote_only.test.ts index f85cd95d6f76..0fb0454b1353 100644 --- a/yarn-project/end-to-end/src/single-node/sequencer/escape_hatch_vote_only.test.ts +++ b/yarn-project/end-to-end/src/single-node/sequencer/escape_hatch_vote_only.test.ts @@ -93,7 +93,6 @@ describe('single-node/sequencer/escape_hatch_vote_only', () => { automineL1Setup: true, // Pipelining opts — exercise the §6 B5 fix (tryVoteWhenEscapeHatchOpen signing/submitting for targetSlot). // inboxLag: 2 so the sequencer sources L1->L2 messages from a sealed checkpoint when building for slot+1. - inboxLag: 2, }); ({ diff --git a/yarn-project/end-to-end/src/single-node/single_node_test_context.ts b/yarn-project/end-to-end/src/single-node/single_node_test_context.ts index 2e73c4fee273..00285605b4dc 100644 --- a/yarn-project/end-to-end/src/single-node/single_node_test_context.ts +++ b/yarn-project/end-to-end/src/single-node/single_node_test_context.ts @@ -265,7 +265,6 @@ export class SingleNodeTestContext { slasherEnabled: false, // `inboxLag: 2` is the intended value when running with pipelining (the production config // default of 1 is a separate bug). Set before `...opts` so tests can still override. - inboxLag: 2, ...opts, ...(hardcodedAccountData ? { additionallyFundedAccounts: [hardcodedAccountData], numberOfAccounts: 0 } : {}), }, diff --git a/yarn-project/ethereum/src/config.ts b/yarn-project/ethereum/src/config.ts index 8388a5c74a54..5d8fa61cceb2 100644 --- a/yarn-project/ethereum/src/config.ts +++ b/yarn-project/ethereum/src/config.ts @@ -35,8 +35,6 @@ export type L1ContractsConfig = { lagInEpochsForValidatorSet: number; /** The number of epochs to lag behind the current epoch for randao selection. */ lagInEpochsForRandao: number; - /** The number of checkpoints to lag in the inbox (prevents sequencer DOS attacks). */ - inboxLag: number; /** The number of epochs after an epoch ends that proofs are still accepted. */ aztecProofSubmissionEpochs: number; /** The deposit amount for a validator */ @@ -129,11 +127,6 @@ export const l1ContractsConfigMappings: ConfigMappingsType = description: 'The number of epochs to lag behind the current epoch for randao selection.', ...numberConfigHelper(l1ContractsDefaultEnv.AZTEC_LAG_IN_EPOCHS_FOR_RANDAO), }, - inboxLag: { - env: 'AZTEC_INBOX_LAG', - description: 'The number of checkpoints to lag in the inbox (prevents sequencer DOS attacks).', - ...numberConfigHelper(l1ContractsDefaultEnv.AZTEC_INBOX_LAG), - }, aztecProofSubmissionEpochs: { env: 'AZTEC_PROOF_SUBMISSION_EPOCHS', description: 'The number of epochs after an epoch ends that proofs are still accepted.', diff --git a/yarn-project/ethereum/src/contracts/inbox.ts b/yarn-project/ethereum/src/contracts/inbox.ts index 1d2a732ace8d..1960962a7fc5 100644 --- a/yarn-project/ethereum/src/contracts/inbox.ts +++ b/yarn-project/ethereum/src/contracts/inbox.ts @@ -1,7 +1,6 @@ import { asyncPool } from '@aztec/foundation/async-pool'; import { maxBigint } from '@aztec/foundation/bigint'; -import { CheckpointNumber } from '@aztec/foundation/branded-types'; -import { Buffer16, Buffer32 } from '@aztec/foundation/buffer'; +import { Buffer32 } from '@aztec/foundation/buffer'; import { Fr } from '@aztec/foundation/curves/bn254'; import { EthAddress } from '@aztec/foundation/eth-address'; import { InboxAbi } from '@aztec/l1-artifacts/InboxAbi'; @@ -19,11 +18,9 @@ import { checkBlockTag } from './utils.js'; export type MessageSentArgs = { index: bigint; leaf: Fr; - checkpointNumber: CheckpointNumber; - rollingHash: Buffer16; - /** Consensus rolling hash (truncated sha256 chain) after this message (AZIP-22 Fast Inbox). Not yet consumed by the node. */ + /** Consensus rolling hash (truncated sha256 chain) after this message. */ inboxRollingHash: Fr; - /** Sequence number of the Inbox bucket this message was absorbed into (AZIP-22 Fast Inbox). Not yet consumed by the node. */ + /** Sequence number of the Inbox bucket this message was absorbed into. */ bucketSeq: bigint; }; @@ -68,21 +65,46 @@ export class InboxContract { return this.inbox; } - public async getLag(opts: { blockTag?: BlockTag; blockNumber?: bigint } = {}): Promise { - await checkBlockTag(opts.blockNumber, this.client); - return await this.inbox.read.LAG(opts); - } - public async getState(opts: { blockTag?: BlockTag; blockNumber?: bigint } = {}): Promise { await checkBlockTag(opts.blockNumber, this.client); const state = await this.inbox.read.getState(opts); return { totalMessagesInserted: state.totalMessagesInserted, - messagesRollingHash: Buffer16.fromString(state.rollingHash), - treeInProgress: state.inProgress, }; } + /** Returns the sequence number of the Inbox bucket currently accumulating messages. */ + public async getCurrentBucketSeq(opts: { blockTag?: BlockTag; blockNumber?: bigint } = {}): Promise { + await checkBlockTag(opts.blockNumber, this.client); + return this.inbox.read.getCurrentBucketSeq(opts); + } + + /** Returns the Inbox bucket with the given sequence number. */ + public async getBucket( + seq: bigint, + opts: { blockTag?: BlockTag; blockNumber?: bigint } = {}, + ): Promise { + await checkBlockTag(opts.blockNumber, this.client); + const bucket = await this.inbox.read.getBucket([seq], opts); + return { + rollingHash: Fr.fromString(bucket.rollingHash), + totalMsgCount: bucket.totalMsgCount, + timestamp: bucket.timestamp, + msgCount: bucket.msgCount, + }; + } + + /** + * Returns the Inbox bucket currently accumulating messages: its consensus rolling hash and cumulative message + * total are the Inbox's live chain position, used by the archiver's message sync and L1-reorg detection. + */ + public async getCurrentBucket( + opts: { blockTag?: BlockTag; blockNumber?: bigint } = {}, + ): Promise { + const seq = await this.getCurrentBucketSeq(opts); + return this.getBucket(seq, opts); + } + /** Fetches MessageSent events within the given block range. */ async getMessageSentEvents(fromBlock: bigint, toBlock: bigint): Promise { const logs = (await this.inbox.getEvents.MessageSent({}, { fromBlock, toBlock })).filter( @@ -135,8 +157,6 @@ export class InboxContract { args: { index?: bigint; hash?: `0x${string}`; - checkpointNumber?: bigint; - rollingHash?: `0x${string}`; inboxRollingHash?: `0x${string}`; bucketSeq?: bigint; }; @@ -151,8 +171,6 @@ export class InboxContract { args: { index: log.args.index!, leaf: Fr.fromString(log.args.hash!), - checkpointNumber: CheckpointNumber.fromBigInt(log.args.checkpointNumber!), - rollingHash: Buffer16.fromString(log.args.rollingHash!), inboxRollingHash: Fr.fromString(log.args.inboxRollingHash!), bucketSeq: log.args.bucketSeq!, }, @@ -162,6 +180,16 @@ export class InboxContract { export type InboxContractState = { totalMessagesInserted: bigint; - messagesRollingHash: Buffer16; - treeInProgress: bigint; +}; + +/** A snapshot of an on-chain Inbox rolling-hash bucket. */ +export type InboxContractBucket = { + /** Consensus rolling hash (truncated sha256 chain) after the last message absorbed into this bucket. */ + rollingHash: Fr; + /** Cumulative number of messages inserted into the Inbox up to and including this bucket. */ + totalMsgCount: bigint; + /** L1 block timestamp at which this bucket was opened; its recency key, in seconds. */ + timestamp: bigint; + /** Number of messages absorbed into this bucket. */ + msgCount: number; }; 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/ethereum/src/deploy_aztec_l1_contracts.ts b/yarn-project/ethereum/src/deploy_aztec_l1_contracts.ts index f62c005c0949..df59d422a678 100644 --- a/yarn-project/ethereum/src/deploy_aztec_l1_contracts.ts +++ b/yarn-project/ethereum/src/deploy_aztec_l1_contracts.ts @@ -581,7 +581,6 @@ export function getDeployRollupForUpgradeEnvVars( AZTEC_TARGET_COMMITTEE_SIZE: args.aztecTargetCommitteeSize.toString(), AZTEC_LAG_IN_EPOCHS_FOR_VALIDATOR_SET: args.lagInEpochsForValidatorSet.toString(), AZTEC_LAG_IN_EPOCHS_FOR_RANDAO: args.lagInEpochsForRandao.toString(), - AZTEC_INBOX_LAG: args.inboxLag?.toString(), AZTEC_PROOF_SUBMISSION_EPOCHS: args.aztecProofSubmissionEpochs.toString(), AZTEC_LOCAL_EJECTION_THRESHOLD: args.localEjectionThreshold.toString(), AZTEC_SLASHING_LIFETIME_IN_ROUNDS: args.slashingLifetimeInRounds.toString(), diff --git a/yarn-project/ethereum/src/queries.ts b/yarn-project/ethereum/src/queries.ts index fde4ceb0561d..be3ea3663823 100644 --- a/yarn-project/ethereum/src/queries.ts +++ b/yarn-project/ethereum/src/queries.ts @@ -5,7 +5,6 @@ import { BaseError, type Block } from 'viem'; import { DefaultL1ContractsConfig, type L1ContractsConfig } from './config.js'; import { ReadOnlyGovernanceContract } from './contracts/governance.js'; import { GovernanceProposerContract } from './contracts/governance_proposer.js'; -import { InboxContract } from './contracts/inbox.js'; import { RollupContract } from './contracts/rollup.js'; import type { ViemClient, ViemPublicClient } from './types.js'; @@ -92,8 +91,6 @@ export async function getL1ContractsConfig( const rollup = new RollupContract(publicClient, rollupAddress.toString()); const slasherProposer = await rollup.getSlashingProposer(); const slasher = await rollup.getSlasherContract(); - const rollupAddresses = await rollup.getRollupAddresses(); - const inboxContract = new InboxContract(publicClient, rollupAddresses.inboxAddress.toString()); const [ l1StartBlock, @@ -104,7 +101,6 @@ export async function getL1ContractsConfig( aztecTargetCommitteeSize, lagInEpochsForValidatorSet, lagInEpochsForRandao, - inboxLag, activationThreshold, ejectionThreshold, localEjectionThreshold, @@ -132,7 +128,6 @@ export async function getL1ContractsConfig( rollup.getTargetCommitteeSize(), rollup.getLagInEpochsForValidatorSet(), rollup.getLagInEpochsForRandao(), - inboxContract.getLag(), rollup.getActivationThreshold(), rollup.getEjectionThreshold(), rollup.getLocalEjectionThreshold(), @@ -162,7 +157,6 @@ export async function getL1ContractsConfig( aztecTargetCommitteeSize: Number(aztecTargetCommitteeSize), lagInEpochsForValidatorSet: Number(lagInEpochsForValidatorSet), lagInEpochsForRandao: Number(lagInEpochsForRandao), - inboxLag: Number(inboxLag), governanceProposerQuorum: Number(governanceProposerQuorum), governanceProposerRoundSize: Number(governanceProposerRoundSize), governanceVotingDuration: DefaultL1ContractsConfig.governanceVotingDuration, diff --git a/yarn-project/ethereum/src/test/rollup_cheat_codes.test.ts b/yarn-project/ethereum/src/test/rollup_cheat_codes.test.ts deleted file mode 100644 index 454549dfe680..000000000000 --- a/yarn-project/ethereum/src/test/rollup_cheat_codes.test.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { getPublicClient } from '@aztec/ethereum/client'; -import { Fr } from '@aztec/foundation/curves/bn254'; -import { createLogger } from '@aztec/foundation/log'; -import { DateProvider } from '@aztec/foundation/timer'; -import { InboxAbi } from '@aztec/l1-artifacts/InboxAbi'; - -import { foundry } from 'viem/chains'; - -import { DefaultL1ContractsConfig } from '../config.js'; -import { deployAztecL1Contracts } from '../deploy_aztec_l1_contracts.js'; -import type { ViemClient } from '../types.js'; -import { EthCheatCodes } from './eth_cheat_codes.js'; -import { RollupCheatCodes } from './rollup_cheat_codes.js'; -import type { Anvil } from './start_anvil.js'; -import { startAnvil } from './start_anvil.js'; - -describe('RollupCheatCodes', () => { - let anvil: Anvil; - let rpcUrl: string; - let privateKey: `0x${string}`; - let publicClient: ViemClient; - let cheatCodes: EthCheatCodes; - let rollupCheatCodes: RollupCheatCodes; - - let vkTreeRoot: Fr; - let protocolContractsHash: Fr; - let deployedL1Contracts: Awaited>; - - beforeAll(async () => { - // this is the 6th address that gets funded by the junk mnemonic - privateKey = '0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba'; - vkTreeRoot = Fr.random(); - protocolContractsHash = Fr.random(); - - ({ anvil, rpcUrl } = await startAnvil()); - - publicClient = getPublicClient({ l1RpcUrls: [rpcUrl], l1ChainId: 31337 }); - cheatCodes = new EthCheatCodes([rpcUrl], new DateProvider()); - - deployedL1Contracts = await deployAztecL1Contracts(rpcUrl, privateKey, foundry.id, { - ...DefaultL1ContractsConfig, - vkTreeRoot, - protocolContractsHash, - genesisArchiveRoot: Fr.random(), - realVerifier: false, - }); - - rollupCheatCodes = RollupCheatCodes.create([rpcUrl], deployedL1Contracts.l1ContractAddresses, new DateProvider()); - }); - - afterAll(async () => { - await cheatCodes.setIntervalMining(0); - await anvil?.stop().catch(err => createLogger('cleanup').error(err)); - }); - - describe('advanceInboxInProgress', () => { - it('should advance the inbox inProgress field correctly', async () => { - const inboxAddress = deployedL1Contracts.l1ContractAddresses.inboxAddress.toString(); - - // Read initial state directly from contract - const initialState = await publicClient.readContract({ - address: inboxAddress as `0x${string}`, - abi: InboxAbi, - functionName: 'getState', - }); - - const initialInProgress = initialState.inProgress; - const initialRollingHash = initialState.rollingHash; - const initialTotalMessagesInserted = initialState.totalMessagesInserted; - - // Advance the inbox inProgress by a large amount - const advanceBy = 1000n; - const newInProgress = await rollupCheatCodes.advanceInboxInProgress(advanceBy); - - // Read state after advancement - const finalState = await publicClient.readContract({ - address: inboxAddress as `0x${string}`, - abi: InboxAbi, - functionName: 'getState', - }); - - const finalInProgress = finalState.inProgress; - const finalRollingHash = finalState.rollingHash; - const finalTotalMessagesInserted = finalState.totalMessagesInserted; - - // Check that the advancement worked - expect(newInProgress).toBe(initialInProgress + advanceBy); - expect(finalInProgress).toBe(initialInProgress + advanceBy); - - // Check that all other fields remain unchanged - expect(finalRollingHash).toBe(initialRollingHash); - expect(finalTotalMessagesInserted).toBe(initialTotalMessagesInserted); - }); - }); -}); diff --git a/yarn-project/ethereum/src/test/rollup_cheat_codes.ts b/yarn-project/ethereum/src/test/rollup_cheat_codes.ts index 9aa17c44994d..e2823cd86c2c 100644 --- a/yarn-project/ethereum/src/test/rollup_cheat_codes.ts +++ b/yarn-project/ethereum/src/test/rollup_cheat_codes.ts @@ -295,47 +295,6 @@ export class RollupCheatCodes { ); } - /** - * Overrides the inProgress field of the Inbox contract state - * @param howMuch - How many checkpoints to move it forward - */ - public advanceInboxInProgress(howMuch: number | bigint): Promise { - return this.ethCheatCodes.execWithPausedAnvil(async () => { - // Storage slot 2 contains the InboxState struct - const inboxStateSlot = 2n; - - // Get inbox and its current state values - const inboxAddress = await this.rollup.read.getInbox(); - const currentStateValue = await this.ethCheatCodes.load(EthAddress.fromString(inboxAddress), inboxStateSlot); - - // Extract current values from the packed storage slot - // Storage layout: rollingHash (128 bits) | totalMessagesInserted (64 bits) | inProgress (64 bits) - const currentRollingHash = currentStateValue & ((1n << 128n) - 1n); - const currentTotalMessages = (currentStateValue >> 128n) & ((1n << 64n) - 1n); - const currentInProgress = currentStateValue >> 192n; - const newInProgress = currentInProgress + BigInt(howMuch); - - // Pack new values: rollingHash (low 128 bits) | totalMessages (middle 64 bits) | inProgress (high 64 bits) - const newValue = (BigInt(newInProgress) << 192n) | (currentTotalMessages << 128n) | currentRollingHash; - - await this.ethCheatCodes.store(EthAddress.fromString(inboxAddress), inboxStateSlot, newValue, { - silent: true, - }); - - this.logger.warn(`Inbox inProgress advanced from ${currentInProgress} to ${newInProgress}`, { - inbox: inboxAddress, - oldValue: '0x' + currentStateValue.toString(16), - newValue: '0x' + newValue.toString(16), - rollingHash: currentRollingHash, - totalMessages: currentTotalMessages, - oldInProgress: currentInProgress, - newInProgress, - }); - - return newInProgress; - }); - } - public insertOutbox(epoch: EpochNumber, numCheckpointsInEpoch: number, outHash: bigint) { return this.ethCheatCodes.execWithPausedAnvil(async () => { const outboxAddress = await this.rollup.read.getOutbox(); diff --git a/yarn-project/foundation/src/config/env_var.ts b/yarn-project/foundation/src/config/env_var.ts index 645525138732..cd9fc54f4d01 100644 --- a/yarn-project/foundation/src/config/env_var.ts +++ b/yarn-project/foundation/src/config/env_var.ts @@ -324,7 +324,6 @@ export type EnvVar = | 'AZTEC_TARGET_COMMITTEE_SIZE' | 'AZTEC_LAG_IN_EPOCHS_FOR_VALIDATOR_SET' | 'AZTEC_LAG_IN_EPOCHS_FOR_RANDAO' - | 'AZTEC_INBOX_LAG' | 'AZTEC_PROOF_SUBMISSION_EPOCHS' | 'AZTEC_ACTIVATION_THRESHOLD' | 'AZTEC_EJECTION_THRESHOLD' diff --git a/yarn-project/foundation/src/testing/files/index.ts b/yarn-project/foundation/src/testing/files/index.ts index 564ec76211e6..1ef8387ecf7f 100644 --- a/yarn-project/foundation/src/testing/files/index.ts +++ b/yarn-project/foundation/src/testing/files/index.ts @@ -51,8 +51,10 @@ export function updateInlineTestData(targetFileFromRepoRoot: string, itemName: s /** * Updates the sample Prover.toml files in noir-projects/fnd/noir-protocol-circuits/crates/. - * @remarks Requires AZTEC_GENERATE_TEST_DATA=1 to be set - * To re-gen, run 'AZTEC_GENERATE_TEST_DATA=1 FAKE_PROOFS=1 yarn test:e2e e2e_prover/full.test' + * @remarks Requires AZTEC_GENERATE_TEST_DATA=1 to be set. The block-root, block-merge, checkpoint, + * tx-merge, and root rollup samples are regenerated by the prover-client + * `regenerate_rollup_sample_inputs.test.ts` suite; the private-kernel and transaction-base rollup + * samples by the e2e 'AZTEC_GENERATE_TEST_DATA=1 FAKE_PROOFS=1 yarn test:e2e single-node/prover/server/full.test' dump. */ export function updateProtocolCircuitSampleInputs(circuitName: string, value: string) { const logger = createConsoleLogger('aztec:testing:test_data'); 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 6900453df023..7969da368851 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,6 @@ 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 { computeInHashFromL1ToL2Messages } from '@aztec/stdlib/messaging'; import { InboxParityPrivateInputs } from '@aztec/stdlib/parity'; import { jest } from '@jest/globals'; @@ -43,13 +42,7 @@ describe('Inbox Parity Benchmark Inputs', () => { // Create InboxParity inputs (picks the 256 rung for 256 messages). const vkTreeRoot = getVKTreeRoot(); - const inputs = InboxParityPrivateInputs.fromMessages( - l1ToL2Messages, - Fr.ZERO, - computeInHashFromL1ToL2Messages(l1ToL2Messages), - vkTreeRoot, - Fr.random(), - ); + const inputs = InboxParityPrivateInputs.fromMessages(l1ToL2Messages, Fr.ZERO, vkTreeRoot, Fr.random()); logger.info('Created inbox parity inputs'); // Convert inputs to Noir format (inline the mapping since it's simple) @@ -60,7 +53,6 @@ describe('Inbox Parity Benchmark Inputs', () => { // eslint-disable-next-line camelcase start_rolling_hash: inputs.startRollingHash.toString(), // 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 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 6ba4dc4bda6e..5162214b506c 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,6 @@ 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 { computeInHashFromL1ToL2Messages } from '@aztec/stdlib/messaging'; import { InboxParityPrivateInputs } from '@aztec/stdlib/parity'; import { jest } from '@jest/globals'; @@ -58,13 +57,7 @@ describe('BB.js Debug Wrapper', () => { // Generate inbox parity inputs (same approach as base_parity_inputs.test.ts) const l1ToL2Messages = new Array(INBOX_PARITY_SIZE_MEDIUM).fill(null).map(() => Fr.random()); const vkTreeRoot = getVKTreeRoot(); - const inboxParityInputs = InboxParityPrivateInputs.fromMessages( - l1ToL2Messages, - Fr.ZERO, - computeInHashFromL1ToL2Messages(l1ToL2Messages), - vkTreeRoot, - Fr.random(), - ); + const inboxParityInputs = InboxParityPrivateInputs.fromMessages(l1ToL2Messages, Fr.ZERO, vkTreeRoot, Fr.random()); const noirInputs = { msgs: inboxParityInputs.messages.map(m => m.toString()), @@ -73,7 +66,6 @@ describe('BB.js Debug Wrapper', () => { // eslint-disable-next-line camelcase start_rolling_hash: inboxParityInputs.startRollingHash.toString(), // 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 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 869cf1bf28df..7ac53a3d06db 100644 --- a/yarn-project/noir-protocol-circuits-types/src/conversion/server.ts +++ b/yarn-project/noir-protocol-circuits-types/src/conversion/server.ts @@ -475,7 +475,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), end_sponge: mapL1ToL2MessageSpongeToNoir(parityPublicInputs.endSponge), @@ -513,7 +512,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.end_sponge), @@ -728,7 +726,6 @@ export function mapInboxParityPrivateInputsToNoir(inputs: InboxParityPrivateInpu msgs: mapFieldArrayToNoir(inputs.messages), num_msgs: mapNumberToNoir(inputs.numMessages), start_rolling_hash: mapFieldToNoir(inputs.startRollingHash), - in_hash: mapFieldToNoir(inputs.inHash), vk_tree_root: mapFieldToNoir(inputs.vkTreeRoot), prover_id: mapFieldToNoir(inputs.proverId), }; diff --git a/yarn-project/p2p/src/client/factory.ts b/yarn-project/p2p/src/client/factory.ts index 1175be199df8..2172e68847db 100644 --- a/yarn-project/p2p/src/client/factory.ts +++ b/yarn-project/p2p/src/client/factory.ts @@ -81,7 +81,10 @@ export async function createP2PClient( const store = deps.store ?? (await createStore(P2P_STORE_NAME, 4, config, bindings)); const archive = await createStore(P2P_ARCHIVE_STORE_NAME, 1, config, bindings); const peerStore = await createStore(P2P_PEER_STORE_NAME, 1, config, bindings); - const attestationStore = await createStore(P2P_ATTESTATION_STORE_NAME, 2, config, bindings); + // Attestation store version 3: persisted proposal/attestation buffers embed the checkpoint header (which lost + // `inHash`) and the block-proposal wire format (which dropped its zeroed `inHash`), so pre-Fast-Inbox bytes no + // longer decode. Bumped to wipe stale pools; same no-migration policy as the archiver store. + const attestationStore = await createStore(P2P_ATTESTATION_STORE_NAME, 3, config, bindings); const l1Constants = await archiver.getL1Constants(); const rollupAddress = inputConfig.rollupAddress.toString().toLowerCase().replace(/^0x/, ''); diff --git a/yarn-project/p2p/src/services/libp2p/libp2p_service.test.ts b/yarn-project/p2p/src/services/libp2p/libp2p_service.test.ts index 3c163f0fe551..30ea05fe0d31 100644 --- a/yarn-project/p2p/src/services/libp2p/libp2p_service.test.ts +++ b/yarn-project/p2p/src/services/libp2p/libp2p_service.test.ts @@ -20,7 +20,7 @@ import { makeCheckpointProposal, mockTx, } from '@aztec/stdlib/testing'; -import { TxArray, TxHashArray } from '@aztec/stdlib/tx'; +import { TxArray, TxHash, TxHashArray } from '@aztec/stdlib/tx'; import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client'; import { ServerWorldStateSynchronizer } from '@aztec/world-state'; @@ -809,8 +809,8 @@ describe('LibP2PService', () => { }); // Regression for A-1013: payloads sharing (slot, position, archive) but differing on another - // signed field (e.g. inHash) used to dedup by archive only and silently drop the second one. - // The pool now dedups by signed-payload hash, so the equivocation surfaces. + // signed field (e.g. the transaction set) used to dedup by archive only and silently drop the + // second one. The pool now dedups by signed-payload hash, so the equivocation surfaces. it('same archive but different signed payload triggers slash callback', async () => { const blockHeader = makeBlockHeader(1, { slotNumber: targetSlot }); const indexWithinCheckpoint = IndexWithinCheckpoint(0); @@ -820,7 +820,7 @@ describe('LibP2PService', () => { signer, blockHeader, indexWithinCheckpoint, - inHash: Fr.fromString('0x1'), + txHashes: [TxHash.fromField(new Fr(1))], archiveRoot: sharedArchive, }); await service.processBlockFromPeer(proposal1.toBuffer(), 'msg-1', mockPeerId); @@ -830,7 +830,7 @@ describe('LibP2PService', () => { signer, blockHeader, indexWithinCheckpoint, - inHash: Fr.fromString('0x2'), + txHashes: [TxHash.fromField(new Fr(2))], archiveRoot: sharedArchive, }); expect(proposal2.archive.toString()).toBe(proposal1.archive.toString()); 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 69f260fc8ed7..b7992afaff75 100644 --- a/yarn-project/prover-client/src/light/lightweight_checkpoint_builder.ts +++ b/yarn-project/prover-client/src/light/lightweight_checkpoint_builder.ts @@ -242,7 +242,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)); @@ -277,9 +277,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; @@ -297,7 +294,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 ba29f3a0aa46..3ad46745c279 100644 --- a/yarn-project/prover-client/src/orchestrator/checkpoint-proving-state.ts +++ b/yarn-project/prover-client/src/orchestrator/checkpoint-proving-state.ts @@ -159,15 +159,12 @@ export class CheckpointProvingState { /** * Builds the checkpoint's single InboxParity input. The circuit is sized to the smallest ladder rung that fits the - * message count and the rolling hash starts from the previous checkpoint's end. `in_hash` (the L1 frontier root) is - * supplied as an unconstrained pass-through hint. + * message count and the rolling hash starts from the previous checkpoint's end. */ public getInboxParityInputs(): InboxParityPrivateInputs { return InboxParityPrivateInputs.fromMessages( this.l1ToL2Messages, this.startInboxRollingHash, - // 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 3beeefd6856b..564f5a8a3d70 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,6 @@ 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 { computeInHashFromL1ToL2Messages } from '@aztec/stdlib/messaging'; import { INBOX_PARITY_SIZES, InboxParityPrivateInputs, type InboxParitySize } from '@aztec/stdlib/parity'; import { TestContext } from '../mocks/test_context.js'; @@ -48,11 +47,8 @@ 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, inHash, getVKTreeRoot(), proverId); + const inputs = InboxParityPrivateInputs.fromMessages(messages, Fr.ZERO, getVKTreeRoot(), proverId); expect(inputs.size).toBe(size); const output = await context.prover.getInboxParityProof(inputs); diff --git a/yarn-project/prover-client/src/test/regenerate_rollup_sample_inputs.test.ts b/yarn-project/prover-client/src/test/regenerate_rollup_sample_inputs.test.ts index b56bc3626d94..79059cc680af 100644 --- a/yarn-project/prover-client/src/test/regenerate_rollup_sample_inputs.test.ts +++ b/yarn-project/prover-client/src/test/regenerate_rollup_sample_inputs.test.ts @@ -1,6 +1,6 @@ import { MAX_L1_TO_L2_MSGS_PER_BLOCK } from '@aztec/constants'; import { EpochNumber } from '@aztec/foundation/branded-types'; -import { timesAsync } from '@aztec/foundation/collection'; +import { times, timesAsync } from '@aztec/foundation/collection'; import { Fr } from '@aztec/foundation/curves/bn254'; import { EthAddress } from '@aztec/foundation/eth-address'; import { type Logger, createLogger } from '@aztec/foundation/log'; @@ -16,11 +16,16 @@ import { ChonkCache } from '../orchestrator/chonk-cache.js'; import { type CheckpointTopTreeData, TopTreeOrchestrator } from '../orchestrator/top-tree-orchestrator.js'; // Regenerates the committed `crates/rollup-*/Prover.toml` sample inputs that CI runs `nargo execute` -// against. The rollup circuits push their serialized inputs via `pushTestData` whenever they run -// through the prover, so driving representative epochs through the (simulated) orchestrator and then -// dumping `getTestData(circuitName)` produces fresh, ABI-current fixtures. Run with: +// against, for every rollup circuit at or above the transaction merge: the block-root variants, the +// block-merge, both checkpoint roots, the checkpoint-merge, the tx-merge, and the root rollup. The +// rollup circuits push their serialized inputs via `pushTestData` whenever they run through the +// prover, so driving representative epochs through the (simulated) orchestrator and then dumping +// `getTestData(circuitName)` produces fresh, ABI-current fixtures. Run with: // AZTEC_GENERATE_TEST_DATA=1 yarn workspace @aztec/prover-client test regenerate_rollup_sample_inputs -// Without that flag the whole suite is skipped, so it is a no-op (no prover setup) in normal CI. +// Without that flag the whole suite is skipped, so it is a no-op (no prover setup) in normal CI. The +// `rollup-tx-base-private`/`rollup-tx-base-public` fixtures and the private-kernel fixtures depend on +// real client-proved transactions the simulated orchestrator cannot produce, so those are regenerated +// separately by the e2e `single-node/prover/server/full.test` dump instead. // // The scenarios are chosen to exercise each block-root variant the orchestrator selects (see // BlockProvingState#getBlockRootRollupTypeAndInputs), which is picked by transaction count alone: @@ -28,7 +33,11 @@ import { type CheckpointTopTreeData, TopTreeOrchestrator } from '../orchestrator // checkpoint epoch a checkpoint-merge; a merge node only exists above the tree root, so both merges // need three leaves — two would pair directly at the root. Single-block checkpoints feed the // checkpoint-root-single-block circuit and multi-block checkpoints the (two-input) checkpoint-root -// circuit. Every scenario also produces the root rollup. +// circuit. A block with three txs merges its transaction base proofs through the tx-merge circuit +// before the (two-input) block root, whereas one- or two-tx blocks feed the block root directly, so a +// dedicated three-tx scenario is what regenerates the tx-merge sample. The samples for the variants +// that thread a start sponge from a previous block are taken from mid-checkpoint blocks, so those +// scenarios need a per-block message distribution. Every scenario also produces the root rollup. const describeOrSkip = isGenerateTestDataEnabled() ? describe : describe.skip; describeOrSkip('prover/regenerate-rollup-sample-inputs', () => { @@ -38,8 +47,13 @@ describeOrSkip('prover/regenerate-rollup-sample-inputs', () => { interface Scenario { numCheckpoints: number; numBlocksPerCheckpoint: number; - numTxsPerBlock: number; + numTxsPerBlock: number | number[]; numL1ToL2Messages: number; + /** + * Per-block message distribution. When set it overrides `numL1ToL2Messages`, letting a mid-checkpoint block + * carry its own bundle. + */ + l1ToL2MessagesPerBlock?: Fr[][]; /** Circuits whose sample inputs this scenario is responsible for regenerating. */ dump: CircuitName[]; } @@ -49,19 +63,12 @@ describeOrSkip('prover/regenerate-rollup-sample-inputs', () => { const withMessages = MAX_L1_TO_L2_MSGS_PER_BLOCK; const scenarios: Scenario[] = [ - { - numCheckpoints: 1, - numBlocksPerCheckpoint: 1, - numTxsPerBlock: 0, - numL1ToL2Messages: withMessages, - dump: ['rollup-block-root-no-txs'], - }, { numCheckpoints: 1, numBlocksPerCheckpoint: 1, numTxsPerBlock: 2, numL1ToL2Messages: withMessages, - dump: ['rollup-block-root', 'rollup-checkpoint-root-single-block', 'rollup-root'], + dump: ['rollup-checkpoint-root-single-block', 'rollup-root'], }, { numCheckpoints: 1, @@ -70,6 +77,35 @@ describeOrSkip('prover/regenerate-rollup-sample-inputs', () => { numL1ToL2Messages: withMessages, dump: ['rollup-block-root-single-tx', 'rollup-block-merge', 'rollup-checkpoint-root'], }, + // Messages split across both blocks so the block-root sample is taken from a mid-checkpoint block with a + // non-empty bundle, exercising the per-block sponge continuity asserts in the circuit. + { + numCheckpoints: 1, + numBlocksPerCheckpoint: 2, + numTxsPerBlock: 2, + numL1ToL2Messages: 0, // Overridden by l1ToL2MessagesPerBlock. + l1ToL2MessagesPerBlock: [times(2, i => new Fr(0xb00 + i)), times(3, i => new Fr(0xc00 + i))], + dump: ['rollup-block-root'], + }, + // Three txs in a block force a tx-merge to pair the base proofs down to the two the block root + // takes; one- or two-tx blocks feed the block root directly and never exercise tx-merge. + { + numCheckpoints: 1, + numBlocksPerCheckpoint: 1, + numTxsPerBlock: 3, + numL1ToL2Messages: withMessages, + dump: ['rollup-tx-merge'], + }, + // A mid-checkpoint block with no txs, carrying its own message bundle: the tx-less block root, sampled from a + // position where both of its start sponges are inherited rather than initial. + { + numCheckpoints: 1, + numBlocksPerCheckpoint: 2, + numTxsPerBlock: [1, 0], + numL1ToL2Messages: 0, // Overridden by l1ToL2MessagesPerBlock. + l1ToL2MessagesPerBlock: [times(2, i => new Fr(0x900 + i)), times(3, i => new Fr(0xa00 + i))], + dump: ['rollup-block-root-no-txs'], + }, // The checkpoint-merge only appears with three checkpoints. Independently-built checkpoints do // not carry the inbox message state forward, so this scenario runs with no L1-to-L2 messages and // a zero previous rolling hash, matching the multi-checkpoint case in top-tree-orchestrator.test. @@ -93,13 +129,19 @@ describeOrSkip('prover/regenerate-rollup-sample-inputs', () => { it.each(scenarios)( 'regenerates $dump from an epoch with $numCheckpoints checkpoints, $numBlocksPerCheckpoint blocks, $numTxsPerBlock txs', - async ({ numCheckpoints, numBlocksPerCheckpoint, numTxsPerBlock, numL1ToL2Messages, dump }) => { + async ({ + numCheckpoints, + numBlocksPerCheckpoint, + numTxsPerBlock, + numL1ToL2Messages, + l1ToL2MessagesPerBlock, + dump, + }) => { + const makeProcessedTxOpts = (_: unknown, txIndex: number) => ({ privateOnly: txIndex % 2 === 0 }); const checkpoints = await timesAsync(numCheckpoints, () => - context.makeCheckpoint(numBlocksPerCheckpoint, { - numTxsPerBlock, - numL1ToL2Messages, - makeProcessedTxOpts: (_, txIndex) => ({ privateOnly: txIndex % 2 === 0 }), - }), + l1ToL2MessagesPerBlock + ? context.makeCheckpointWithMessagesPerBlock(l1ToL2MessagesPerBlock, { numTxsPerBlock, makeProcessedTxOpts }) + : context.makeCheckpoint(numBlocksPerCheckpoint, { numTxsPerBlock, numL1ToL2Messages, makeProcessedTxOpts }), ); const finalBlobChallenges = await context.getFinalBlobChallenges(); @@ -135,7 +177,8 @@ describeOrSkip('prover/regenerate-rollup-sample-inputs', () => { const { header, txs } = blocks[i]; const { blockNumber, timestamp } = header.globalVariables; - await subTree.startNewBlock(blockNumber, timestamp, txs.length, i === 0 ? l1ToL2Messages : []); + const blockMessages = l1ToL2MessagesPerBlock?.[i] ?? (i === 0 ? l1ToL2Messages : []); + await subTree.startNewBlock(blockNumber, timestamp, txs.length, blockMessages); if (txs.length > 0) { await subTree.addTxs(txs); } diff --git a/yarn-project/prover-node/src/prover-node.test.ts b/yarn-project/prover-node/src/prover-node.test.ts index fe66ecf44b6c..2ab479b5c976 100644 --- a/yarn-project/prover-node/src/prover-node.test.ts +++ b/yarn-project/prover-node/src/prover-node.test.ts @@ -708,7 +708,6 @@ describe('ProverNode', () => { */ function setupRegistrationSuccess() { worldState.syncImmediate.mockResolvedValue(undefined as any); - l1ToL2MessageSource.getL1ToL2Messages.mockResolvedValue([]); l2BlockSource.getBlockData.mockResolvedValue({ header: { lastArchive: { root: Fr.ZERO }, state: { l1ToL2MessageTree: { nextAvailableLeafIndex: 0 } } }, } as any); 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/README.md b/yarn-project/sequencer-client/README.md index 5b709095ce54..d9145611c527 100644 --- a/yarn-project/sequencer-client/README.md +++ b/yarn-project/sequencer-client/README.md @@ -142,7 +142,7 @@ Inside `execute()`: - Transitions to `INITIALIZING_CHECKPOINT`. If there is a pending invalidation, enqueue it. - Builds **pipelined-parent simulation overrides**: when building on top of a parent that hasn't landed on L1 yet, the fee-asset price modifier must be computed against the parent fee header we predicted (not the L1 one), so all in-flight checkpoints in the pipeline agree on the same modifier. - Asks the global variables builder for the slot's `CheckpointGlobalVariables` (`coinbase`, `feeRecipient`, `timestamp`, `gasFees`, `chainId`, `version`, `slotNumber`). These are shared across every block within the checkpoint — only `blockNumber` increments. - - Computes `inHash` from the L1→L2 messages for the checkpoint, and collects `previousCheckpointOutHashes` for prior checkpoints in the same epoch. + - Resolves the streaming Inbox consumption cursor (the parent checkpoint's last-consumed bucket, from the fork's L1→L2 leaf count), and collects `previousCheckpointOutHashes` for prior checkpoints in the same epoch. Each block then selects its own message bundle against the cursor (AZIP-22 Fast Inbox). - Forks world state at the parent (`closeDelayMs: 12 s`) and asks `FullNodeCheckpointsBuilder` for a `CheckpointBuilder` bound to that fork. - Runs `buildBlocksForCheckpoint()` — the per-block loop, described below. - Transitions to `ASSEMBLING_CHECKPOINT`, asks the builder to `completeCheckpoint()`, validates it against the configured caps, and asks the validator client to sign the `CheckpointProposal` (which bundles the final block proposal so the two travel together). @@ -206,7 +206,7 @@ See the [Block Building Timetable Spec](../stdlib/src/timetable/README.md) for t Key entry points: - `canProposeAt(archive, msgSender, simulationOverridesPlan?)` — eth_call simulation of `Rollup.canProposeAt`. The sequencer runs this before deciding to build. -- `enqueueProposeCheckpoint(checkpoint, attestations, attestationsSignature, opts)` — adds the propose call, with a `preCheck` that re-validates the proposal against real L1 state when it is finally sent (catches drift between build time and submit time). +- `enqueueProposeCheckpoint(checkpoint, attestations, attestationsSignature, bucketHint, opts)` — adds the propose call, with a `preCheck` that re-validates the proposal against real L1 state when it is finally sent (catches drift between build time and submit time). - `enqueueInvalidateCheckpoint`, `enqueueGovernanceCastSignal`, `enqueueSlashingActions` — the rest of the actions a proposer may bundle. - `sendRequests(targetSlot?)` — immediately flushes the queue as one Multicall3 transaction. Used by the `AutomineSequencer` for synchronous in-slot publishing. - `sendRequestsAt(targetSlot)` — the production (pipelined) path: sleeps (cancellable) until the ideal L1 send time, runs each request's `preCheck` (dropping those that fail), and then calls `sendRequests(targetSlot)`, which filters expired requests, sorts the remainder, and submits one Multicall3 that mines inside the target slot. diff --git a/yarn-project/sequencer-client/src/index.ts b/yarn-project/sequencer-client/src/index.ts index 2375b9013a90..c88e2a5f5d74 100644 --- a/yarn-project/sequencer-client/src/index.ts +++ b/yarn-project/sequencer-client/src/index.ts @@ -6,3 +6,10 @@ export { Sequencer, SequencerState, type SequencerEvents } from './sequencer/ind // Used by the node to simulate public parts of transactions. Should these be moved to a shared library? // ISSUE(#9832) export * from './global_variable_builder/index.js'; +export { + type ConsumedBucketCursor, + type InboxBucketSelection, + type InboxBucketSource, + type SelectInboxBucketInput, + selectInboxBucketForBlock, +} from './sequencer/inbox_bucket_selector.js'; 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..a2b482cf14a2 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'; @@ -113,7 +116,7 @@ const logger = createLogger('integration_l1_publisher'); const config: SequencerClientConfig & L1ContractsConfig = { ...getL1ContractsConfigEnvVars(), ...getConfigEnvVars() }; // Several consecutive checkpoints, each consuming the L1->L2 messages sent while it was being built, so real -// messages are genuinely consumed and validated on L1 (AZIP-22 Fast Inbox). +// messages are genuinely consumed and validated on L1. const numberOfConsecutiveBlocks = 3; jest.setTimeout(1000000); @@ -141,7 +144,7 @@ describe('L1Publisher integration', () => { let builderDb: NativeWorldStateService; // Backs the blockSource mock's streaming L1->L2 message queries. The world-state synchronizer reconstructs each - // block's consumed message bundle from Inbox buckets (AZIP-22 Fast Inbox) when it syncs a block back, so the test + // block's consumed message bundle from Inbox buckets when it syncs a block back, so the test // registers one bucket per published block here (see buildAndPublishBlock). let messageSource: MockL1ToL2MessageSource; @@ -370,7 +373,7 @@ describe('L1Publisher integration', () => { getBlockNumber(): Promise { return Promise.resolve(BlockNumber(blocks.at(-1)?.number ?? BlockNumber.ZERO)); }, - // Streaming L1->L2 message reconstruction (AZIP-22 Fast Inbox): the world-state synchronizer resolves each + // Streaming L1->L2 message reconstruction: the world-state synchronizer resolves each // block's consumed message bundle from the Inbox buckets registered per published block in buildAndPublishBlock. getInboxBucketByTotalMsgCount(totalMsgCount: bigint) { return messageSource.getInboxBucketByTotalMsgCount(totalMsgCount); @@ -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`. */ @@ -549,7 +552,7 @@ describe('L1Publisher integration', () => { describe('block building', () => { beforeEach(async () => { // This suite proposes consecutive checkpoints, each consuming the streaming-Inbox messages sent while it was - // being built (AZIP-22 Fast Inbox), so real messages are genuinely consumed and validated on L1. + // being built, so real messages are genuinely consumed and validated on L1. await setup(); }); @@ -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: 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/checkpoint_proposal_job.test.ts b/yarn-project/sequencer-client/src/sequencer/checkpoint_proposal_job.test.ts index 8cdddbdf0d9e..27af8fd3cf2d 100644 --- a/yarn-project/sequencer-client/src/sequencer/checkpoint_proposal_job.test.ts +++ b/yarn-project/sequencer-client/src/sequencer/checkpoint_proposal_job.test.ts @@ -249,7 +249,6 @@ describe('CheckpointProposalJob', () => { checkpointBuilder = checkpointsBuilder.createCheckpointBuilder(checkpointConstants, checkpointNumber); l1ToL2MessageSource = mock(); - l1ToL2MessageSource.getL1ToL2Messages.mockResolvedValue(Array(4).fill(Fr.ZERO)); // Genesis bucket for the empty-tree cursor above; with no newer synced buckets mocked, block bundle // selection consumes nothing by default. l1ToL2MessageSource.getInboxBucketByTotalMsgCount.mockResolvedValue({ @@ -297,12 +296,11 @@ describe('CheckpointProposalJob', () => { validatorClient = mock(); validatorClient.collectAttestations.mockImplementation(() => Promise.resolve([])); validatorClient.createBlockProposal.mockImplementation( - async (blockHeader, _checkpointNumber, indexWithinCheckpoint, inHash, archiveRoot, txs) => { + async (blockHeader, _checkpointNumber, indexWithinCheckpoint, archiveRoot, txs) => { const txHashes = await Promise.all((txs ?? []).map((tx: Tx) => tx.getTxHash())); return new BlockProposal( blockHeader, IndexWithinCheckpoint(indexWithinCheckpoint), - inHash, archiveRoot, txHashes, mockedSig, @@ -1445,8 +1443,7 @@ describe('CheckpointProposalJob', () => { expect(checkpoint).toBeDefined(); - // Streaming skips the bulk per-checkpoint fetch; every message reaches the builder through a block. - expect(l1ToL2MessageSource.getL1ToL2Messages).not.toHaveBeenCalled(); + // Streaming has no bulk per-checkpoint list; every message reaches the builder through a block. expect(checkpointsBuilder.startCheckpointCalls).toHaveLength(1); // The first block consumes the selected bundle; the second consumes nothing. @@ -1455,7 +1452,7 @@ describe('CheckpointProposalJob', () => { expect(checkpointBuilder.buildBlockCalls[1].opts.l1ToL2Messages).toEqual([]); // Both block proposals carry the selected bucket reference (the second reuses the first's). - const bucketRefArgs = validatorClient.createBlockProposal.mock.calls.map(call => call[8]); + const bucketRefArgs = validatorClient.createBlockProposal.mock.calls.map(call => call[7]); expect(bucketRefArgs).toHaveLength(2); expect(bucketRefArgs[0]?.bucketSeq).toBe(2n); expect(bucketRefArgs[1]?.bucketSeq).toBe(2n); diff --git a/yarn-project/sequencer-client/src/sequencer/checkpoint_proposal_job.timing.test.ts b/yarn-project/sequencer-client/src/sequencer/checkpoint_proposal_job.timing.test.ts index 558059f6fd2e..a11d3f131727 100644 --- a/yarn-project/sequencer-client/src/sequencer/checkpoint_proposal_job.timing.test.ts +++ b/yarn-project/sequencer-client/src/sequencer/checkpoint_proposal_job.timing.test.ts @@ -455,7 +455,6 @@ describe('CheckpointProposalJob Timing Tests', () => { worldState.fork.mockResolvedValue(mockFork); l1ToL2MessageSource = mock(); - l1ToL2MessageSource.getL1ToL2Messages.mockResolvedValue(Array(4).fill(Fr.ZERO)); l1ToL2MessageSource.getInboxBucketByTotalMsgCount.mockResolvedValue({ seq: 0n, inboxRollingHash: Fr.ZERO, diff --git a/yarn-project/sequencer-client/src/sequencer/checkpoint_proposal_job.ts b/yarn-project/sequencer-client/src/sequencer/checkpoint_proposal_job.ts index 7165a1b1db99..c0ad7b221997 100644 --- a/yarn-project/sequencer-client/src/sequencer/checkpoint_proposal_job.ts +++ b/yarn-project/sequencer-client/src/sequencer/checkpoint_proposal_job.ts @@ -648,10 +648,6 @@ export class CheckpointProposalJob implements Traceable { this.checkpointSimulationOverridesPlan, ); - // Under the streaming Inbox (AZIP-22 Fast Inbox) messages are selected per block, so the legacy inHash is fed - // zero; the running values are computed block by block. - const inHash = Fr.ZERO; - // Collect the out hashes of all the checkpoints before this one in the same epoch. // Under pipelining the parent checkpoint may not be on L1 yet at build time, so the helper // splices in the parent's checkpointOutHash from the locally-known proposed checkpoint so @@ -723,7 +719,6 @@ export class CheckpointProposalJob implements Traceable { const result = await this.buildBlocksForCheckpoint( checkpointBuilder, checkpointGlobalVariables.timestamp, - inHash, blockProposalOptions, streamingState, ); @@ -922,7 +917,6 @@ export class CheckpointProposalJob implements Traceable { private async buildBlocksForCheckpoint( checkpointBuilder: CheckpointBuilder, timestamp: bigint, - inHash: Fr, blockProposalOptions: BlockProposalOptions, streamingState?: StreamingCheckpointState, ): Promise<{ @@ -1020,9 +1014,7 @@ export class CheckpointProposalJob implements Traceable { usedTxs.forEach(tx => txHashesAlreadyIncluded.add(tx.txHash.toString())); // Streaming Inbox: the block built successfully, so advance the consumption cursor and carry this block's - // rolling-hash bucket reference. A block that consumed nothing reuses the parent bucket reference. The legacy - // inHash is dead post-flip; block proposals carry zero (AZIP-22 Fast Inbox). - const blockInHash = inHash; + // rolling-hash bucket reference. A block that consumed nothing reuses the parent bucket reference. let blockBucketRef: InboxBucketRef | undefined = undefined; if (streamingState && selection) { if (selection.consume) { @@ -1035,7 +1027,6 @@ export class CheckpointProposalJob implements Traceable { // Sign the block proposal. This will throw if HA signing fails. const proposal = await this.createBlockProposal( block, - blockInHash, usedTxs, { ...blockProposalOptions, @@ -1084,7 +1075,6 @@ export class CheckpointProposalJob implements Traceable { /** Creates a block proposal for a given block via the validator client (unless in fisherman mode) */ private createBlockProposal( block: L2Block, - inHash: Fr, usedTxs: Tx[], blockProposalOptions: BlockProposalOptions, bucketRef?: InboxBucketRef, @@ -1097,7 +1087,6 @@ export class CheckpointProposalJob implements Traceable { block.header, this.checkpointNumber, block.indexWithinCheckpoint, - inHash, block.archive.root, usedTxs, this.proposer, @@ -1177,7 +1166,7 @@ export class CheckpointProposalJob implements Traceable { indexWithinCheckpoint: IndexWithinCheckpoint; buildDeadline: Date | undefined; txHashesAlreadyIncluded: Set; - /** Streaming Inbox message bundle to insert into this block's L1-to-L2 tree; undefined in the legacy flow. */ + /** Streaming Inbox message bundle for this block's L1-to-L2 tree; undefined when it consumes nothing. */ l1ToL2Messages?: Fr[]; }, ): Promise< diff --git a/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts b/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts index 95a866e36a3e..c3ca4b8b3f00 100644 --- a/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts +++ b/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts @@ -1,4 +1,3 @@ -import { NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } 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 +348,6 @@ describe('sequencer', () => { }); l1ToL2MessageSource = mock({ - getL1ToL2Messages: () => Promise.resolve(Array(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP).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..484ef4a1e401 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, @@ -143,7 +142,6 @@ export function createBlockProposal(block: L2Block, signature: Signature): Block return new BlockProposal( block.header, block.indexWithinCheckpoint, - Fr.ZERO, // inHash - using zero for testing block.archive.root, txHashes, signature, diff --git a/yarn-project/stdlib/src/block/l2_block.ts b/yarn-project/stdlib/src/block/l2_block.ts index 06cd9133ac56..0d9975bee049 100644 --- a/yarn-project/stdlib/src/block/l2_block.ts +++ b/yarn-project/stdlib/src/block/l2_block.ts @@ -155,7 +155,6 @@ export class L2Block { * @param txsPerBlock - The number of transactions to include in the block. * @param numPublicCallsPerTx - The number of public function calls to include in each transaction. * @param numPublicLogsPerCall - The number of public logs per 1 public function invocation. - * @param inHash - The hash of the L1 to L2 messages subtree which got inserted in this block. * @returns The L2 block. */ static async random( diff --git a/yarn-project/stdlib/src/config/network-consensus-config.ts b/yarn-project/stdlib/src/config/network-consensus-config.ts index 152f29ed836c..c0fbeb13bb99 100644 --- a/yarn-project/stdlib/src/config/network-consensus-config.ts +++ b/yarn-project/stdlib/src/config/network-consensus-config.ts @@ -47,7 +47,6 @@ export const NETWORK_CONSENSUS_ENV_VARS = [ 'AZTEC_EJECTION_THRESHOLD', 'AZTEC_LOCAL_EJECTION_THRESHOLD', 'AZTEC_EXIT_DELAY_SECONDS', - 'AZTEC_INBOX_LAG', 'AZTEC_PROOF_SUBMISSION_EPOCHS', 'AZTEC_MANA_TARGET', 'AZTEC_PROVING_COST_PER_MANA', diff --git a/yarn-project/stdlib/src/interfaces/archiver.test.ts b/yarn-project/stdlib/src/interfaces/archiver.test.ts index c27cffd488c6..06ef09d352e3 100644 --- a/yarn-project/stdlib/src/interfaces/archiver.test.ts +++ b/yarn-project/stdlib/src/interfaces/archiver.test.ts @@ -204,11 +204,6 @@ describe('ArchiverApiSchema', () => { expect(result).toEqual([expect.any(Fr)]); }); - it('getL1ToL2Messages', async () => { - const result = await context.client.getL1ToL2Messages(CheckpointNumber(1)); - expect(result).toEqual([expect.any(Fr)]); - }); - it('getL1ToL2MessageIndex', async () => { const result = await context.client.getL1ToL2MessageIndex(Fr.random()); expect(result).toBe(1n); @@ -571,10 +566,6 @@ class MockArchiver implements ArchiverApi { expect(Array.isArray(signatures)).toBe(true); return Promise.resolve(); } - getL1ToL2Messages(checkpointNumber: CheckpointNumber): Promise { - expect(checkpointNumber).toEqual(CheckpointNumber(1)); - return Promise.resolve([Fr.random()]); - } getL1ToL2MessageIndex(l1ToL2Message: Fr): Promise { expect(l1ToL2Message).toBeInstanceOf(Fr); return Promise.resolve(1n); diff --git a/yarn-project/stdlib/src/interfaces/archiver.ts b/yarn-project/stdlib/src/interfaces/archiver.ts index 3728c39090cc..a1a55af686fb 100644 --- a/yarn-project/stdlib/src/interfaces/archiver.ts +++ b/yarn-project/stdlib/src/interfaces/archiver.ts @@ -136,7 +136,6 @@ export const ArchiverApiSchema: ApiSchemaFor = { }), getContractClassIds: z.function({ input: z.tuple([]), output: z.array(schemas.Fr) }), registerContractFunctionSignatures: z.function({ input: z.tuple([z.array(z.string())]), output: z.void() }), - getL1ToL2Messages: z.function({ input: z.tuple([CheckpointNumberSchema]), output: z.array(schemas.Fr) }), getL1ToL2MessageIndex: z.function({ input: z.tuple([schemas.Fr]), output: schemas.BigInt.optional() }), getLatestInboxBucketAtOrBefore: z.function({ input: z.tuple([schemas.BigInt]), diff --git a/yarn-project/stdlib/src/interfaces/aztec-node.test.ts b/yarn-project/stdlib/src/interfaces/aztec-node.test.ts index b0bfd03bf8eb..934ea3d0cde4 100644 --- a/yarn-project/stdlib/src/interfaces/aztec-node.test.ts +++ b/yarn-project/stdlib/src/interfaces/aztec-node.test.ts @@ -154,11 +154,6 @@ describe('AztecNodeApiSchema', () => { expect(response).toEqual([1n, expect.any(SiblingPath)]); }); - it('getL1ToL2MessageCheckpoint', async () => { - const response = await context.client.getL1ToL2MessageCheckpoint(Fr.random()); - expect(response).toEqual(5); - }); - it('getL1ToL2MessageIndex', async () => { const response = await context.client.getL1ToL2MessageIndex(Fr.random()); expect(response).toEqual(5n); @@ -715,10 +710,6 @@ class MockAztecNode implements AztecNode { expect(noteHash).toBeInstanceOf(Fr); return Promise.resolve(MembershipWitness.random(NOTE_HASH_TREE_HEIGHT)); } - getL1ToL2MessageCheckpoint(l1ToL2Message: Fr): Promise { - expect(l1ToL2Message).toBeInstanceOf(Fr); - return Promise.resolve(CheckpointNumber(5)); - } getL1ToL2MessageIndex(l1ToL2Message: Fr): Promise { expect(l1ToL2Message).toBeInstanceOf(Fr); return Promise.resolve(5n); diff --git a/yarn-project/stdlib/src/interfaces/aztec-node.ts b/yarn-project/stdlib/src/interfaces/aztec-node.ts index 26d9a2fb0556..6e5881f50ec7 100644 --- a/yarn-project/stdlib/src/interfaces/aztec-node.ts +++ b/yarn-project/stdlib/src/interfaces/aztec-node.ts @@ -200,13 +200,10 @@ export interface AztecNode { l1ToL2Message: Fr, ): Promise<[bigint, SiblingPath] | undefined>; - /** Returns the L2 checkpoint number in which this L1 to L2 message becomes available, or undefined if not found. */ - getL1ToL2MessageCheckpoint(l1ToL2Message: Fr): Promise; - /** * Returns the compact leaf index assigned to this L1 to L2 message as soon as the node has ingested it from L1, - * before any L2 block consumes it. Returns undefined if the node has not yet seen the message. Unlike - * {@link getL1ToL2MessageCheckpoint}, this does not require the L2 chain to have advanced past the message. + * before any L2 block consumes it. Returns undefined if the node has not yet seen the message. The message becomes + * consumable once a block's L1-to-L2 message tree grows past this index. */ getL1ToL2MessageIndex(l1ToL2Message: Fr): Promise; @@ -616,8 +613,6 @@ export const AztecNodeApiSchema: ApiSchemaFor = { output: z.tuple([schemas.BigInt, SiblingPath.schemaFor(L1_TO_L2_MSG_TREE_HEIGHT)]).optional(), }), - getL1ToL2MessageCheckpoint: z.function({ input: z.tuple([schemas.Fr]), output: CheckpointNumberSchema.optional() }), - getL1ToL2MessageIndex: z.function({ input: z.tuple([schemas.Fr]), output: schemas.BigInt.optional() }), getL2ToL1Messages: z.function({ diff --git a/yarn-project/stdlib/src/interfaces/validator.ts b/yarn-project/stdlib/src/interfaces/validator.ts index f17e96e2a475..17903fdbe645 100644 --- a/yarn-project/stdlib/src/interfaces/validator.ts +++ b/yarn-project/stdlib/src/interfaces/validator.ts @@ -153,7 +153,6 @@ export interface Validator { blockHeader: BlockHeader, checkpointNumber: CheckpointNumber, indexWithinCheckpoint: number, - inHash: Fr, archive: Fr, txs: Tx[], proposerAddress: EthAddress | undefined, diff --git a/yarn-project/stdlib/src/messaging/in_hash.ts b/yarn-project/stdlib/src/messaging/in_hash.ts deleted file mode 100644 index 52833bf937fe..000000000000 --- a/yarn-project/stdlib/src/messaging/in_hash.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/constants'; -import { padArrayEnd } from '@aztec/foundation/collection'; -import { Fr } from '@aztec/foundation/curves/bn254'; -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); - 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 deleted file mode 100644 index 21d703d06150..000000000000 --- a/yarn-project/stdlib/src/messaging/inbox_leaf.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { INITIAL_CHECKPOINT_NUMBER, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/constants'; -import { toBigIntBE } from '@aztec/foundation/bigint-buffer'; -import { CheckpointNumber } from '@aztec/foundation/branded-types'; -import { Fr } from '@aztec/foundation/curves/bn254'; -import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; - -export class InboxLeaf { - constructor( - /** Index of the leaf in the whole tree. */ - public readonly index: bigint, - /** Leaf in the subtree/message hash. */ - public readonly leaf: Fr, - ) {} - - toBuffer(): Buffer { - return serializeToBuffer([this.index, this.leaf]); - } - - fromBuffer(buffer: Buffer | BufferReader): InboxLeaf { - const reader = BufferReader.asReader(buffer); - const index = toBigIntBE(reader.readBytes(32)); - const leaf = reader.readObject(Fr); - return new InboxLeaf(index, leaf); - } - - static smallestIndexForCheckpoint(checkpointNumber: CheckpointNumber): bigint { - return BigInt(checkpointNumber - INITIAL_CHECKPOINT_NUMBER) * BigInt(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP); - } - - /** - * Returns the range of valid indices for a given checkpoint. - * Start index is inclusive, end index is exclusive. - */ - static indexRangeForCheckpoint(checkpointNumber: CheckpointNumber): [bigint, bigint] { - const start = this.smallestIndexForCheckpoint(checkpointNumber); - const end = start + BigInt(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP); - 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); - } -} diff --git a/yarn-project/stdlib/src/messaging/index.ts b/yarn-project/stdlib/src/messaging/index.ts index aad4f1a91990..3f8fc52654d4 100644 --- a/yarn-project/stdlib/src/messaging/index.ts +++ b/yarn-project/stdlib/src/messaging/index.ts @@ -1,8 +1,6 @@ export * from './append_l1_to_l2_messages.js'; -export * from './in_hash.js'; export * from './inbox_bucket.js'; export * from './inbox_consumption.js'; -export * from './inbox_leaf.js'; export * from './inbox_rolling_hash.js'; export * from './l1_to_l2_message_bundle.js'; export * from './l1_to_l2_message_sponge.js'; diff --git a/yarn-project/stdlib/src/messaging/l1_to_l2_message_source.ts b/yarn-project/stdlib/src/messaging/l1_to_l2_message_source.ts index 82610dbb461a..4040483f144b 100644 --- a/yarn-project/stdlib/src/messaging/l1_to_l2_message_source.ts +++ b/yarn-project/stdlib/src/messaging/l1_to_l2_message_source.ts @@ -1,4 +1,3 @@ -import type { CheckpointNumber } from '@aztec/foundation/branded-types'; import type { Fr } from '@aztec/foundation/curves/bn254'; import type { L2Tips } from '../block/l2_block_source.js'; @@ -8,15 +7,6 @@ import type { InboxBucket } from './inbox_bucket.js'; * Interface of classes allowing for the retrieval of L1 to L2 messages. */ export interface L1ToL2MessageSource { - /** - * Gets new L1 to L2 message (to be) included in a given checkpoint. - * @param checkpointNumber - Checkpoint number to get messages for. - * @returns The L1 to L2 messages/leaves of the messages subtree. - * @throws If the message tree for the given checkpoint has not yet been sealed on L1 - * (i.e., checkpointNumber >= inbox treeInProgress). - */ - getL1ToL2Messages(checkpointNumber: CheckpointNumber): Promise; - /** * Gets the L1 to L2 message index in the L1 to L2 message tree. * @param l1ToL2Message - The L1 to L2 message. diff --git a/yarn-project/stdlib/src/messaging/l1_to_l2_message_sponge.ts b/yarn-project/stdlib/src/messaging/l1_to_l2_message_sponge.ts index 50d4916aecbb..0a874572a9f8 100644 --- a/yarn-project/stdlib/src/messaging/l1_to_l2_message_sponge.ts +++ b/yarn-project/stdlib/src/messaging/l1_to_l2_message_sponge.ts @@ -7,7 +7,7 @@ import type { FieldsOf } from '@aztec/foundation/types'; * An absorb-only Poseidon2 sponge over L1-to-L2 message leaves. * * Mirrors `L1ToL2MessageSponge` in - * `noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/l1_to_l2_message_sponge.nr`. Each block of a + * `noir-projects/fnd/noir-protocol-circuits/crates/rollup-lib/src/abis/l1_to_l2_message_sponge.nr`. Each block of a * checkpoint absorbs its message bundle into the sponge it inherited from the previous block; the checkpoint root * recomputes the sponge over the checkpoint's whole message list and asserts the accumulated states are equal. */ diff --git a/yarn-project/stdlib/src/p2p/block_proposal.test.ts b/yarn-project/stdlib/src/p2p/block_proposal.test.ts index 9ac84730babc..abe06317c4ba 100644 --- a/yarn-project/stdlib/src/p2p/block_proposal.test.ts +++ b/yarn-project/stdlib/src/p2p/block_proposal.test.ts @@ -23,7 +23,6 @@ const makeLegacyFixtureProposal = () => new BlockProposal( BlockHeader.empty(), IndexWithinCheckpoint(3), - new Fr(42n), new Fr(99n), [TxHash.fromField(new Fr(7n)), TxHash.fromField(new Fr(8n))], Signature.empty(), @@ -105,7 +104,6 @@ describe('Block Proposal serialization / deserialization', () => { const tampered = new BlockProposal( proposal.blockHeader, proposal.indexWithinCheckpoint, - proposal.inHash, proposal.archiveRoot, proposal.txHashes, proposal.signature, @@ -161,7 +159,6 @@ describe('Block Proposal serialization / deserialization', () => { const withoutRef = await makeBlockProposal({ blockHeader: withRef.blockHeader, indexWithinCheckpoint: withRef.indexWithinCheckpoint, - inHash: withRef.inHash, archiveRoot: withRef.archiveRoot, txHashes: withRef.txHashes, }); @@ -195,7 +192,6 @@ describe('Block Proposal serialization / deserialization', () => { const tampered = new BlockProposal( proposal.blockHeader, proposal.indexWithinCheckpoint, - proposal.inHash, proposal.archiveRoot, proposal.txHashes, proposal.signature, @@ -215,7 +211,6 @@ describe('Block Proposal serialization / deserialization', () => { const injected = new BlockProposal( proposal.blockHeader, proposal.indexWithinCheckpoint, - proposal.inHash, proposal.archiveRoot, proposal.txHashes, proposal.signature, diff --git a/yarn-project/stdlib/src/p2p/block_proposal.ts b/yarn-project/stdlib/src/p2p/block_proposal.ts index 750fde3f77ed..8bfe553c184f 100644 --- a/yarn-project/stdlib/src/p2p/block_proposal.ts +++ b/yarn-project/stdlib/src/p2p/block_proposal.ts @@ -71,9 +71,6 @@ export class BlockProposal extends Gossipable implements Signable { /** Index of this block within the checkpoint (0-indexed) */ public readonly indexWithinCheckpoint: IndexWithinCheckpoint, - /** Hash of L1 to L2 messages for this checkpoint (constant across all blocks in checkpoint) */ - public readonly inHash: Fr, - /** Archive root after this block is applied */ public readonly archiveRoot: Fr, @@ -132,15 +129,14 @@ export class BlockProposal extends Gossipable implements Signable { /** * Get the payload to sign for this block proposal. - * The signature is over: blockHeader + indexWithinCheckpoint + inHash + archiveRoot + txHashes, plus the bucket - * reference when set. Appending only when set keeps the pre-flip signed payload byte-identical to the legacy format, - * while binding the reference to the signature so a relay cannot strip or inject it without breaking recovery. + * The signature is over: blockHeader + indexWithinCheckpoint + archiveRoot + txHashes, plus the bucket reference + * when set. Appending only when set binds the reference to the signature so a relay cannot strip or inject it + * without breaking recovery. */ getPayloadToSign(): Buffer { return serializeToBuffer([ this.blockHeader, this.indexWithinCheckpoint, - this.inHash, this.archiveRoot, this.txHashes.length, this.txHashes, @@ -167,7 +163,6 @@ export class BlockProposal extends Gossipable implements Signable { blockHeader: BlockHeader, checkpointNumber: CheckpointNumber, indexWithinCheckpoint: IndexWithinCheckpoint, - inHash: Fr, archiveRoot: Fr, txHashes: TxHash[], txs: Tx[] | undefined, @@ -180,7 +175,6 @@ export class BlockProposal extends Gossipable implements Signable { const tempProposal = new BlockProposal( blockHeader, indexWithinCheckpoint, - inHash, archiveRoot, txHashes, Signature.empty(), @@ -216,7 +210,6 @@ export class BlockProposal extends Gossipable implements Signable { return new BlockProposal( blockHeader, indexWithinCheckpoint, - inHash, archiveRoot, txHashes, sig, @@ -263,7 +256,6 @@ export class BlockProposal extends Gossipable implements Signable { const buffer: any[] = [ this.blockHeader, this.indexWithinCheckpoint, - this.inHash, this.archiveRoot, this.signature, serializeCoordinationSignatureContext(this.signatureContext), @@ -276,8 +268,8 @@ export class BlockProposal extends Gossipable implements Signable { } else { buffer.push(0); // hasSignedTxs = false } - // Optional bucket-reference tail (AZIP-22 Fast Inbox). Appended only when set, so pre-flip proposals serialize - // byte-identically to the legacy format and mixed-version peers keep decoding them. + // Optional bucket-reference tail. Appended only when set, so a proposal without a reference + // serializes without the tail and a decoder that reaches EOF reads it as unset. if (this.bucketRef) { buffer.push(1); // hasBucketRef = true buffer.push(this.bucketRef.toBuffer()); @@ -290,7 +282,6 @@ export class BlockProposal extends Gossipable implements Signable { const blockHeader = reader.readObject(BlockHeader); const indexWithinCheckpoint = IndexWithinCheckpoint(reader.readNumber()); - const inHash = reader.readObject(Fr); const archiveRoot = reader.readObject(Fr); const signature = reader.readObject(Signature); const signatureContext = readCoordinationSignatureContext(reader); @@ -308,8 +299,8 @@ export class BlockProposal extends Gossipable implements Signable { } } - // Optional bucket-reference tail (AZIP-22 Fast Inbox). Legacy buffers end after the signedTxs flag, so EOF here - // decodes as "no reference" — this is the cross-version tolerance that keeps mixed-version gossip working. + // Optional bucket-reference tail. A buffer that ends after the signedTxs flag decodes as + // "no reference", so proposals written without the tail round-trip cleanly. let bucketRef: InboxBucketRef | undefined; if (!reader.isEmpty()) { const hasBucketRef = reader.readNumber(); @@ -321,7 +312,6 @@ export class BlockProposal extends Gossipable implements Signable { return new BlockProposal( blockHeader, indexWithinCheckpoint, - inHash, archiveRoot, txHashes, signature, @@ -335,7 +325,6 @@ export class BlockProposal extends Gossipable implements Signable { return ( this.blockHeader.getSize() + 4 /* indexWithinCheckpoint */ + - this.inHash.size + this.archiveRoot.size + this.signature.getSize() + 4 /* chainId */ + @@ -353,7 +342,6 @@ export class BlockProposal extends Gossipable implements Signable { BlockHeader.empty(), IndexWithinCheckpoint(0), Fr.ZERO, - Fr.ZERO, [], Signature.empty(), EMPTY_COORDINATION_SIGNATURE_CONTEXT, @@ -365,7 +353,6 @@ export class BlockProposal extends Gossipable implements Signable { BlockHeader.random(), IndexWithinCheckpoint(Math.floor(Math.random() * 5)), Fr.random(), - Fr.random(), [TxHash.random(), TxHash.random()], Signature.random(), EMPTY_COORDINATION_SIGNATURE_CONTEXT, @@ -376,7 +363,6 @@ export class BlockProposal extends Gossipable implements Signable { return { blockHeader: this.blockHeader.toInspect(), indexWithinCheckpoint: this.indexWithinCheckpoint, - inHash: this.inHash.toString(), archiveRoot: this.archiveRoot.toString(), signature: this.signature.toString(), txHashes: this.txHashes.map(h => h.toString()), @@ -404,7 +390,6 @@ export class BlockProposal extends Gossipable implements Signable { return new BlockProposal( this.blockHeader, this.indexWithinCheckpoint, - this.inHash, this.archiveRoot, this.txHashes, this.signature, diff --git a/yarn-project/stdlib/src/p2p/checkpoint_proposal.ts b/yarn-project/stdlib/src/p2p/checkpoint_proposal.ts index 126e24b56dc4..a22dd24f21c2 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 + // 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,6 @@ export class CheckpointProposal extends Gossipable implements Signable { return new BlockProposal( this.lastBlock.blockHeader, this.lastBlock.indexWithinCheckpoint, - this.checkpointHeader.inHash, this.archive, this.lastBlock.txHashes, this.lastBlock.signature, @@ -302,8 +294,8 @@ export class CheckpointProposal extends Gossipable implements Signable { } else { buffer.push(0); // hasSignedTxs = false } - // Optional bucket-reference tail (AZIP-22 Fast Inbox). Appended only when set, so pre-flip proposals serialize - // byte-identically to the legacy format and mixed-version peers keep decoding them. + // Optional bucket-reference tail. Appended only when set, so a proposal without a reference + // serializes without the tail and a decoder that reaches EOF reads it as unset. if (this.lastBlock.bucketRef) { buffer.push(1); // hasBucketRef = true buffer.push(this.lastBlock.bucketRef.toBuffer()); @@ -344,8 +336,8 @@ export class CheckpointProposal extends Gossipable implements Signable { } } - // Optional bucket-reference tail (AZIP-22 Fast Inbox). Legacy buffers end after the signedTxs flag, so EOF here - // decodes as "no reference" — the cross-version tolerance that keeps mixed-version gossip working. + // Optional bucket-reference tail. A buffer that ends after the signedTxs flag decodes as + // "no reference", so proposals written without the tail round-trip cleanly. let bucketRef: InboxBucketRef | undefined; if (!reader.isEmpty()) { const hasBucketRef = reader.readNumber(); diff --git a/yarn-project/stdlib/src/p2p/wire_compat_fixtures.ts b/yarn-project/stdlib/src/p2p/wire_compat_fixtures.ts index 61f5720243ce..59813e0aee65 100644 --- a/yarn-project/stdlib/src/p2p/wire_compat_fixtures.ts +++ b/yarn-project/stdlib/src/p2p/wire_compat_fixtures.ts @@ -1,22 +1,23 @@ -// 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 were refreshed for the removal of the legacy `inHash` +// field; the checkpoint-proposal fixture was refreshed for the checkpoint-header format change. // // Both fixtures come from a deterministic proposal built with: -// BlockHeader.empty(), IndexWithinCheckpoint(3), inHash=Fr(42), archiveRoot=Fr(99), +// BlockHeader.empty(), IndexWithinCheckpoint(3), 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 = - '0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000630000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000800000000'; + '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000630000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000800000000'; /** 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 = - '0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000630000000200000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000008'; + '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000630000000200000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000008'; 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 e8640411a3f0..87cd3a273995 100644 --- a/yarn-project/stdlib/src/parity/inbox_parity_private_inputs.ts +++ b/yarn-project/stdlib/src/parity/inbox_parity_private_inputs.ts @@ -39,8 +39,6 @@ export class InboxParityPrivateInputs { public readonly numMessages: number, /** Inbox rolling hash before this checkpoint's messages (the previous checkpoint's end; genesis is zero). */ public readonly startRollingHash: Fr, - /** 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. */ @@ -55,13 +53,7 @@ export class InboxParityPrivateInputs { * Builds the inputs from a checkpoint's real messages, sizing the circuit by the message count and padding the * message array out to that size. */ - static fromMessages( - messages: Fr[], - startRollingHash: Fr, - inHash: Fr, - vkTreeRoot: Fr, - proverId: Fr, - ): InboxParityPrivateInputs { + static fromMessages(messages: Fr[], startRollingHash: Fr, vkTreeRoot: Fr, proverId: Fr): InboxParityPrivateInputs { const size = pickInboxParitySize(messages.length); // Explicit `` keeps the result `Fr[]`; padding to the union-literal `size` would infer a deep tuple. return new InboxParityPrivateInputs( @@ -69,7 +61,6 @@ export class InboxParityPrivateInputs { padArrayEnd(messages, Fr.ZERO, size), messages.length, startRollingHash, - inHash, vkTreeRoot, proverId, ); @@ -82,7 +73,6 @@ export class InboxParityPrivateInputs { this.messages, new Fr(this.numMessages), this.startRollingHash, - this.inHash, this.vkTreeRoot, this.proverId, ); @@ -109,7 +99,6 @@ export class InboxParityPrivateInputs { Fr.fromBuffer(reader), 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 bb9c1fb0884e..ce724b9368a2 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. */ @@ -25,11 +20,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. @@ -37,7 +28,6 @@ export class ParityPublicInputs { */ toBuffer() { return serializeToBuffer( - this.inHash, this.startRollingHash, this.endRollingHash, this.endSponge, @@ -76,7 +66,6 @@ export class ParityPublicInputs { */ static getFields(fields: FieldsOf) { return [ - fields.inHash, fields.startRollingHash, fields.endRollingHash, fields.endSponge, @@ -94,7 +83,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 73719021b42d..e0106c310954 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..85fece4ec6f2 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: + * 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..73230264205f 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: 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: 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 d09525fd7545..0dfbbdcbea5c 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, @@ -820,7 +820,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 + 0x580), @@ -839,7 +838,6 @@ export function makeInboxParityPrivateInputs(seed = 0): InboxParityPrivateInputs messages, numMsgs, new Fr(seed + 0x3500), - new Fr(seed + 0x3900), new Fr(seed + 0x4000), new Fr(seed + 0x5000), ); @@ -885,7 +883,6 @@ export function makeCheckpointHeader(seed = 0, overrides: Partial => { const blockHeader = options?.blockHeader ?? makeBlockHeader(1); const indexWithinCheckpoint = options?.indexWithinCheckpoint ?? IndexWithinCheckpoint(0); - const inHash = options?.inHash ?? Fr.random(); const archiveRoot = options?.archiveRoot ?? Fr.random(); const txHashes = options?.txHashes ?? [0, 1, 2, 3, 4, 5].map(() => TxHash.random()); const txs = options?.txs; @@ -610,7 +605,6 @@ export const makeBlockProposal = (options?: MakeBlockProposalOptions): Promise 0, then validate global variables match parent (chainId, version, slotNumber, timestamp, coinbase, feeRecipient, gasFees) -7. Verify inHash matches computed from L1-to-L2 messages +7. Run the streaming Inbox acceptance checks on the proposal's bucket reference and derive the block's L1-to-L2 message bundle (AZIP-22 Fast Inbox) 8. Collect transactions from pool/network/proposal 9. Re-execute transactions (if enabled) 10. Compare re-execution result with proposal diff --git a/yarn-project/validator-client/src/duties/validation_service.test.ts b/yarn-project/validator-client/src/duties/validation_service.test.ts index 1cecf73317ac..b631709247e5 100644 --- a/yarn-project/validator-client/src/duties/validation_service.test.ts +++ b/yarn-project/validator-client/src/duties/validation_service.test.ts @@ -34,14 +34,12 @@ describe('ValidationService', () => { const txs = await Promise.all([Tx.random(), Tx.random()]); const blockHeader = makeBlockHeader(1); const indexWithinCheckpoint = IndexWithinCheckpoint(0); - const inHash = Fr.random(); const archive = Fr.random(); const proposal = await service.createBlockProposal( blockHeader, CheckpointNumber(1), indexWithinCheckpoint, - inHash, archive, txs, addresses[0], @@ -56,14 +54,12 @@ describe('ValidationService', () => { const txs = await Promise.all([Tx.random(), Tx.random()]); const blockHeader = makeBlockHeader(1); const indexWithinCheckpoint = IndexWithinCheckpoint(0); - const inHash = Fr.random(); const archive = Fr.random(); const proposal = await service.createBlockProposal( blockHeader, CheckpointNumber(1), indexWithinCheckpoint, - inHash, archive, txs, addresses[0], @@ -89,13 +85,12 @@ describe('ValidationService', () => { 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. const blockProposal = await service.createBlockProposal( blockHeader, CheckpointNumber(1), indexWithinCheckpoint, - checkpointHeader.inHash, archive, txs, addresses[0], diff --git a/yarn-project/validator-client/src/duties/validation_service.ts b/yarn-project/validator-client/src/duties/validation_service.ts index d5f2157d7a49..c6c7d1143def 100644 --- a/yarn-project/validator-client/src/duties/validation_service.ts +++ b/yarn-project/validator-client/src/duties/validation_service.ts @@ -35,7 +35,6 @@ export class ValidationService { * * @param blockHeader - The block header * @param blockIndexWithinCheckpoint - The block index within checkpoint for HA signing context - * @param inHash - Hash of L1 to L2 messages for this checkpoint * @param archive - The archive of the current block * @param txs - Ordered list of transactions (Tx[]) * @param proposerAttesterAddress - The address of the proposer/attester, or undefined @@ -49,7 +48,6 @@ export class ValidationService { blockHeader: BlockHeader, checkpointNumber: CheckpointNumber, blockIndexWithinCheckpoint: IndexWithinCheckpoint, - inHash: Fr, archive: Fr, txs: Tx[], proposerAttesterAddress: EthAddress | undefined, @@ -77,7 +75,6 @@ export class ValidationService { blockHeader, checkpointNumber, blockIndexWithinCheckpoint, - inHash, archive, txs.map(tx => tx.getTxHash()), options.publishFullTxs ? txs : undefined, diff --git a/yarn-project/validator-client/src/metrics.ts b/yarn-project/validator-client/src/metrics.ts index 8aad926ca859..401584c6a5f7 100644 --- a/yarn-project/validator-client/src/metrics.ts +++ b/yarn-project/validator-client/src/metrics.ts @@ -43,13 +43,7 @@ export class ValidatorMetrics { meter, Metrics.VALIDATOR_ATTESTATION_FAILED_BAD_PROPOSAL_COUNT, { - [Attributes.ERROR_TYPE]: [ - 'invalid_proposal', - 'state_mismatch', - 'failed_txs', - 'in_hash_mismatch', - 'parent_block_wrong_slot', - ], + [Attributes.ERROR_TYPE]: ['invalid_proposal', 'state_mismatch', 'failed_txs', 'parent_block_wrong_slot'], [Attributes.IS_COMMITTEE_MEMBER]: [true, false], }, ); diff --git a/yarn-project/validator-client/src/proposal_handler.test.ts b/yarn-project/validator-client/src/proposal_handler.test.ts index b0421851a91e..c51398e295bd 100644 --- a/yarn-project/validator-client/src/proposal_handler.test.ts +++ b/yarn-project/validator-client/src/proposal_handler.test.ts @@ -66,7 +66,6 @@ describe('ProposalHandler checkpoint validation', () => { blockSource.syncImmediate.mockResolvedValue(undefined); l1ToL2MessageSource = mock(); - l1ToL2MessageSource.getL1ToL2Messages.mockResolvedValue([]); checkpointsBuilder = mock(); checkpointsBuilder.getConfig.mockReturnValue({ diff --git a/yarn-project/validator-client/src/proposal_handler.ts b/yarn-project/validator-client/src/proposal_handler.ts index 107b56ed3228..4b528a466f50 100644 --- a/yarn-project/validator-client/src/proposal_handler.ts +++ b/yarn-project/validator-client/src/proposal_handler.ts @@ -73,7 +73,6 @@ export type BlockProposalValidationFailureReason = | 'invalid_proposal' | 'parent_block_not_found' | 'parent_block_wrong_slot' - | 'in_hash_mismatch' // Streaming Inbox per-block acceptance failures. | StreamingBlockCheckReason | 'global_variables_mismatch' @@ -192,7 +191,6 @@ export const SLASHABLE_BLOCK_PROPOSAL_VALIDATION_RESULT: BlockProposalValidation 'global_variables_mismatch', 'invalid_proposal', 'parent_block_wrong_slot', - 'in_hash_mismatch', ]; /** Checkpoint-proposal validation failures that constitute a slashable invalid-checkpoint offense. */ diff --git a/yarn-project/validator-client/src/validator.ha.integration.test.ts b/yarn-project/validator-client/src/validator.ha.integration.test.ts index b6bd10e4ad1d..e1f9e292ca7b 100644 --- a/yarn-project/validator-client/src/validator.ha.integration.test.ts +++ b/yarn-project/validator-client/src/validator.ha.integration.test.ts @@ -19,7 +19,6 @@ import { AztecAddress } from '@aztec/stdlib/aztec-address'; import type { L2BlockSink, L2BlockSource } from '@aztec/stdlib/block'; import { CheckpointReexecutionTracker } from '@aztec/stdlib/checkpoint'; import type { SlasherConfig, ValidatorClientFullConfig, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server'; -import { computeInHashFromL1ToL2Messages } from '@aztec/stdlib/messaging'; import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging'; import { TEST_COORDINATION_SIGNATURE_CONTEXT, @@ -115,7 +114,6 @@ describe('ValidatorClient HA Integration', () => { blockSource = mock(); l1ToL2MessageSource = mock(); txProvider = mock(); - l1ToL2MessageSource.getL1ToL2Messages.mockResolvedValue([]); dateProvider = new TestDateProvider(); blobClient = mock(); blobClient.canUpload.mockReturnValue(false); @@ -306,7 +304,6 @@ describe('ValidatorClient HA Integration', () => { // Use all 5 validators - all try to create the same block proposal const blockHeader = makeBlockHeader(1); const indexWithinCheckpoint = IndexWithinCheckpoint(0); - const inHash = computeInHashFromL1ToL2Messages([]); const archive = Fr.random(); const txs = await Promise.all([1, 2, 3].map(() => mockTx())); const proposerAddress = EthAddress.fromString(validatorAccounts[0].address); @@ -318,7 +315,6 @@ describe('ValidatorClient HA Integration', () => { blockHeader, CheckpointNumber(1), indexWithinCheckpoint, - inHash, archive, txs, proposerAddress, @@ -348,7 +344,6 @@ describe('ValidatorClient HA Integration', () => { it('should allow different validators to create proposals for different slots', async () => { const proposerAddress = EthAddress.fromString(validatorAccounts[0].address); const txs = await Promise.all([1, 2, 3].map(() => mockTx())); - const inHash = computeInHashFromL1ToL2Messages([]); // Each of the 5 validators creates a proposal for a different slot const proposals = await Promise.all( @@ -359,7 +354,6 @@ describe('ValidatorClient HA Integration', () => { blockHeader, CheckpointNumber(1), IndexWithinCheckpoint(0), - inHash, archive, txs, proposerAddress, diff --git a/yarn-project/validator-client/src/validator.integration.test.ts b/yarn-project/validator-client/src/validator.integration.test.ts index d12bb5608f50..ee3c996e3b76 100644 --- a/yarn-project/validator-client/src/validator.integration.test.ts +++ b/yarn-project/validator-client/src/validator.integration.test.ts @@ -26,7 +26,7 @@ import { CheckpointReexecutionTracker, L1PublishedData, PublishedCheckpoint } fr import { type L1RollupConstants, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers'; import { Gas, GasFees } from '@aztec/stdlib/gas'; import { tryStop } from '@aztec/stdlib/interfaces/server'; -import { InboxBucketRef, computeInHashFromL1ToL2Messages } from '@aztec/stdlib/messaging'; +import { InboxBucketRef } from '@aztec/stdlib/messaging'; import { type BlockProposal, CheckpointProposal } from '@aztec/stdlib/p2p'; import { mockTx } from '@aztec/stdlib/testing'; import { BlockHeader, type CheckpointGlobalVariables, Tx } from '@aztec/stdlib/tx'; @@ -240,7 +240,7 @@ describe('ValidatorClient Integration', () => { type BlockProposalResult = { block: L2Block; proposal: BlockProposal }; - /** Builds a new block proposal with the given txs and l1-to-l2 messages */ + /** Builds a new block proposal with the given txs and L1-to-L2 message bundle */ const buildBlockProposal = async ( checkpointBuilder: CheckpointBuilder, blockNumber: BlockNumber, @@ -248,7 +248,6 @@ describe('ValidatorClient Integration', () => { txs: Tx[] = [], l1ToL2Messages: Fr[] = [], ): Promise<{ block: L2Block; proposal: BlockProposal }> => { - const inHash = computeInHashFromL1ToL2Messages(l1ToL2Messages); const blockTimestamp = getTimestampForSlot(checkpointBuilder.getConstantData().slotNumber, l1Constants); const { block, usedTxs } = await checkpointBuilder.buildBlock(txs, blockNumber, blockTimestamp, { isBuildingProposal: true, @@ -270,7 +269,6 @@ describe('ValidatorClient Integration', () => { block.header, cpNumber, block.indexWithinCheckpoint, - inHash, block.archive.root, usedTxs, proposerSigner.address, @@ -478,7 +476,7 @@ describe('ValidatorClient Integration', () => { it('validates and attests with txs anchored to proposed blocks and non-empty l1-to-l2 messages', async () => { // Create l1 to l2 messages and seed them into the archivers - const l1ToL2Messages = makeInboxMessages(4, { messagesPerCheckpoint: 4 }); + const l1ToL2Messages = makeInboxMessages(4); await proposer.archiver.dataStores.messages.addL1ToL2MessageBuckets(l1ToL2Messages); await attestor.archiver.dataStores.messages.addL1ToL2MessageBuckets(l1ToL2Messages); @@ -678,10 +676,10 @@ describe('ValidatorClient Integration', () => { }); it('refuses block proposal with mismatching l1 to l2 messages', async () => { - const l1ToL2Messages = makeInboxMessages(4, { messagesPerCheckpoint: 4 }); + const l1ToL2Messages = makeInboxMessages(4); await proposer.archiver.dataStores.messages.addL1ToL2MessageBuckets(l1ToL2Messages); - const otherL1ToL2Messages = makeInboxMessages(4, { messagesPerCheckpoint: 4 }); + const otherL1ToL2Messages = makeInboxMessages(4); await attestor.archiver.dataStores.messages.addL1ToL2MessageBuckets(otherL1ToL2Messages); const { blocks } = await buildCheckpoint( diff --git a/yarn-project/validator-client/src/validator.test.ts b/yarn-project/validator-client/src/validator.test.ts index 1af680ff3218..47f4ef07cd46 100644 --- a/yarn-project/validator-client/src/validator.test.ts +++ b/yarn-project/validator-client/src/validator.test.ts @@ -32,12 +32,7 @@ import { AztecAddress } from '@aztec/stdlib/aztec-address'; import { type BlockData, BlockHash, L2Block, type L2BlockSink, type L2BlockSource } from '@aztec/stdlib/block'; import { type Checkpoint, CheckpointReexecutionTracker, type ProposedCheckpointData } from '@aztec/stdlib/checkpoint'; import type { SlasherConfig, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server'; -import { - type InboxBucket, - InboxBucketRef, - type L1ToL2MessageSource, - computeInHashFromL1ToL2Messages, -} from '@aztec/stdlib/messaging'; +import { type InboxBucket, InboxBucketRef, type L1ToL2MessageSource } from '@aztec/stdlib/messaging'; import type { BlockProposal } from '@aztec/stdlib/p2p'; import { CheckpointHeader } from '@aztec/stdlib/rollup'; import { @@ -184,7 +179,6 @@ describe('ValidatorClient', () => { epochCache.isEscapeHatchOpenAtSlot.mockResolvedValue(false); l1ToL2MessageSource = mock(); txProvider = mock(); - l1ToL2MessageSource.getL1ToL2Messages.mockResolvedValue([]); dateProvider = new TestDateProvider(); blobClient = mock(); blobClient.canUpload.mockReturnValue(false); @@ -243,7 +237,6 @@ describe('ValidatorClient', () => { it('should create a valid block proposal without txs', async () => { const blockHeader = makeBlockHeader(); const indexWithinCheckpoint = IndexWithinCheckpoint(0); - const inHash = Fr.random(); const archive = Fr.random(); const txs = await Promise.all([1, 2, 3, 4, 5].map(() => mockTx())); @@ -251,7 +244,6 @@ describe('ValidatorClient', () => { blockHeader, CheckpointNumber(1), indexWithinCheckpoint, - inHash, archive, txs, EthAddress.fromString(validatorAccounts[0].address), @@ -471,10 +463,9 @@ describe('ValidatorClient', () => { const genesisBucketRef = InboxBucketRef.fromBucket(genesisInboxBucket); beforeEach(async () => { - const emptyInHash = computeInHashFromL1ToL2Messages([]); const blockHeader = makeBlockHeader(1, { blockNumber: BlockNumber(100), slotNumber: SlotNumber(100) }); blockNumber = BlockNumber(blockHeader.globalVariables.blockNumber); - proposal = await makeBlockProposal({ blockHeader, inHash: emptyInHash, bucketRef: genesisBucketRef }); + proposal = await makeBlockProposal({ blockHeader, bucketRef: genesisBucketRef }); // The proposal targets slot 100, which under pipelining is built during the previous slot. Set the // wall clock to the start of that build slot (target_slot_start - S), matching how a pipelined // proposer is positioned when validating an inbound block proposal. With S - 2E = 0 in this config @@ -590,10 +581,9 @@ describe('ValidatorClient', () => { validatorClient.getProposalHandler().register(p2pClient, true); const signer = Secp256k1Signer.random(); - 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 }), @@ -619,7 +609,6 @@ describe('ValidatorClient', () => { signer, blockHeader: laterBlockHeader, indexWithinCheckpoint: IndexWithinCheckpoint(1), - inHash: emptyInHash, archiveRoot: Fr.random(), }); @@ -669,9 +658,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 +668,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 }), @@ -709,7 +697,6 @@ describe('ValidatorClient', () => { blockNumber, slotNumber: futureSlot, }), - inHash: computeInHashFromL1ToL2Messages([]), }); // Under pipelining, the target slot is the future slot the proposer is building for, built during @@ -748,10 +735,8 @@ describe('ValidatorClient', () => { it('should process block proposal from own validator key (HA peer)', async () => { const selfSigner = new Secp256k1Signer(Buffer32.fromString(validatorPrivateKeys[0])); - const emptyInHash = computeInHashFromL1ToL2Messages([]); const selfProposal = await makeBlockProposal({ blockHeader: proposal.blockHeader, - inHash: emptyInHash, archiveRoot: proposal.archive, txHashes: proposal.txHashes, signer: selfSigner, @@ -1461,10 +1446,6 @@ describe('ValidatorClient', () => { describe('non-first block in checkpoint validation', () => { // When indexWithinCheckpoint > 0, global variables must match parent block (except blockNumber). - // The inHash validation is implicitly handled: all blocks in a checkpoint share the same - // checkpointNumber, so they fetch the same L1-to-L2 messages and compute the same inHash. - // If a proposal has a different inHash, the existing validation (which computes inHash from - // L1 messages for the checkpoint) will catch it. it('should return false if global variables do not match parent for non-first block in checkpoint', async () => { // Create a proposal with indexWithinCheckpoint > 0 (non-first block in checkpoint) @@ -1486,8 +1467,6 @@ describe('ValidatorClient', () => { coinbase: EthAddress.random(), // Different from parent - should cause failure }); - // Use empty messages and compute the matching inHash - const emptyInHash = computeInHashFromL1ToL2Messages([]); const proposalBlockHeader = makeBlockHeader(1, { blockNumber: BlockNumber(parentBlockNumber + 1), slotNumber: SlotNumber(parentSlotNumber), @@ -1498,7 +1477,6 @@ describe('ValidatorClient', () => { const nonFirstBlockProposal = await makeBlockProposal({ blockHeader: proposalBlockHeader, indexWithinCheckpoint: IndexWithinCheckpoint(1), // Non-first block in checkpoint - inHash: emptyInHash, }); // Update epochCache mock for the new proposal @@ -1559,13 +1537,6 @@ describe('ValidatorClient', () => { const isValid = await validatorClient.validateBlockProposal(nonFirstBlockProposal, sender); expect(isValid).toBe(false); }); - - // Note: inHash validation for non-first blocks is implicitly handled by the existing - // validation that computes inHash from L1-to-L2 messages for the checkpoint. Since all - // blocks in the same checkpoint share the same checkpointNumber, they will always - // compute the same inHash from the same L1 messages. If a malicious proposal has a - // different inHash, it will fail the existing validation at lines 192-200 in - // proposal_handler.ts. }); it('should validate proposals in fisherman mode but not create or broadcast attestations', async () => { diff --git a/yarn-project/validator-client/src/validator.ts b/yarn-project/validator-client/src/validator.ts index 97f8e6f9114e..b3f93748de1f 100644 --- a/yarn-project/validator-client/src/validator.ts +++ b/yarn-project/validator-client/src/validator.ts @@ -478,7 +478,6 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter) 'invalid_proposal', 'state_mismatch', 'failed_txs', - 'in_hash_mismatch', 'parent_block_wrong_slot', ]; @@ -917,7 +916,6 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter) blockHeader: BlockHeader, checkpointNumber: CheckpointNumber, indexWithinCheckpoint: IndexWithinCheckpoint, - inHash: Fr, archive: Fr, txs: Tx[], proposerAddress: EthAddress | undefined, @@ -945,7 +943,6 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter) blockHeader, checkpointNumber, indexWithinCheckpoint, - inHash, archive, txs, proposerAddress, 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/native/native_world_state.ts b/yarn-project/world-state/src/native/native_world_state.ts index 432c70e566b2..2adde41e0bce 100644 --- a/yarn-project/world-state/src/native/native_world_state.ts +++ b/yarn-project/world-state/src/native/native_world_state.ts @@ -266,7 +266,6 @@ export class NativeWorldStateService implements MerkleTreeDatabase { public async handleL2BlockAndMessages(l2Block: L2Block, l1ToL2Messages: Fr[]): Promise { // Any block may carry an L1-to-L2 message bundle and transition the L1-to-L2 message tree by its real (unpadded, // compact) leaves, matching how the circuits build the tree. - const paddedL1ToL2Messages = l1ToL2Messages; // We have to pad the note hashes and nullifiers within tx effects because that's how the trees are built by // circuits. @@ -294,7 +293,7 @@ export class NativeWorldStateService implements MerkleTreeDatabase { // Forwarded so the native sync verifies the archive root against canonical and rejects a divergent tree. expectedArchiveRoot: l2Block.archive.root.toBuffer(), expectedPreviousArchiveRoot: l2Block.header.lastArchive.root.toBuffer(), - paddedL1ToL2Messages: paddedL1ToL2Messages.map(serializeLeaf), + paddedL1ToL2Messages: l1ToL2Messages.map(serializeLeaf), paddedNoteHashes: paddedNoteHashes.map(serializeLeaf), paddedNullifiers: paddedNullifiers.map(serializeLeaf), publicDataWrites: publicDataWrites.map(serializeLeaf), 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..6dd5f77baa9c 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 @@ -33,9 +33,8 @@ 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 - * exactly as given. Padding is a transitional concern of this method that moves entirely to the caller at the flip. + * tree, not just the first block of a checkpoint. The bundle's real (unpadded, compact) leaves are appended as + * given, matching how the circuits build the tree. * @param block - The L2 block to handle. * @param l1ToL2Messages - The L1 to L2 messages for the block. */