Skip to content

feat(ethrex): Use EIP-8025 feature to delegate as much logic into their guest program#39

Merged
jsign merged 14 commits into
eth-act:mainfrom
jsign:jsign-ethrex-8025
Apr 27, 2026
Merged

feat(ethrex): Use EIP-8025 feature to delegate as much logic into their guest program#39
jsign merged 14 commits into
eth-act:mainfrom
jsign:jsign-ethrex-8025

Conversation

@jsign
Copy link
Copy Markdown
Collaborator

@jsign jsign commented Apr 21, 2026

Summary

This PR rewires stateless-validator-ethrex to use Ethrex's native EIP-8025 execution path instead of maintaining a local NewPayloadRequest -> Block conversion layer.

The main goal is to keep the guest thinner and align our input format with the upstream Ethrex interface, while reducing duplicate payload/parsing logic on our side.

What changed

  • updated Ethrex dependencies to the jsign/ethrex fork at a485d97, which includes the EIP-8025 guest entrypoint we need. I'll wait until feat(l1): fix EIP-8025 compliance lambdaclass/ethrex#6516 gets merged so we can change to ethrex repo again before merging.
  • replaced the old StatelessValidatorEthrexInput rkyv-based guest input with raw EIP-8025 wire bytes
  • added a small wire module that encodes the input as [ssz_len: u32 LE][ssz_bytes][rkyv_witness_bytes] -- this isn't perfectly EIP-8025 compliant since their ExecutionWitness with EIP-8025 feature still uses rkyv. The next step for Ethrex is moving the ExecutionWitness to be spec compliant (thus use SSZ). But we're getting closer.
  • changed the guest to call execution_program_eip8025_bytes(...) directly and return the upstream new_payload_request_root / valid result
  • moved host-side input construction to a dedicated build_eip8025_input(...) helper
  • derived SSZ encode/decode for the shared NewPayloadRequest types needed to serialize Electra/Fulu payloads
  • removed the local execution_payload.rs and new_payload_request.rs conversion/validation code, which is now redundant with the Ethrex implementation
  • updated debug and integration-test call sites to build the new wire input
  • limited the Ethrex path to Electra/Fulu fixtures, which matches the current EIP-8025-supported flow -- this is mostly because the NewRequestPayload expected in Ethrex today only supports this fork.

Why

This reduces maintenance in a sensitive part of the stack:

  • less duplicated block/payload translation logic in this repo
  • closer alignment with the upstream Ethrex guest interface
  • clearer separation between wire encoding on the host side and validation/execution inside Ethrex
  • smaller surface area for fork-specific payload handling bugs

Comment on lines +22 to +24
if !matches!(fork, ForkName::Electra | ForkName::Fulu) {
return None;
}
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.

Mainly because now we rely on whatever input is supported by Ethrex which is Electra/Fulu and prob forward from now on.

Should prob be okay until we do runs for older forks some day.
Amsterdam support should be added soon for Ethrex to continue with forward forks.

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.

Nice all this is gone.

Comment on lines -22 to -28
#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)]
pub struct StatelessValidatorEthrexInput {
/// New payload request data.
pub new_payload_request: NewPayloadRequest,
/// database containing all the data necessary to execute
pub execution_witness: ExecutionWitness,
}
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 is gone, since now the guest program receives raw bytes which are directly passed to Ethreum guest program entrypoint. See L21.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Does it mean that the deserialize_input cost is not merged into stf?

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.

The generic deserialize_input scope is still separate, but it only covers copying the raw input bytes now. The actual EIP-8025 decode/deserialization happens inside Ethrex’s execution_program_eip8025_bytes, so that part is now accounted under stf.

I think this prob means we should rename stf for Ethrex now to something a bit more generic, since doesn't have the same meaning as stf before. I'll think some name and change it :)

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.

Changed to run_validation -- not sure if fully convinced, but seems to be a fairer name.


/// [`Guest`] implementation for Ethrex stateless validator.
#[derive(Debug, Clone)]
pub struct StatelessValidatorEthrexGuest;
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 got massively simplified, since now we rely on Ethrex logic doing all this stuff.

Comment on lines -116 to -119
#[cfg(not(feature = "std"))]
{
Self::compute_inner::<P>(input, new_payload_request_root, crypto)
}
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.

Trying to assume no std, tracked by lambdaclass/ethrex#6339

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.

Another nice deletion, also done by Ethrex guest program now.

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 is the encoding defined by Ethrex today which has an hybrid of using SSZ for the NewPayloadRequest and rkyv for ExecutionWitness.

Eventually this should be gone whenever it complies to the spec (which uses all SSZ).

Comment thread Cargo.toml Outdated
Comment on lines +88 to +93
ethrex-common = { git = "https://github.com/jsign/ethrex.git", rev = "a485d9717e9ce5da6901361b9a04c8026d62fa68", default-features = false }
ethrex-crypto = { git = "https://github.com/jsign/ethrex.git", rev = "a485d9717e9ce5da6901361b9a04c8026d62fa68", default-features = false }
ethrex-guest-program = { git = "https://github.com/jsign/ethrex.git", rev = "a485d9717e9ce5da6901361b9a04c8026d62fa68", default-features = false }
ethrex-rlp = { git = "https://github.com/jsign/ethrex.git", rev = "a485d9717e9ce5da6901361b9a04c8026d62fa68", default-features = false }
ethrex-rpc = { git = "https://github.com/jsign/ethrex.git", rev = "a485d9717e9ce5da6901361b9a04c8026d62fa68", default-features = false }
ethrex-vm = { git = "https://github.com/jsign/ethrex.git", rev = "a485d9717e9ce5da6901361b9a04c8026d62fa68", default-features = false }
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.

To be updated whenever lambdaclass/ethrex#6516 gets merged.

@jsign jsign marked this pull request as ready for review April 21, 2026 22:40
@jsign
Copy link
Copy Markdown
Collaborator Author

jsign commented Apr 21, 2026

@han0110, despite is better to wait until Ethrex PR is merged, I think from this side should be okay to have your eyes for a review.

@jsign jsign requested a review from han0110 April 21, 2026 22:42
Copy link
Copy Markdown
Collaborator

@han0110 han0110 left a comment

Choose a reason for hiding this comment

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

Nice simplifcation, looks much cleaner! Just got a small question

Comment on lines -22 to -28
#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)]
pub struct StatelessValidatorEthrexInput {
/// New payload request data.
pub new_payload_request: NewPayloadRequest,
/// database containing all the data necessary to execute
pub execution_witness: ExecutionWitness,
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Does it mean that the deserialize_input cost is not merged into stf?

jsign added 6 commits April 22, 2026 10:24
Signed-off-by: jsign <jsign.uy@gmail.com>
# Conflicts:
#	Cargo.lock
#	bin/stateless-validator-ethrex/risc0/Cargo.lock
#	bin/stateless-validator-ethrex/sp1/Cargo.lock
#	bin/stateless-validator-ethrex/zisk/Cargo.lock
#	crates/stateless-validator-ethrex/Cargo.toml
#	crates/stateless-validator-ethrex/src/execution_payload.rs
#	crates/stateless-validator-ethrex/src/guest.rs
#	crates/stateless-validator-ethrex/src/host.rs
Co-authored-by: Copilot <copilot@github.com>
@jsign jsign requested a review from han0110 April 27, 2026 13:16
@jsign
Copy link
Copy Markdown
Collaborator Author

jsign commented Apr 27, 2026

@han0110, the lambdaclass/ethrex#6516 pr was merged. Now switched Cargo.toml to point to ethrex main repo and branch. Reg code, nothing interesting has changed since your last comment (only this nit change during the ethrex pr reviewing).

Copy link
Copy Markdown
Collaborator

@han0110 han0110 left a comment

Choose a reason for hiding this comment

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

LGTM

@jsign jsign merged commit 3387b8f into eth-act:main Apr 27, 2026
41 checks passed
Itodo-S pushed a commit to Itodo-S/ethrex that referenced this pull request May 1, 2026
## Summary

This branch fixes the EIP-8025 guest-program execution flow and aligns
the SSZ-side `NewPayloadRequest` model with the Electra/CL spec.

At a high level, it adds a byte-oriented EIP-8025 execution entrypoint,
moves execution requests into a typed SSZ container, and uses that typed
representation to reconstruct the EL `requests_hash` correctly when
converting a `NewPayloadRequest` into a block.

## What changed

- Added `execution_program_eip8025_bytes`, which decodes and executes
the EIP-8025 wire format directly:
  - `[ssz_len: u32 LE][ssz_bytes][rkyv_bytes]`
- Refactored the EIP-8025 validation flow so both entrypoints share the
same logic for:
  - `NewPayloadRequest -> Block` conversion
  - block hash validation
  - blob versioned hash validation
  - stateless execution via `execute_blocks`
- Updated the EIP-8025 SSZ types in `ethrex-common` to better match the
Electra consensus spec:
- `ExecutionPayload` no longer carries deposit/withdrawal/consolidation
request lists
- `NewPayloadRequest.execution_requests` is now a typed
`ExecutionRequests` container
  - spec constants were renamed/normalized for clarity
  - the consolidation request limit was corrected to the Electra value
- Added `ExecutionRequests::to_encoded_requests()` to convert the SSZ
typed request container into the EIP-7685 encoded request list expected
by `compute_requests_hash`
- Re-exported the new EIP-8025 byte entrypoint from
`ethrex-guest-program`

## Behavior notes

- Malformed EIP-8025 wire input still returns an error
- For well-formed input, the byte-entrypoint now always computes the
`new_payload_request_root`
- If validation/execution fails after decoding, the byte-entrypoint
returns `ProgramOutput { valid: false, ... }` instead of failing
outright

## Why this matters

This makes the EIP-8025 guest path closer to the consensus-side
structure we actually need to prove against, while also giving us a
direct raw-bytes execution entrypoint for the EIP-8025 wire format.

It also removes the mismatch between the SSZ request representation and
the EL request-hash computation path by deriving `requests_hash` from a
properly typed `ExecutionRequests` container.

## External usage

I'm already using this PR branch into an ere-guests PR here:
eth-act/ere-guests#39
This allowed `ere-guests` to basically remove a lot of wrapper code
before calling the guest program regarding execution-payload->Block,
output calculation, etc -- making now Ethrex own all that logic as
expected.

---------

Signed-off-by: jsign <jsign.uy@gmail.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.

2 participants