Skip to content

fix(interpreter): use IntoAddress in pop_address to avoid const-eval panic - #3735

Merged
rakita merged 2 commits into
bluealloy:mainfrom
pjdurden:fix/pop-address-const-eval-panic
Jun 3, 2026
Merged

fix(interpreter): use IntoAddress in pop_address to avoid const-eval panic#3735
rakita merged 2 commits into
bluealloy:mainfrom
pjdurden:fix/pop-address-const-eval-panic

Conversation

@pjdurden

@pjdurden pjdurden commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Summary

StackTr::pop_address converts the popped stack word with
Address::from(value.to_be_bytes()). Because Address: From<[u8; 20]>, type
inference forces ruint's const-generic Uint::<256, 4>::to_be_bytes::<20>(),
whose const { Self::assert_bytes(BYTES) } requires BYTES == Self::BYTES
(32 for U256).

That const block is unreachable for valid inputs, so the code compiles whenever
the compiler dead-code-eliminates it — but the Rust Reference only guarantees such
a block may or may not be evaluated. Build profiles that do evaluate it
(cargo-fuzz coverage instrumentation, release codegen) hit
error[E0080]: evaluation panicked: BYTES must be equal to Self::BYTES,
breaking downstream builds. Fixes #3728.

Fix

Reuse the crate's existing canonical converter, IntoAddress for U256
(crates/interpreter/src/instructions/utility.rs), via value.into_address().
It does Address::from_word(B256::from(value.to_be_bytes())), whose
to_be_bytes() infers the correct [u8; 32] size — identical EVM word→address
truncation (keep the low 20 bytes) with no fragile const-generic instantiation.
This also removes the inlined conversion in favour of the shared helper. The same
problematic idiom is present on main, so this is not release-specific.

Testing

Added pop_address_truncates_to_low_20_bytes (truncates a full 32-byte word to its
low 20 bytes; returns None on an empty stack). Verified on rustc 1.96:

  • cargo test -p revm-interpreter --all-features → 45 passed
  • cargo clippy -p revm-interpreter --all-targets --all-features → clean
  • cargo fmt --all --check → clean
  • cargo build -p revm-interpreter --release and --no-default-features → both build
    (these are the profiles that previously hit the E0080)

Closes #3728

…panic

`StackTr::pop_address` converted the popped word with
`Address::from(value.to_be_bytes())`. Since `Address: From<[u8; 20]>`, type
inference forces ruint's const-generic `Uint::<256, 4>::to_be_bytes::<20>()`,
whose `const { Self::assert_bytes(BYTES) }` requires `BYTES == Self::BYTES`
(32 for U256). That unreachable const block is only sometimes dead-code
eliminated; build profiles that actually evaluate it (cargo-fuzz coverage,
release codegen) hit `error[E0080]: BYTES must be equal to Self::BYTES`,
breaking downstream builds (bluealloy#3728).

Use the crate's existing `IntoAddress` converter (`value.into_address()`),
which does `Address::from_word(B256::from(value.to_be_bytes()))`. Its
`to_be_bytes()` infers the correct `[u8; 32]` size, so there is no fragile
const-generic instantiation, and `pop_address` now reuses the canonical
U256->Address conversion instead of inlining its own.

Add a regression test asserting `pop_address` truncates a full 32-byte word to
its low 20 bytes and returns `None` on an empty stack.

Closes bluealloy#3728

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 2, 2026 00:27

Copilot AI 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.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Refactors pop_address to use the IntoAddress utility trait instead of constructing an Address directly from big-endian bytes, and adds a unit test verifying the truncation behavior.

Changes:

  • Replace inline Address::from(value.to_be_bytes()) with value.into_address() in StackTr::pop_address.
  • Import IntoAddress from instructions::utility.
  • Add a test covering both the truncation-to-low-20-bytes behavior and the empty-stack case.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
crates/interpreter/src/interpreter_types.rs Use IntoAddress trait to convert popped U256 to Address.
crates/interpreter/src/interpreter/stack.rs Add test asserting pop_address returns low 20 bytes and None on empty stack.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread crates/interpreter/src/interpreter_types.rs
Comment on lines +505 to +532
#[test]
fn pop_address_truncates_to_low_20_bytes() {
use primitives::Address;

// A full 32-byte word whose high 12 bytes are set and must be discarded,
// and whose low 20 bytes are the address. This mirrors how the EVM reads an
// address from a stack word (e.g. BALANCE/EXTCODESIZE/EXTCODECOPY targets).
let word = U256::from_be_bytes::<32>([
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x11, 0x22,
0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00,
0x12, 0x34, 0x56, 0x78,
]);
let expected = Address::from([
0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee,
0xff, 0x00, 0x12, 0x34, 0x56, 0x78,
]);

run(|stack| {
assert!(stack.push(word));
assert_eq!(stack.pop_address(), Some(expected));
});

// Empty stack returns None rather than panicking.
run(|stack| {
assert_eq!(stack.pop_address(), None);
});
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

No need for this

Suggested change
#[test]
fn pop_address_truncates_to_low_20_bytes() {
use primitives::Address;
// A full 32-byte word whose high 12 bytes are set and must be discarded,
// and whose low 20 bytes are the address. This mirrors how the EVM reads an
// address from a stack word (e.g. BALANCE/EXTCODESIZE/EXTCODECOPY targets).
let word = U256::from_be_bytes::<32>([
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x11, 0x22,
0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00,
0x12, 0x34, 0x56, 0x78,
]);
let expected = Address::from([
0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee,
0xff, 0x00, 0x12, 0x34, 0x56, 0x78,
]);
run(|stack| {
assert!(stack.push(word));
assert_eq!(stack.pop_address(), Some(expected));
});
// Empty stack returns None rather than panicking.
run(|stack| {
assert_eq!(stack.pop_address(), None);
});
}

@rakita rakita left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hey lets remove the test, it is not needed

Remove `pop_address_truncates_to_low_20_bytes` as requested in review on
bluealloy#3735 — the truncation behaviour is covered by the shared `IntoAddress`
conversion path and the dedicated test is not needed.
@pjdurden

pjdurden commented Jun 2, 2026

Copy link
Copy Markdown
Contributor Author

Hey lets remove the test, it is not needed

@rakita , i've removed the test. One small issue - The CI workflows are still showing awaiting approval, would you mind approving the runs so the workflow can run?

@codspeed-hq

codspeed-hq Bot commented Jun 3, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 176 untouched benchmarks
⏩ 1 skipped benchmark1


Comparing pjdurden:fix/pop-address-const-eval-panic (32b4008) with main (16aab94)

Open in CodSpeed

Footnotes

  1. 1 benchmark was skipped, so the baseline result was used instead. If it was deleted from the codebase, click here and archive it to remove it from the performance reports.

@rakita
rakita merged commit 939b879 into bluealloy:main Jun 3, 2026
34 checks passed
@github-actions github-actions Bot mentioned this pull request Jun 3, 2026
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.

const-eval panic in revm-interpreter 19.1.0: Uint<256>::to_be_bytes::<20> hits ruint 1.17.2 assert_bytes on rustc 1.93+

3 participants