Skip to content

feat(l1): implement FCUv4 targetGasLimit (execution-apis#796)#6773

Merged
iovoid merged 9 commits into
mainfrom
feat/fcu-v4-target-gas-limit
Jun 29, 2026
Merged

feat(l1): implement FCUv4 targetGasLimit (execution-apis#796)#6773
iovoid merged 9 commits into
mainfrom
feat/fcu-v4-target-gas-limit

Conversation

@edg-l

@edg-l edg-l commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Adds target_gas_limit: u64 to PayloadAttributesV4 in types/fork_choice.rs. build_payload_v4 in engine/fork_choice.rs uses the CL-supplied value as BuildPayloadArgs.gas_ceil; the EIP-1559 1/1024 envelope in calc_gas_limit still clamps it relative to the parent.

Per execution-apis#796 the field is required. parse_v4 rejects a present-but-malformed V4 attributes object (e.g. one missing targetGasLimit) with InvalidPayloadAttributes rather than silently dropping it; an absent/null attributes parameter still yields no attributes (a plain forkchoice update). There is no --builder.gas-limit fallback: an Amsterdam CL that omits targetGasLimit is non-compliant and is rejected, keeping the EL aligned with #796 for upcoming devnets.

Also fixes a latent BuildPayloadArgs::id() collision: slot_number was not hashed and gas_ceil is now caller-supplied, so two FCUv4 calls differing only in targetGasLimit or slotNumber would produce the same payload_id. Both fields are now part of the Keccak input.

Tests cover: PayloadAttributesV4 serde (present parses; absent and explicit null are rejected); FCUv4 e2e accepts a present targetGasLimit, rejects an absent one with InvalidPayloadAttributes, and still rejects pre-Amsterdam timestamps; BuildPayloadArgs::id() distinguishes gas_ceil and slot_number; calc_gas_limit step-up, step-down, and clamp. First commit cherry-picked from ilitteri.

@github-actions github-actions Bot added the L1 Ethereum client label Jun 2, 2026
@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown

⚠️ Known Issues — intentionally skipped tests

Source: docs/known_issues.md

Known Issues

Tests intentionally excluded from CI. Source of truth for the Known
Issues
section the L1 workflow appends to each ef-tests job summary
and posts as a sticky PR comment.

EF Tests — Stateless coverage narrowed to EIP-8025 optional-proofs

make -C tooling/ef_tests/blockchain test calls test-stateless-zkevm
instead of test-stateless. The zkevm@v0.3.3 fixtures are filled against
bal@v5.6.1, out of sync with current bal spec; the broad target trips ~549
fixtures. Re-broaden once the zkevm bundle is regenerated.

Why and resolution path

PR #6527 broadened
test-stateless to extract the entire for_amsterdam/ tree from the
zkevm bundle and run all of it under --features stateless; combined with
this branch's bal-devnet-7 semantics that scope produces ~549
GasUsedMismatch / ReceiptsRootMismatch /
BlockAccessListHashMismatch failures.

test-stateless-zkevm filters cargo to the eip8025_optional_proofs
suite, which still validates the stateless harness without the bal-version
mismatch.

Re-broaden by switching test: back to test-stateless in
tooling/ef_tests/blockchain/Makefile once the zkevm bundle is regenerated
against the current bal spec.

@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown

Lines of code report

Total lines added: 8
Total lines removed: 0
Total lines changed: 8

Detailed view
+---------------------------------------------------+-------+------+
| File                                              | Lines | Diff |
+---------------------------------------------------+-------+------+
| ethrex/crates/blockchain/payload.rs               | 864   | +6   |
+---------------------------------------------------+-------+------+
| ethrex/crates/networking/rpc/types/fork_choice.rs | 57    | +2   |
+---------------------------------------------------+-------+------+

@edg-l edg-l force-pushed the feat/fcu-v4-target-gas-limit branch from e0799cf to d3c2476 Compare June 2, 2026 08:40
@edg-l edg-l marked this pull request as ready for review June 2, 2026 08:53
@edg-l edg-l requested a review from a team as a code owner June 2, 2026 08:53
@ethrex-project-sync ethrex-project-sync Bot moved this to In Review in ethrex_l1 Jun 2, 2026
@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown

🤖 Kimi Code Review

Overall this is a solid implementation of execution-apis#796 with good backward compatibility handling and comprehensive test coverage. A few minor points:

Arithmetic safety in calc_gas_limit (crates/blockchain/payload.rs)

The delta calculation assumes parent_gas_limit >= GAS_LIMIT_BOUND_DIVISOR:

let delta = parent_gas_limit / GAS_LIMIT_BOUND_DIVISOR - 1;

While Ethereum protocol constraints ensure gas limits are well above 1024 in practice, consider using saturating_sub for defense-in-depth:

let delta = parent_gas_limit / GAS_LIMIT_BOUND_DIVISOR.saturating_sub(1);

Similarly, limit + delta and limit - delta could use saturating arithmetic to prevent theoretical overflow/underflow, though the current logic is safe given realistic gas limit bounds.

Code clarity

The comment in validate_attributes_v4 (lines 536-541) clearly explains the optional field rationale. Consider adding a GitHub issue link or tracking number for the TODO to make it discoverable.

Tests

The new test coverage is excellent:

  • Property-based differentiation of payload IDs (lines 21-46 in payload_tests.rs)
  • Gas limit boundary behavior (lines 48-74)
  • FCU V4 deserialization and integration tests (lines 129-284 in fork_choice_tests.rs)

Security/Consensus

Correctly handles the consensus-critical requirement that payload IDs must be unique across different slot_number or gas_ceil values. The use of big-endian byte encoding in the hash preimage is consistent with existing fields.

The permissive deserialization of target_gas_limit with fallback to --builder.gas-limit is the right approach for devnet coordination, but ensure the follow-up TODO (making it mandatory) is tracked for mainnet readiness.

No blocking issues found.


Automated review by Kimi (Moonshot AI) · kimi-k2.5 · custom prompt

@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown

🤖 Codex Code Review

Findings:

  1. engine_forkchoiceUpdatedV4 still accepts spec-invalid Amsterdam payload attributes when targetGasLimit is missing or explicitly null, then silently builds with the EL-local --builder.gas-limit instead ([crates/networking/rpc/engine/fork_choice.rs](/home/runner/work/ethrex/ethrex/crates/networking/rpc/engine/fork_choice.rs:536), [crates/networking/rpc/engine/fork_choice.rs](/home/runner/work/ethrex/ethrex/crates/networking/rpc/engine/fork_choice.rs:568), [crates/networking/rpc/types/fork_choice.rs](/home/runner/work/ethrex/ethrex/crates/networking/rpc/types/fork_choice.rs:41)). That is an execution-apis#796 compliance break on every Amsterdam chain, not just the transitional devnets mentioned in comments, and it can hide EL/CL drift by producing a payload with a gas target the CL never requested. The new tests also lock this in, including the explicit-null case ([test/tests/rpc/fork_choice_tests.rs](/home/runner/work/ethrex/ethrex/test/tests/rpc/fork_choice_tests.rs:150), [test/tests/rpc/fork_choice_tests.rs](/home/runner/work/ethrex/ethrex/test/tests/rpc/fork_choice_tests.rs:165), [test/tests/rpc/fork_choice_tests.rs](/home/runner/work/ethrex/ethrex/test/tests/rpc/fork_choice_tests.rs:248)). I’d strongly prefer rejecting null/missing on Amsterdam, or at minimum gating the fallback behind an explicit devnet compatibility switch and distinguishing “omitted” from “present but null”.

The payload-id fix itself looks correct: hashing both slot_number and gas_ceil closes the wrong-build reuse bug, and the added gas-limit step tests are sensible.

I could not run the targeted Rust tests in this environment because cargo/rustup attempted to write under /home/runner/.rustup and failed with read-only file system, so this review is based on static inspection.


Automated review by OpenAI Codex · gpt-5.4 · custom prompt

@greptile-apps

greptile-apps Bot commented Jun 2, 2026

Copy link
Copy Markdown

Greptile Summary

This PR implements targetGasLimit support for engine_forkchoiceUpdatedV4 (execution-apis#796), allowing the CL to supply a desired gas limit that is forwarded through BuildPayloadArgs.gas_ceil into calc_gas_limit. The field is kept Option to remain compatible with pre-#796 CLs on Amsterdam devnets, with a fallback to --builder.gas-limit and a warn! log; both the validator and the builder include TODO markers to harden this to a hard error once #796 is universally shipped.

  • PayloadAttributesV4 gains target_gas_limit: Option<u64> with #[serde(default, with = \"hex_str_opt\")], correctly handling absent, explicit-null, and hex-string inputs.
  • BuildPayloadArgs::id() now hashes slot_number and gas_ceil to eliminate a latent collision where two FCUv4 calls differing only in targetGasLimit or slotNumber would produce the same payload_id; gas_ceil is currently added unconditionally for all versions, not just V4 (see inline comment).
  • Tests cover serde round-trips, id() uniqueness, calc_gas_limit step-up/step-down/clamp, and end-to-end FCUv4 behaviour (present, absent, and pre-Amsterdam rejection).

Confidence Score: 4/5

Safe to merge; the core FCUv4 gas-limit flow, serde handling, and ID-collision fix are all correct and well-tested.

The only notable concern is that gas_ceil is now hashed unconditionally in BuildPayloadArgs::id() for every payload version (V1–V4), not just V4 where the CL-supplied value can actually vary. For V1/V2/V3 the value is always the static --builder.gas-limit config, so there is no collision risk, but the ID formula for those versions quietly changes relative to before this PR.

crates/blockchain/payload.rs — the unconditional gas_ceil hashing change affects V1/V2/V3 payload ID output.

Important Files Changed

Filename Overview
crates/blockchain/payload.rs Adds slot_number and gas_ceil to the Keccak payload ID hash; removes a TODO comment from calc_gas_limit. The gas_ceil field is added unconditionally for all payload versions (V1–V4), changing ID output even for non-V4 payloads.
crates/networking/rpc/engine/fork_choice.rs Adds graceful fallback in build_payload_v4: CL-supplied target_gas_limit is preferred; absent field falls back to context.gas_ceil with a structured warn!. Comment in validate_attributes_v4 explains intentional leniency with a TODO for future hardening.
crates/networking/rpc/types/fork_choice.rs Adds target_gas_limit: Option to PayloadAttributesV4 with #[serde(default, with = serde_utils::u64::hex_str_opt)]; handles absent, null, and hex-string inputs correctly.
test/tests/blockchain/payload_tests.rs New unit tests cover BuildPayloadArgs::id() collision fix (distinct gas_ceil, distinct slot_number, stable on identical inputs) and calc_gas_limit step-up, step-down, and clamp behaviour.
test/tests/rpc/fork_choice_tests.rs New integration tests: serde round-trips for present/absent/null targetGasLimit; end-to-end FCUv4 accepting present and absent gas limit on Amsterdam genesis; rejection of pre-Amsterdam timestamp.

Sequence Diagram

sequenceDiagram
    participant CL as Consensus Layer
    participant RPC as engine_forkchoiceUpdatedV4
    participant V as validate_attributes_v4
    participant B as build_payload_v4
    participant P as BuildPayloadArgs id
    participant CP as create_payload

    CL->>RPC: FCUv4(forkChoiceState, PayloadAttributesV4)
    RPC->>V: validate_attributes_v4(attrs, head, ctx)
    V-->>V: check amsterdam_activated
    V-->>V: check withdrawals present
    V-->>V: check parent_beacon_block_root present
    V-->>RPC: Ok
    RPC->>B: build_payload_v4(attrs, ctx, fcs)

    alt targetGasLimit present
        B-->>B: "gas_ceil = attrs.target_gas_limit"
    else targetGasLimit absent
        B-->>B: warn and fallback to context.gas_ceil
    end

    B->>P: BuildPayloadArgs with slot_number and gas_ceil
    Note over P: hash includes slot_number and gas_ceil
    P-->>B: payload_id

    B->>CP: create_payload(args, storage, extra_data)
    CP-->>B: Block with calc_gas_limit applied

    B-->>RPC: payload_id
    RPC-->>CL: payloadStatus VALID plus payloadId
Loading
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
crates/blockchain/payload.rs:123-126
`gas_ceil` is added to the hash unconditionally, affecting payload IDs for V1/V2/V3 payloads even though those versions do not carry a CL-supplied gas limit. The collision fix is only needed for V4, where two FCUs may legitimately differ only in `targetGasLimit`. For V1/V2/V3, `gas_ceil` is always drawn from static config (`context.gas_ceil`), so there are no duplicates to disambiguate — and the change alters the ID formula for those versions relative to what existed before this PR. Scoping the `gas_ceil` hash update to V4 (i.e. when `slot_number.is_some()`) keeps the fix targeted and avoids silently invalidating any existing tooling or test fixtures that compute V1/V2/V3 payload IDs off-node.

```suggestion
        if let Some(slot_number) = self.slot_number {
            hasher.update(slot_number.to_be_bytes());
            // execution-apis#796: only include gas_ceil in the hash for V4
            // (slot_number is the V4 discriminator). V1/V2/V3 always use the
            // static --builder.gas-limit, so adding it there changes IDs
            // without preventing any collision.
            hasher.update(self.gas_ceil.to_be_bytes());
        }
```

Reviews (1): Last reviewed commit: "test(l1): move FCUv4 targetGasLimit test..." | Re-trigger Greptile

@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown

🤖 Claude Code Review

Now I have enough context for a thorough review.


Review: feat(l1): implement FCUv4 targetGasLimit (execution-apis#796)

Summary

The implementation is correct and the transitional Option<u64> strategy is a pragmatic choice for devnet compatibility. The latent payload_id collision fix is well-motivated and the test coverage is thorough. A few points below range from medium to low severity.


Medium

gas_ceil is now hashed unconditionally for all payload versions (V1–V4)

crates/blockchain/payload.rs, line 126:

hasher.update(self.gas_ceil.to_be_bytes());

This line runs for every call to id() regardless of self.version. For V1/V2/V3, gas_ceil comes from context.gas_ceil (the --builder.gas-limit CLI value) and was never previously part of the hash. The Engine API specs for V1–V3 define payload ID computation over the payload attributes only, which does not include server-side configuration like gas ceil.

In practice, payload IDs are ephemeral and the CL always uses the ID returned by the EL, never computing it independently — so this is unlikely to break production nodes. However it could break conformance test suites (e.g., Hive) that independently derive expected payload IDs for V1/V2/V3 calls. The fix is straightforward: guard by version or slot_number presence to preserve the existing computation for earlier versions:

// Only include gas_ceil in the hash for V4+ where it is caller-supplied.
if self.version >= 4 {
    hasher.update(self.gas_ceil.to_be_bytes());
}

The slot_number change is unambiguously correct since it is already guarded by if let Some(...).


Low

fcu_v4_request test helper builds JSON with string interpolation

test/tests/rpc/fork_choice_tests.rs, starting at the fcu_v4_request function:

let target_field = match target_gas_limit {
    Some(hex) => format!(",\n                    \"targetGasLimit\": \"{hex}\""),
    None => String::new(),
};
let body = format!(r#"{{ ... {target_field} ... }}"#);

The {hex} value is injected verbatim into the JSON string without escaping. If a caller passes something like 0x1"}},"malicious":"field, it would corrupt the JSON structure. This is a test utility so there is no runtime security risk, but the construction is fragile for future test authors. Using serde_json::json! and setting/removing a key on the resulting Value is more robust:

let mut attrs = serde_json::json!({
    "timestamp": format!("{timestamp:#x}"),
    // ... other fields ...
});
if let Some(hex) = target_gas_limit {
    attrs["targetGasLimit"] = serde_json::Value::String(hex.to_string());
}

No test for targetGasLimit: "0x0" edge case

If a CL sends targetGasLimit: "0x0", gas_ceil becomes 0 and calc_gas_limit computes desired_limit = max(0, MIN_GAS_LIMIT) = MIN_GAS_LIMIT. The code handles this correctly via the max(builder_gas_ceil, MIN_GAS_LIMIT) guard, but this edge case is not exercised in the new tests. A one-liner in payload_tests.rs would close the gap:

#[test]
fn gas_limit_respects_minimum_when_target_is_zero() {
    assert_eq!(calc_gas_limit(30_000_000, 0), 30_000_000 - (30_000_000 / GAS_LIMIT_BOUND_DIVISOR - 1));
    // or check it moves toward MIN_GAS_LIMIT
}

validate_attributes_v4 does not distinguish explicit null from absent targetGasLimit

With #[serde(default)] and hex_str_opt, both absent and null deserialize to None. The fallback path in build_payload_v4 treats both identically (warn + use context.gas_ceil). The spec for execution-apis#796 marks the field required, so an explicit null is arguably a malformed request rather than a legacy omission. For now this is acceptable under the devnet-compatibility rationale, but the existing TODO should be extended to note that enforcement should also cover the explicit-null case.


Positive

  • The hash fix for slot_number and gas_ceil on V4 correctly closes the collision window; the three ID-stability tests are exactly what's needed.
  • The calc_gas_limit step/clamp tests are correct and match the geth reference implementation's delta = parent / GAS_LIMIT_BOUND_DIVISOR - 1 formula.
  • The warn! log in the fallback path includes both slot and gas_ceil fields, which is good for diagnosing mixed CL/EL version deployments.
  • Serde roundtrip tests for present/absent/null cover all three meaningful deserialization states.
  • TODOs clearly mark the temporary compatibility shim and describe the exact change needed (InvalidPayloadAttributes) once Investigate when transactions should be removed from the mempool #796 is mandatory — no ambiguity left for the next developer.

Automated review by Claude (Anthropic) · sonnet · custom prompt

Comment thread crates/blockchain/payload.rs Outdated
Comment on lines +123 to +126
if let Some(slot_number) = self.slot_number {
hasher.update(slot_number.to_be_bytes());
}
hasher.update(self.gas_ceil.to_be_bytes());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 gas_ceil is added to the hash unconditionally, affecting payload IDs for V1/V2/V3 payloads even though those versions do not carry a CL-supplied gas limit. The collision fix is only needed for V4, where two FCUs may legitimately differ only in targetGasLimit. For V1/V2/V3, gas_ceil is always drawn from static config (context.gas_ceil), so there are no duplicates to disambiguate — and the change alters the ID formula for those versions relative to what existed before this PR. Scoping the gas_ceil hash update to V4 (i.e. when slot_number.is_some()) keeps the fix targeted and avoids silently invalidating any existing tooling or test fixtures that compute V1/V2/V3 payload IDs off-node.

Suggested change
if let Some(slot_number) = self.slot_number {
hasher.update(slot_number.to_be_bytes());
}
hasher.update(self.gas_ceil.to_be_bytes());
if let Some(slot_number) = self.slot_number {
hasher.update(slot_number.to_be_bytes());
// execution-apis#796: only include gas_ceil in the hash for V4
// (slot_number is the V4 discriminator). V1/V2/V3 always use the
// static --builder.gas-limit, so adding it there changes IDs
// without preventing any collision.
hasher.update(self.gas_ceil.to_be_bytes());
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/blockchain/payload.rs
Line: 123-126

Comment:
`gas_ceil` is added to the hash unconditionally, affecting payload IDs for V1/V2/V3 payloads even though those versions do not carry a CL-supplied gas limit. The collision fix is only needed for V4, where two FCUs may legitimately differ only in `targetGasLimit`. For V1/V2/V3, `gas_ceil` is always drawn from static config (`context.gas_ceil`), so there are no duplicates to disambiguate — and the change alters the ID formula for those versions relative to what existed before this PR. Scoping the `gas_ceil` hash update to V4 (i.e. when `slot_number.is_some()`) keeps the fix targeted and avoids silently invalidating any existing tooling or test fixtures that compute V1/V2/V3 payload IDs off-node.

```suggestion
        if let Some(slot_number) = self.slot_number {
            hasher.update(slot_number.to_be_bytes());
            // execution-apis#796: only include gas_ceil in the hash for V4
            // (slot_number is the V4 discriminator). V1/V2/V3 always use the
            // static --builder.gas-limit, so adding it there changes IDs
            // without preventing any collision.
            hasher.update(self.gas_ceil.to_be_bytes());
        }
```

How can I resolve this? If you propose a fix, please make it concise.

azteca1998
azteca1998 previously approved these changes Jun 2, 2026

@ElFantasma ElFantasma 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.

Just one non-blocking observation

hasher.update(slot_number.to_be_bytes());
}
if self.version >= 4 {
hasher.update(self.gas_ceil.to_be_bytes());

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.

Asymmetry worth pinning down: gas_ceil hashing is explicitly gated on self.version >= 4, but slot_number hashing is gated implicitly via if let Some(slot_number). Today the two are equivalent because every V1-V3 construction site in fork_choice.rs sets slot_number: None (verified at line 461) — only the V4 path passes Some(attributes.slot_number) (line 563). So V1-V3 payload IDs are unchanged in practice.

But the contract is a soft invariant. If a future V5/V6/V7 path keeps setting slot_number: Some(_) (which is correct) AND someone adds slot to a V3 fork-choice call site by mistake (e.g. via a helper that defaults it), the V3 ID would silently start including the slot in the hash, breaking ID stability for any caller cached against the old IDs.

Two cheap lock-ins:

  1. Symmetric explicit gate: if self.version >= 4 && let Some(slot_number) = self.slot_number { ... } so both V4+-only inputs are gated the same way and the version is the single source of truth.
  2. Or add a regression test that V1-V3 IDs stay stable when slot_number is None (your existing payload_id_stable_when_inputs_match uses version=4; a parallel one for version=3 with slot_number=None would lock it in).

(1) is one extra &&; (2) is ~10 lines of test. Either works. Non-blocking — current code is correct.

@edg-l edg-l enabled auto-merge June 9, 2026 09:31
@edg-l edg-l disabled auto-merge June 9, 2026 09:32
@edg-l edg-l force-pushed the feat/fcu-v4-target-gas-limit branch from 257a31a to 8c72b36 Compare June 9, 2026 09:36
ilitteri and others added 6 commits June 9, 2026 16:44
…96) for glamsterdam-devnet-4

Extends PayloadAttributesV4 with the new required target_gas_limit field
introduced by execution-apis#796 and consensus-specs#5235 (Gloas /
upstream "Glamsterdam"). The CL is now the authoritative source of the
per-block target gas limit on engine_forkchoiceUpdatedV4 instead of the
EL reading it from the static --builder.gas-limit flag.

validate_attributes_v4 strictly rejects FCUv4 requests that omit the
field with RpcErr::InvalidPayloadAttributes, matching the spec contract.
The paired CL on glamsterdam-devnet-4 ships consensus-specs#5235 and
populates the field on every FCUv4 it sends.

build_payload_v4 sets BuildPayloadArgs.gas_ceil directly from the
attribute. The EIP-1559 1/1024 gas-limit envelope inside calc_gas_limit
continues to clamp the value relative to the parent block. V1/V2/V3 and
non-engine callers (sequencer, dev mode) keep using --builder.gas-limit.

Also fixes a latent collision in BuildPayloadArgs::id(): slot_number was
never hashed and gas_ceil now matters per-call, so two FCUv4s differing
only in CL-supplied targetGasLimit could collide on payload_id. Both
fields are now part of the Keccak input.

Tests added:
- payload-attribute parser round-trips for hex value present, field
  absent, and explicit null
- validator accepts present, rejects absent (matches spec error
  message), and continues to reject pre-Amsterdam timestamps
- BuildPayloadArgs::id() distinguishes different gas_ceil values and
  different slot_number values, and is stable under equal inputs
- calc_gas_limit steps up/down toward target within one EIP-1559 1/1024
  step and clamps when the target is within one step
execution-apis#796 makes targetGasLimit a required field on
engine_forkchoiceUpdatedV4, but some Amsterdam devnets still run
pre-#796 CLs that omit it. Relax validate_attributes_v4 to accept an
absent field and fall back to --builder.gas-limit in build_payload_v4,
warning so the config drift stays visible. Keeps EL rollout decoupled
from CL rollout.
Move the payload-id / calc_gas_limit unit tests to
test/tests/blockchain/payload_tests.rs and the PayloadAttributesV4
serde tests to test/tests/rpc/fork_choice_tests.rs (all public API).

Rewrite the validate_attributes_v4 unit tests as end-to-end
ForkChoiceUpdatedV4 handler tests driven through the public RPC entry
point, so the private validator is no longer exposed for testing:
accepts a present targetGasLimit, accepts an absent one (fallback), and
rejects pre-Amsterdam timestamps.
Make target_gas_limit a required u64 on PayloadAttributesV4 (was Option
with a --builder.gas-limit fallback). parse_v4 now rejects present-but-
malformed V4 attributes (e.g. missing targetGasLimit) with
InvalidPayloadAttributes instead of silently dropping them; build_payload_v4
uses the CL value directly. Drops the fallback + warn and the optionality
that diverged from #796.
@edg-l edg-l force-pushed the feat/fcu-v4-target-gas-limit branch from 8c72b36 to ded8b3e Compare June 9, 2026 14:44
edg-l added 2 commits June 16, 2026 17:56
…s-limit

# Conflicts:
#	test/tests/rpc/fork_choice_tests.rs
…s-limit

# Conflicts:
#	test/tests/rpc/fork_choice_tests.rs
@edg-l edg-l dismissed azteca1998’s stale review June 29, 2026 09:35

no longer in team

@iovoid iovoid added this pull request to the merge queue Jun 29, 2026
Merged via the queue into main with commit 834733e Jun 29, 2026
55 checks passed
@iovoid iovoid deleted the feat/fcu-v4-target-gas-limit branch June 29, 2026 13:23
@github-project-automation github-project-automation Bot moved this from In Review to Done in ethrex_l1 Jun 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Glamsterdam L1 Ethereum client

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

5 participants