Skip to content

feat: add extra getters - #22563

Merged
LHerskind merged 1 commit into
merge-train/spartanfrom
lh/tmnt-517-add-extra-getters
Apr 29, 2026
Merged

LHerskind merged 1 commit into
merge-train/spartanfrom
lh/tmnt-517-add-extra-getters

Conversation

@LHerskind

Copy link
Copy Markdown
Contributor

Adds external getters for the last three RollupConfig fields that were previously unreachable from outside the contract:

  • getVkTreeRoot()
  • getProtocolContractsHash()
  • getEpochProofVerifier()

These were omitted originally because the rollup was tight on bytecode. Recent size reductions (TMNT-509, TMNT-513, TMNT-514) freed enough room to add them directly in Rollup.sol without needing ExtLib delegation.

@LHerskind LHerskind changed the title Lh/tmnt 517 add extra getters feat: add extra getters Apr 15, 2026
@LHerskind
LHerskind force-pushed the lh/tmnt-520-make-it-stupid-simple-to-check-cross-chain-replay-for branch from fb34548 to 5a02d69 Compare April 15, 2026 12:39
@LHerskind
LHerskind force-pushed the lh/tmnt-517-add-extra-getters branch from 66d8749 to 0eb7829 Compare April 15, 2026 12:40
@LHerskind LHerskind added the ci-draft Run CI on draft PRs. label Apr 15, 2026
@LHerskind
LHerskind force-pushed the lh/tmnt-520-make-it-stupid-simple-to-check-cross-chain-replay-for branch 3 times, most recently from 7003b54 to 7256238 Compare April 16, 2026 18:21
@LHerskind
LHerskind force-pushed the lh/tmnt-520-make-it-stupid-simple-to-check-cross-chain-replay-for branch 2 times, most recently from 41f782d to 63e0b5c Compare April 28, 2026 11:59
Base automatically changed from lh/tmnt-520-make-it-stupid-simple-to-check-cross-chain-replay-for to merge-train/spartan April 28, 2026 12:32
@LHerskind
LHerskind force-pushed the lh/tmnt-517-add-extra-getters branch from 0eb7829 to 0697a9c Compare April 28, 2026 12:53
@LHerskind
LHerskind marked this pull request as ready for review April 29, 2026 11:24

@Maddiaa0 Maddiaa0 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

golden

@LHerskind
LHerskind merged commit edd9dd7 into merge-train/spartan Apr 29, 2026
18 checks passed
@LHerskind
LHerskind deleted the lh/tmnt-517-add-extra-getters branch April 29, 2026 12:29
ElusAegis pushed a commit to ElusAegis/aztec-packages that referenced this pull request Apr 30, 2026
BEGIN_COMMIT_OVERRIDE
chore: pass through p2p tx pool size (AztecProtocol#22804)
feat: track sequencer state time (AztecProtocol#22800)
feat(rpc)!: updated rpc api for blocks and checkpoints (AztecProtocol#22781)
feat(pipelining): complete attestations by build-slot end (AztecProtocol#22735)
feat(archiver): handle multiple proposed checkpoints (AztecProtocol#22784)
feat: scrape bench-10tps (AztecProtocol#22803)
feat!: make proposals EIP-712 (AztecProtocol#22531)
chore: remove default env (AztecProtocol#22837)
chore: remove default env (AztecProtocol#22839)
chore: grafana provisioning (AztecProtocol#22843)
feat: add extra getters (AztecProtocol#22563)
fix: use number config helper for NaN protection (AztecProtocol#22598)
test(e2e): relax blocks per checkpoint in high tps e2e (AztecProtocol#22846)
test(e2e): equivocation recovery under proposer pipelining (AztecProtocol#22831)
feat: scrape metrics data (AztecProtocol#22840)
END_COMMIT_OVERRIDE
spalladino added a commit that referenced this pull request Sep 1, 2026
…ir getters (#25313)

`RollupContract.getVkTreeRoot` and `getProtocolContractsHash` read raw
storage at `keccak256("aztec.stf.storage") + 3` and `+ 4` instead of
calling the contract.

`IRollup` has exposed `getVkTreeRoot()` and `getProtocolContractsHash()`
since #22563, so the slot arithmetic was never necessary. This switches
both to the ABI getters.

### Why it matters

The slot offsets hardcode the field order of `RollupStore.config`.
Reordering or removing a field there does not break the read — it
returns a wrong-but-well-formed `bytes32`, which surfaces downstream as
a spurious VK mismatch in `checkRollupCompatibility` rather than as an
error at the read site.

That is not hypothetical: a change on the `next` line moves these values
out of `RollupStore` entirely (they are constructor-only and have no
setter, so they belong in immutables). A node still doing the raw read
would see zeroes against such a rollup and sit in standby forever.
Landing the getter read on the v5 line first means node binaries cut
from it work against a rollup deployed from either version.

### Scope

Two functions in `yarn-project/ethereum/src/contracts/rollup.ts`, plus
two test descriptions that said "from storage". The remaining
`stfStorageSlot` consumers use offsets `+0` (tips), `+1` (archives) and
`+2` (tempCheckpointLogs) and are untouched.

The existing tests in `rollup.test.ts` cover both getters against a real
anvil deployment, so they exercise the new path unchanged.
spalladino added a commit that referenced this pull request Sep 4, 2026
…5314)

This is the bottom of the Fast Inbox stack: it targets
`project/fast-inbox` directly and is independently mergeable — nothing
in it depends on the inbox work above it.

## Context

Every field of `RollupConfig` — `vkTreeRoot`, `protocolContractsHash`,
`version`, `feeAsset`, `feeAssetPortal`, `epochProofVerifier`, `inbox`,
`outbox` — is written exactly once in the Rollup's constructor and has
no setter anywhere in `src/`. They were nonetheless kept in storage, so
every read paid a cold `SLOAD`. `propose` paid one,
`submitEpochRootProof` paid six.

## Approach

Move all eight into immutables and drop `config` from `RollupStore`.

Libraries cannot read a contract's immutables, and the `*ExtLib`
libraries here are `external` (delegatecalled), so they cannot either.
The values are assembled into a memory `RollupConfig` by
`RollupCore._getRollupConfig()` and threaded down as parameters: the
full struct into the epoch-proof path, the `IInbox` into propose, and
the fee asset into the reward claims. Propose needed its `IInbox`
bundled with the existing `checkBlob` flag into a `ProposeConfig` struct
— a seventh scalar parameter pushed `ProposeLib.propose` over the stack
limit, and a memory struct costs one slot instead of two.

`config` was the last member of `RollupStore`, so `tips`, `archives` and
`tempCheckpointLogs` keep slots 0–2 and every raw-slot consumer of
`keccak256("aztec.stf.storage")` is unaffected — all of them use offsets
≤ +2. The two that used `+3`/`+4` were `RollupContract.getVkTreeRoot`
and `getProtocolContractsHash` in the TS client, which now call the
contract getters that `IRollup` has exposed since #22563.

The second commit is bytecode budget, not gas. Each immutable read
inlines a 32-byte push, which grew `Rollup`'s runtime code to within 148
bytes of the EIP-170 limit. `Rollup.validateHeaderWithAttestations` was
decoding seven parameters, building a `ValidateHeaderArgs` (which embeds
a full `ProposedHeader`) in memory, resolving the mana min fee through
two delegatecall hops, and re-encoding four arguments — all in the
Rollup's own runtime code. Forwarding the parameters straight through
and assembling the struct in `RollupOperationsExtLib` frees 629 bytes.
The fee value is unchanged: `RewardExtLib.summedMinFee` and
`.getManaMinFeeComponentsAt` are one-line forwarders to the `FeeLib` /
`ProposeLib` functions the ExtLib now calls directly.

## Gas

| Benchmark | Before | After | Δ |
|---|---|---|---|
| `propose` (no validators) | 199,366 | 197,433 | **−1,933** |
| `submitEpochRootProof` (no validators) | 991,032 | 980,225 |
**−10,807** |
| `propose` (100 validators) | 327,774 | 325,847 | **−1,927** |
| `submitEpochRootProof` (100 validators) | 1,572,081 | 1,561,291 |
**−10,790** |
| `aggregate3` (100 validators) | 376,665 | 374,738 | **−1,927** |

The config getters lose their cold `SLOAD` outright — `getInbox` 2,543 →
878, `getVersion` 1,447 → 852, `getOutbox` 2,521 → 856. A handful of
unrelated views move by 22–44 gas as the Rollup's selector dispatch
shifts. Deployment also drops eight `SSTORE`s.

`Rollup` runtime bytecode ends at 23,799 against the 24,576 limit — 777
bytes of margin, against 886 before this change.

Note on the regenerated `gas_report.json`: call counts on a few entries
drop by 36 because the three tests that fail only under
`FORGE_GAS_REPORT` (`testExtraBlobs`, `testRevertInvalidCoinbase`,
`testRevertInvalidTimestamp` — all failing identically on the base
commit) abort at a slightly different point. Per-call gas for those
entries is unchanged.

## Node compatibility

Node binaries already in the field read `vkTreeRoot` and
`protocolContractsHash` from raw slots `+3`/`+4`. Against a rollup
deployed from this branch those slots are zero, so such a node's
`waitForCompatibleRollup` reports a VK mismatch and sits in standby.
#25313 lands the getter-based read on the v5 line so binaries cut from
it work against a rollup deployed from either version; it does nothing
for binaries already released, so this needs sequencing against any node
rollout.
spalladino added a commit that referenced this pull request Sep 10, 2026
…5314)

This is the bottom of the Fast Inbox stack: it targets
`project/fast-inbox` directly and is independently mergeable — nothing
in it depends on the inbox work above it.

Every field of `RollupConfig` — `vkTreeRoot`, `protocolContractsHash`,
`version`, `feeAsset`, `feeAssetPortal`, `epochProofVerifier`, `inbox`,
`outbox` — is written exactly once in the Rollup's constructor and has
no setter anywhere in `src/`. They were nonetheless kept in storage, so
every read paid a cold `SLOAD`. `propose` paid one,
`submitEpochRootProof` paid six.

Move all eight into immutables and drop `config` from `RollupStore`.

Libraries cannot read a contract's immutables, and the `*ExtLib`
libraries here are `external` (delegatecalled), so they cannot either.
The values are assembled into a memory `RollupConfig` by
`RollupCore._getRollupConfig()` and threaded down as parameters: the
full struct into the epoch-proof path, the `IInbox` into propose, and
the fee asset into the reward claims. Propose needed its `IInbox`
bundled with the existing `checkBlob` flag into a `ProposeConfig` struct
— a seventh scalar parameter pushed `ProposeLib.propose` over the stack
limit, and a memory struct costs one slot instead of two.

`config` was the last member of `RollupStore`, so `tips`, `archives` and
`tempCheckpointLogs` keep slots 0–2 and every raw-slot consumer of
`keccak256("aztec.stf.storage")` is unaffected — all of them use offsets
≤ +2. The two that used `+3`/`+4` were `RollupContract.getVkTreeRoot`
and `getProtocolContractsHash` in the TS client, which now call the
contract getters that `IRollup` has exposed since #22563.

The second commit is bytecode budget, not gas. Each immutable read
inlines a 32-byte push, which grew `Rollup`'s runtime code to within 148
bytes of the EIP-170 limit. `Rollup.validateHeaderWithAttestations` was
decoding seven parameters, building a `ValidateHeaderArgs` (which embeds
a full `ProposedHeader`) in memory, resolving the mana min fee through
two delegatecall hops, and re-encoding four arguments — all in the
Rollup's own runtime code. Forwarding the parameters straight through
and assembling the struct in `RollupOperationsExtLib` frees 629 bytes.
The fee value is unchanged: `RewardExtLib.summedMinFee` and
`.getManaMinFeeComponentsAt` are one-line forwarders to the `FeeLib` /
`ProposeLib` functions the ExtLib now calls directly.

| Benchmark | Before | After | Δ |
|---|---|---|---|
| `propose` (no validators) | 199,366 | 197,433 | **−1,933** |
| `submitEpochRootProof` (no validators) | 991,032 | 980,225 |
**−10,807** |
| `propose` (100 validators) | 327,774 | 325,847 | **−1,927** |
| `submitEpochRootProof` (100 validators) | 1,572,081 | 1,561,291 |
**−10,790** |
| `aggregate3` (100 validators) | 376,665 | 374,738 | **−1,927** |

The config getters lose their cold `SLOAD` outright — `getInbox` 2,543 →
878, `getVersion` 1,447 → 852, `getOutbox` 2,521 → 856. A handful of
unrelated views move by 22–44 gas as the Rollup's selector dispatch
shifts. Deployment also drops eight `SSTORE`s.

`Rollup` runtime bytecode ends at 23,799 against the 24,576 limit — 777
bytes of margin, against 886 before this change.

Note on the regenerated `gas_report.json`: call counts on a few entries
drop by 36 because the three tests that fail only under
`FORGE_GAS_REPORT` (`testExtraBlobs`, `testRevertInvalidCoinbase`,
`testRevertInvalidTimestamp` — all failing identically on the base
commit) abort at a slightly different point. Per-call gas for those
entries is unchanged.

Node binaries already in the field read `vkTreeRoot` and
`protocolContractsHash` from raw slots `+3`/`+4`. Against a rollup
deployed from this branch those slots are zero, so such a node's
`waitForCompatibleRollup` reports a VK mismatch and sits in standby.
it work against a rollup deployed from either version; it does nothing
for binaries already released, so this needs sequencing against any node
rollout.
spalladino added a commit that referenced this pull request Sep 10, 2026
…5314)

This is the bottom of the Fast Inbox stack: it targets
`project/fast-inbox` directly and is independently mergeable — nothing
in it depends on the inbox work above it.

Every field of `RollupConfig` — `vkTreeRoot`, `protocolContractsHash`,
`version`, `feeAsset`, `feeAssetPortal`, `epochProofVerifier`, `inbox`,
`outbox` — is written exactly once in the Rollup's constructor and has
no setter anywhere in `src/`. They were nonetheless kept in storage, so
every read paid a cold `SLOAD`. `propose` paid one,
`submitEpochRootProof` paid six.

Move all eight into immutables and drop `config` from `RollupStore`.

Libraries cannot read a contract's immutables, and the `*ExtLib`
libraries here are `external` (delegatecalled), so they cannot either.
The values are assembled into a memory `RollupConfig` by
`RollupCore._getRollupConfig()` and threaded down as parameters: the
full struct into the epoch-proof path, the `IInbox` into propose, and
the fee asset into the reward claims. Propose needed its `IInbox`
bundled with the existing `checkBlob` flag into a `ProposeConfig` struct
— a seventh scalar parameter pushed `ProposeLib.propose` over the stack
limit, and a memory struct costs one slot instead of two.

`config` was the last member of `RollupStore`, so `tips`, `archives` and
`tempCheckpointLogs` keep slots 0–2 and every raw-slot consumer of
`keccak256("aztec.stf.storage")` is unaffected — all of them use offsets
≤ +2. The two that used `+3`/`+4` were `RollupContract.getVkTreeRoot`
and `getProtocolContractsHash` in the TS client, which now call the
contract getters that `IRollup` has exposed since #22563.

The second commit is bytecode budget, not gas. Each immutable read
inlines a 32-byte push, which grew `Rollup`'s runtime code to within 148
bytes of the EIP-170 limit. `Rollup.validateHeaderWithAttestations` was
decoding seven parameters, building a `ValidateHeaderArgs` (which embeds
a full `ProposedHeader`) in memory, resolving the mana min fee through
two delegatecall hops, and re-encoding four arguments — all in the
Rollup's own runtime code. Forwarding the parameters straight through
and assembling the struct in `RollupOperationsExtLib` frees 629 bytes.
The fee value is unchanged: `RewardExtLib.summedMinFee` and
`.getManaMinFeeComponentsAt` are one-line forwarders to the `FeeLib` /
`ProposeLib` functions the ExtLib now calls directly.

| Benchmark | Before | After | Δ |
|---|---|---|---|
| `propose` (no validators) | 199,366 | 197,433 | **−1,933** |
| `submitEpochRootProof` (no validators) | 991,032 | 980,225 |
**−10,807** |
| `propose` (100 validators) | 327,774 | 325,847 | **−1,927** |
| `submitEpochRootProof` (100 validators) | 1,572,081 | 1,561,291 |
**−10,790** |
| `aggregate3` (100 validators) | 376,665 | 374,738 | **−1,927** |

The config getters lose their cold `SLOAD` outright — `getInbox` 2,543 →
878, `getVersion` 1,447 → 852, `getOutbox` 2,521 → 856. A handful of
unrelated views move by 22–44 gas as the Rollup's selector dispatch
shifts. Deployment also drops eight `SSTORE`s.

`Rollup` runtime bytecode ends at 23,799 against the 24,576 limit — 777
bytes of margin, against 886 before this change.

Note on the regenerated `gas_report.json`: call counts on a few entries
drop by 36 because the three tests that fail only under
`FORGE_GAS_REPORT` (`testExtraBlobs`, `testRevertInvalidCoinbase`,
`testRevertInvalidTimestamp` — all failing identically on the base
commit) abort at a slightly different point. Per-call gas for those
entries is unchanged.

Node binaries already in the field read `vkTreeRoot` and
`protocolContractsHash` from raw slots `+3`/`+4`. Against a rollup
deployed from this branch those slots are zero, so such a node's
`waitForCompatibleRollup` reports a VK mismatch and sits in standby.
it work against a rollup deployed from either version; it does nothing
for binaries already released, so this needs sequencing against any node
rollout.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci-draft Run CI on draft PRs.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants