Skip to content

fix: isExecutionEnabled accepts block object#379

Draft
spiral-ladder wants to merge 3 commits into
mainfrom
bing/is-ex-enabled
Draft

fix: isExecutionEnabled accepts block object#379
spiral-ladder wants to merge 3 commits into
mainfrom
bing/is-ex-enabled

Conversation

@spiral-ladder

Copy link
Copy Markdown
Collaborator

IBeaconStateView (and all instances of calling isExecutionEnabled in lodestar) deals with the block object, so lets do the same.

@spiral-ladder spiral-ladder self-assigned this May 26, 2026
@spiral-ladder spiral-ladder requested a review from a team as a code owner May 26, 2026 13:29
`IBeaconStateView` (and all instances of calling `isExecutionEnabled` in
lodestar) deals with the block object, so lets do the same.
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request refactors the isExecutionEnabled method in BeaconStateView to accept a block object directly, matching the pattern used elsewhere in the Lodestar codebase. The changes improve efficiency by short-circuiting post-merge operations and introduce robust handling for pre-merge Bellatrix blocks by walking the JS block object to check for non-default execution payloads.

Highlights

  • API Update: Updated isExecutionEnabled to accept a block object instead of separate fork and block bytes, aligning with Lodestar's internal block representation.
  • Performance Optimization: Implemented short-circuit logic for post-merge states, avoiding unnecessary block inspection when execution is already enabled.
  • Type Safety: Added BeaconBlockLike interface in TypeScript to support the new block object parameter and improved native JS-to-Zig payload conversion.
New Features

🧠 You can now enable Memory (public preview) to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot 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.

Code Review

This pull request refactors the isExecutionEnabled method in BeaconStateView.zig to accept a Lodestar-shaped block object instead of raw bytes, short-circuiting early on post-merge states without parsing the block. It also introduces a native parser executionPayloadFromJs to reconstruct the Bellatrix execution payload from JS objects. The code review identified critical issues: a potential silent truncation of bigints larger than 256 bits in readBigintU256, a missing implementation for the newly declared isMergeTransitionBlock method in the TypeScript bindings, and a violation of the 100-column line limit rule.

Comment on lines +1116 to +1134
fn readBigintU256(value: napi.Value) !u256 {
var sign_bit: c_int = 0;
var word_count: usize = 4;
var words: [4]u64 = .{ 0, 0, 0, 0 };
try napi.status.check(napi.c.napi_get_value_bigint_words(
value.env,
value.value,
&sign_bit,
&word_count,
&words,
));
if (sign_bit != 0) return error.NegativeBigint;
var result: u256 = 0;
var i: usize = 0;
while (i < @min(word_count, 4)) : (i += 1) {
result |= @as(u256, words[i]) << @intCast(i * 64);
}
return result;
}

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.

high

In readBigintU256, if the JS bigint is larger than 256 bits, napi_get_value_bigint_words will write the first 4 words and update word_count to the total number of words required (which will be greater than 4).

Because the loop condition uses @min(word_count, 4), any words beyond the 4th will be silently ignored, resulting in silent truncation of the bigint. To prevent silent truncation and potential consensus/correctness issues, we should explicitly check if word_count > 4 and return an error (e.g., error.BigintOverflow).

fn readBigintU256(value: napi.Value) !u256 {
    var sign_bit: c_int = 0;
    var word_count: usize = 4;
    var words: [4]u64 = .{ 0, 0, 0, 0 };
    try napi.status.check(napi.c.napi_get_value_bigint_words(
        value.env,
        value.value,
        &sign_bit,
        &word_count,
        &words,
    ));
    if (sign_bit != 0) return error.NegativeBigint;
    if (word_count > 4) return error.BigintOverflow;
    var result: u256 = 0;
    var i: usize = 0;
    while (i < word_count) : (i += 1) {
        result |= @as(u256, words[i]) << @intCast(i * 64);
    }
    return result;
}

Comment thread bindings/src/index.d.ts
Comment on lines +214 to +215
/** True iff state is pre-merge AND the given block carries a non-default execution payload. Bellatrix-only. */
isMergeTransitionBlock(signedBlockBytes: Uint8Array): boolean;

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.

high

The method isMergeTransitionBlock is declared in the BeaconStateView class interface, but it is not implemented or exported in BeaconStateView.zig. Calling this method at runtime will result in a TypeError. Please implement this method in BeaconStateView.zig or remove it from the type definitions if it is not intended to be exposed.

Comment on lines +622 to +625
const merge_complete: bool = switch (fork_seq) {
inline .bellatrix, .capella, .deneb, .electra, .fulu => |f| st.isMergeTransitionComplete(f, cached_state.state.castToFork(f)),
else => unreachable,
};

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.

medium

This line exceeds the 100-column limit specified in the Repository Style Guide (Rule 400). Please wrap the switch arm to keep it under 100 columns.

    const merge_complete: bool = switch (fork_seq) {
        inline .bellatrix, .capella, .deneb, .electra, .fulu => |f| st.isMergeTransitionComplete(
            f,
            cached_state.state.castToFork(f),
        ),
        else => unreachable,
    };
References
  1. Rule 400: Hard limit all line lengths, without exception, to at most 100 columns. (link)

/// Read a JS bigint into u256.
///
/// Throws on negative values; we never store signed u256 in consensus types.
fn readBigintU256(value: napi.Value) !u256 {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

this has to be fixed on the zapi level first since casting a u1 to *c_int is UB due to alignment

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