Skip to content

Build and submit a Gloas/Amsterdam block - #575

Open
0w3n-d wants to merge 1 commit into
od/gloas-sim-amsterdam-step3from
od/gloas-build-amsterdam-step4
Open

Build and submit a Gloas/Amsterdam block#575
0w3n-d wants to merge 1 commit into
od/gloas-sim-amsterdam-step3from
od/gloas-build-amsterdam-step4

Conversation

@0w3n-d

@0w3n-d 0w3n-d commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

Issue: #561 (step 4 of 6)

What this PR does

The building role now produces Amsterdam blocks and submits them in the Gloas
shape. Two fork decisions drive it, and they are independent:

  • Block shape comes from the EL: is_amsterdam_activated(slot.timestamp)
    sets the header's EIP-7843 slot number and keeps the EIP-7928 list ethrex
    already records but which BuiltBlock was discarding.
  • Submission shape comes from the CL: chain_info.fork_at_slot(slot)
    selects SignedBidSubmissionGloas. The relay decodes by the fork its clock
    reports, so the builder reads the same spec or its bytes are undecodable.

When the two disagree the submission is refused rather than sent. An Amsterdam
block in a Fulu shape silently drops the list; the simulator then rebuilds a
header whose hash is not the one signed, and every bid dies as a hash mismatch
with nothing to point at.

The finding that made this more than plumbing

Under Amsterdam the trailing payout needs 204600 gas, not 21000, whenever the
proposer's fee recipient does not yet exist on chain.
EIP-8037 charges state
gas for the account the payment creates: 120 state bytes at 1530 each = 183600,
on top of the transfer's 21000.

With the old fixed reserve every such block was lost — PayoutReverted, from an
out-of-gas that consumed the whole limit. On a fresh testnet fee recipients
generally don't exist yet, so this would have broken the builder on the exact
network step 4 exists to enable, and the error message pointed at the payout
rather than at gas.

The reserve is now sized from state: payout_gas_reserve covers the transfer,
and the creation charge is added only when the recipient is absent. It is read
twice — before the fill to size the reserve, and after it for the transaction —
so a recipient created earlier in the same block is not charged for twice. The
constants come from ethrex-levm rather than being hardcoded, so they cannot
drift from the rules the simulator enforces when the ethrex pin moves.

A related property worth knowing, which this PR does not change: under Amsterdam
an ordinary 21000-gas transfer to a fresh address also fails. That is EIP-8037
applying to all traffic, not something the builder can fix; such transactions
are included with failed receipts, which is correct.

What this PR deliberately does not do

  • The proposer is still paid in-block, as you decided. ePBS's own payment
    path is untouched.
  • No merging. The merged route still refuses Gloas; that is step 5.
  • No binary search on the payout gas. A contract fee recipient needing more
    than the reserve still fails and the slot is skipped, as before. rbuilder
    searches for this; it stays a stage-2 item.
  • The reserve is not lowered after the first payment. It does not need to
    be: the in-block read already reports the account as existing from the next
    block on.

Tests

Tests were written first, as in every earlier step. 15 new tests; 414 pass in
total (399 before).

The end-to-end pair is the one that matters: build a block, sign it the way the
relay will receive it, and validate it with our own step 3 simulation role,
asserting 200. I checked it is not vacuous by flipping one byte of the list —
the test then fails with the expected block hash mismatch. Both forks are
covered.

Also covered: an Amsterdam block carries both header fields and the exact bytes
the header hashed; a pre-Amsterdam block carries neither, guarding against
changing every Fulu block hash; the slot number is the proposal slot, not the
block number; the in-block payout is unchanged; mempool transactions still fit
alongside the larger reserve; the three payout-gas cases as a unit; a first
payment to an absent recipient really succeeds; a recipient created earlier in
the block needs no extra reserve; the Gloas submission decodes through the
relay's own decoder; the Fulu shape is unchanged; both fork-mismatch directions
are refused by name; and the bid trace's slot matches the header's, which is the
contract step 3 relies on.

Reviewer checklist

  • CI (lint, unit-test) is green
  • Matches the linked issue/step
  • No unexplained scope creep or unrelated files touched

Set the header's slot number from the proposal slot and carry the block
access list ethrex records, then submit it in the Gloas shape. Size the
payout reserve for EIP-8037's account-creation state gas, without which
a fee recipient's first payment runs out of gas.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant