Skip to content

feat: introduce Address newtype (closes #75)#76

Merged
tcrypt25519 merged 7 commits into
mainfrom
feat/address-type
Feb 15, 2026
Merged

feat: introduce Address newtype (closes #75)#76
tcrypt25519 merged 7 commits into
mainfrom
feat/address-type

Conversation

@tcrypt25519

Copy link
Copy Markdown
Owner

Summary

  • Adds jet_runtime::Address — a #[repr(transparent)] newtype over [u8; 20] that replaces the private type alias and scattered raw byte arrays throughout the codebase
  • Implements Display/Debug (lowercase 0x-prefixed hex), FromStr/TryFrom<&str> (with left-padding for short forms like "0x1234"), From<[u8; 20]>/Into<[u8; 20]>, AsRef<[u8]>, Clone, Copy, PartialEq, Eq, Hash, Default
  • Exports Address from jet_runtime at the crate root
  • Updates BlockInfo::coinbase, Engine::build_contract/run_contract, Manager::add_contract_function, mangle_contract_fn, jet_contract_fn_lookup, test helpers, and jetdbg to use the new type
  • Eliminates boilerplate like vec![0u8; ADDRESS_SIZE_BYTES] + manual hex::encode at all call sites
  • As a side-effect, jet_contract_fn_lookup now produces function names consistent with mangle_contract_fn (both use the same Display format), fixing a latent name-mismatch bug in inter-contract call lookup

Test plan

  • cargo check passes (verified locally)
  • Unit tests in address.rs cover zero address, roundtrip, full/short parse, prefix-less parse, and error cases
  • Existing ROM integration tests continue to build and run contracts via the new Address API
  • CI green

Closes #75

🤖 Generated with Claude Code

Replace the private `type Address = [u8; ADDRESS_SIZE_BYTES]` alias
with a proper `Address([u8; 20])` newtype in `jet_runtime::address`.

Key properties of the new type:
- `#[repr(transparent)]` over `[u8; 20]` — identical memory layout,
  compatible with `#[repr(C)]` structs like `BlockInfo`
- Derives `Clone`, `Copy`, `PartialEq`, `Eq`, `Hash`, `Default`
- `Display` / `Debug` emit lowercase `0x`-prefixed hex
- `FromStr` / `TryFrom<&str>` parse hex strings with optional `0x`
  prefix and left-pad short forms (e.g. `"0x1234"`) to 20 bytes
- `From<[u8; 20]>` / `Into<[u8; 20]>` and `AsRef<[u8]>` for
  zero-cost interop with existing byte-slice APIs
- `Address::ZERO` constant and `as_bytes_mut` for in-place mutation

Updated across the codebase:
- `BlockInfo::coinbase` field and getter now use `Address`
- `mangle_contract_fn` takes `&Address`; `jet_contract_fn_lookup`
  builds an `Address` from the little-endian stack bytes (fixing an
  implicit reversal that previously produced inconsistent names)
- `Engine::build_contract` / `run_contract` take `Address` by value
- `Manager::add_contract_function` / `verify_contract` take `Address`
- Test helpers and `jetdbg` construct addresses via `Address::new`,
  `Address::ZERO`, and `str::parse::<Address>()`

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings February 14, 2026 23:20
@semanticdiff-com

semanticdiff-com Bot commented Feb 14, 2026

Copy link
Copy Markdown

Review changes with  SemanticDiff

Changed Files
File Status
  crates/jet/src/engine/mod.rs  83% smaller
  crates/jet/src/bin/jetdbg.rs  51% smaller
  crates/jet/tests/roms/mod.rs  45% smaller
  crates/jet/src/builder/manager.rs  34% smaller
  crates/jet_runtime/src/exec.rs  14% smaller
  crates/jet_runtime/src/binding/mod.rs  10% smaller
  crates/jet/tests/invalid_opcode.rs  0% smaller
  crates/jet_runtime/src/address.rs  0% smaller
  crates/jet_runtime/src/lib.rs  0% smaller

@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello @tcrypt25519, 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 significantly refactors how Ethereum addresses are handled within the runtime by introducing a dedicated Address newtype. This change enhances type safety, reduces boilerplate code, and provides a more robust and consistent representation of addresses across the system. The new type simplifies address manipulation, parsing, and formatting, leading to cleaner and more maintainable code.

Highlights

  • New Address Newtype: Introduced jet_runtime::Address, a #[repr(transparent)] newtype wrapping [u8; 20], to replace raw byte arrays and private type aliases for Ethereum addresses throughout the codebase.
  • Enhanced Address Functionality: Implemented Display/Debug (lowercase 0x-prefixed hex), FromStr/TryFrom<&str> (with left-padding for short forms), From<[u8; 20]>/Into<[u8; 20]>, AsRef<[u8]>, Clone, Copy, PartialEq, Eq, Hash, and Default for the new Address type.
  • Codebase Integration and Refinement: Updated BlockInfo::coinbase, Engine::build_contract/run_contract, Manager::add_contract_function, mangle_contract_fn, jet_contract_fn_lookup, test helpers, and jetdbg to utilize the new Address type, eliminating boilerplate and improving type safety.
  • Bug Fix and Consistency: Fixed a latent name-mismatch bug in inter-contract call lookup by ensuring jet_contract_fn_lookup now produces function names consistent with mangle_contract_fn due to the shared Display format of Address.
Changelog
  • crates/jet/src/bin/jetdbg.rs
    • Imported Address from jet_runtime.
    • Updated build_contract and run_contract calls to use Address instances parsed from string literals.
    • Modified new_test_block_info to construct coinbase using Address::new.
  • crates/jet/src/builder/manager.rs
    • Imported Address from jet_runtime.
    • Changed add_contract_function and verify_contract methods to accept Address type instead of &str.
    • Updated calls to exec::mangle_contract_fn to pass a reference to Address.
  • crates/jet/src/engine/mod.rs
    • Imported Address from jet_runtime.
    • Modified build_contract, run_contract, and get_contract_exec_fn methods to accept Address type instead of &str.
    • Updated calls to exec::mangle_contract_fn to pass a reference to Address.
  • crates/jet/tests/roms/mod.rs
    • Imported Address from jet_runtime.
    • Refactored contract building loop to use Address::ZERO and mutable Address instances.
    • Simplified run_contract call to use Address::ZERO directly, removing manual byte array creation and hex encoding.
  • crates/jet_runtime/src/address.rs
    • Added a new module defining the Address newtype.
    • Implemented Address as a #[repr(transparent)] wrapper around [u8; 20].
    • Provided ZERO constant, LEN constant, new, as_bytes, and as_bytes_mut methods.
    • Implemented AsRef<[u8]>, From<[u8; 20]>, From<Address> for [u8; 20] traits.
    • Implemented fmt::Debug and fmt::Display for Address.
    • Defined AddressParseError and implemented fmt::Display and std::error::Error for it.
    • Implemented FromStr and TryFrom<&str> for Address, supporting 0x prefix and left-padding for short forms.
    • Included unit tests for Address functionality, including zero address, roundtrip conversion, parsing full/short/prefix-less addresses, and error handling.
  • crates/jet_runtime/src/binding/mod.rs
    • Removed unused import ADDRESS_SIZE_BYTES.
    • Updated BlockInfo's fmt::Display implementation to directly use the Address type for coinbase.
  • crates/jet_runtime/src/exec.rs
    • Removed the type alias type Address = [u8; ADDRESS_SIZE_BYTES].
    • Imported the new Address type from crate::address.
    • Modified mangle_contract_fn to accept &Address instead of &str.
    • Refactored jet_contract_fn_lookup to convert the little-endian addr_slice into the new Address type before mangling the function name, ensuring consistency.
  • crates/jet_runtime/src/lib.rs
    • Added pub mod address; to expose the new address module.
    • Exported Address with pub use address::Address; at the crate root.
Activity
  • The pull request introduces a new Address newtype and integrates it across the codebase.
  • The author has verified that cargo check passes locally.
  • Unit tests for the new address.rs module cover various scenarios including zero address, roundtrip conversions, and parsing different address formats.
  • Existing ROM integration tests are confirmed to continue building and running contracts using the new Address API.
  • The author expects CI to pass, indicating successful integration and no regressions.
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 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 counter productive. 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.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

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

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 introduces a type-safe Address newtype, significantly improving code clarity, safety, and maintainability across the codebase by replacing raw byte arrays and string slices with a dedicated Address type. While the refactoring is clean and the new address.rs module is well-implemented, a critical regression was identified in the jet_contract_fn_lookup function. This issue, where a fixed-size array is indexed using an unconstrained iterator, can lead to a runtime panic (Denial of Service) if the input slice exceeds 20 bytes. Please address this vulnerability to ensure runtime robustness. Additionally, consider a minor optimization for the FromStr implementation for Address.

Comment thread crates/jet_runtime/src/exec.rs
Comment thread crates/jet_runtime/src/address.rs Outdated
Missed in the initial pass — the test called build_contract with a bare
&str; update it to pass Address::ZERO instead.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a dedicated jet_runtime::Address newtype to replace ad-hoc address representations (raw byte arrays / strings) across the runtime, compiler engine API, tests, and the jetdbg binary, aiming to centralize formatting/parsing and eliminate boilerplate.

Changes:

  • Added jet_runtime::Address ([u8; 20] wrapper) with hex formatting + parsing utilities and unit tests.
  • Migrated contract build/run APIs and function-name mangling/lookup to use Address consistently.
  • Updated test helpers and jetdbg to construct and pass Address values instead of strings/byte vectors.

Reviewed changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
crates/jet_runtime/src/lib.rs Adds address module and re-exports Address at crate root.
crates/jet_runtime/src/address.rs Implements the Address newtype, formatting/parsing, and unit tests.
crates/jet_runtime/src/exec.rs Replaces prior alias with Address, updates mangling and lookup path.
crates/jet_runtime/src/binding/mod.rs Updates BlockInfo display to print coinbase via Address formatting.
crates/jet/src/engine/mod.rs Changes Engine::build_contract / run_contract to take Address.
crates/jet/src/builder/manager.rs Changes Manager::add_contract_function/verification to use Address.
crates/jet/tests/roms/mod.rs Updates ROM test harness to generate/pass Address values.
crates/jet/src/bin/jetdbg.rs Updates debug binary to parse and use Address values.
Comments suppressed due to low confidence (1)

crates/jet_runtime/src/exec.rs:9

  • use crate::{ address::Address, ..., * } will likely fail to compile because the glob import (*) re-exports Address from the crate root (pub use address::Address), causing a duplicate Address import. Remove the explicit address::Address import or stop using the glob import and list required items explicitly.
use crate::{
    address::Address,
    error::{Result, RuntimeError},
    symbols::FN_CONTRACT_PREFIX,
    *,
};

Comment thread crates/jet_runtime/src/exec.rs
Comment thread crates/jet/src/engine/mod.rs
Comment thread crates/jet_runtime/src/address.rs
tcrypt25519 and others added 4 commits February 14, 2026 18:34
…constant

Three issues raised in code review:

1. jet_contract_fn_lookup: add .take(ADDRESS_SIZE_BYTES) so a 32-byte
   EVM stack word cannot index past the 20-byte `bytes` array and panic.
   The address lives in the low bytes of the word, so after reversing
   (little- to big-endian) we only need the first 20 bytes.

2. FromStr: replace format! + hex::decode (two heap allocations) with a
   stack-allocated [u8; 40] padding buffer and hex::decode_to_slice,
   making address parsing allocation-free.

3. Address::LEN: source from jet_ir::ADDRESS_SIZE_BYTES instead of a
   duplicated literal so the canonical address length is defined once.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Address::LEN = 20 is now the sole definition of address width.
Every use of the literal 20 (or 40 for hex) inside the type and
its impls is replaced:

- struct field:        [u8; Self::LEN]
- ZERO constant:       [0u8; Self::LEN]
- new / as_bytes*:     [u8; Self::LEN]
- From impls:          [u8; Address::LEN]
- FromStr stack bufs:  Address::HEX_LEN (= Self::LEN * 2, private const)
- tests:               Address::LEN / Address::HEX_LEN

Removes the previous jet_ir::ADDRESS_SIZE_BYTES dependency; the
canonical length now lives in Address itself.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@tcrypt25519
tcrypt25519 merged commit d41a428 into main Feb 15, 2026
tcrypt25519 added a commit that referenced this pull request Feb 16, 2026
* feat: introduce Address newtype to replace raw [u8; 20] (closes #75)

Replace the private `type Address = [u8; ADDRESS_SIZE_BYTES]` alias
with a proper `Address([u8; 20])` newtype in `jet_runtime::address`.

Key properties of the new type:
- `#[repr(transparent)]` over `[u8; 20]` — identical memory layout,
  compatible with `#[repr(C)]` structs like `BlockInfo`
- Derives `Clone`, `Copy`, `PartialEq`, `Eq`, `Hash`, `Default`
- `Display` / `Debug` emit lowercase `0x`-prefixed hex
- `FromStr` / `TryFrom<&str>` parse hex strings with optional `0x`
  prefix and left-pad short forms (e.g. `"0x1234"`) to 20 bytes
- `From<[u8; 20]>` / `Into<[u8; 20]>` and `AsRef<[u8]>` for
  zero-cost interop with existing byte-slice APIs
- `Address::ZERO` constant and `as_bytes_mut` for in-place mutation

Updated across the codebase:
- `BlockInfo::coinbase` field and getter now use `Address`
- `mangle_contract_fn` takes `&Address`; `jet_contract_fn_lookup`
  builds an `Address` from the little-endian stack bytes (fixing an
  implicit reversal that previously produced inconsistent names)
- `Engine::build_contract` / `run_contract` take `Address` by value
- `Manager::add_contract_function` / `verify_contract` take `Address`
- Test helpers and `jetdbg` construct addresses via `Address::new`,
  `Address::ZERO`, and `str::parse::<Address>()`

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* chore: CI change 🤖

* fix: update invalid_opcode test to use Address

Missed in the initial pass — the test called build_contract with a bare
&str; update it to pass Address::ZERO instead.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix: address review feedback — panic safety, alloc-free parsing, LEN constant

Three issues raised in code review:

1. jet_contract_fn_lookup: add .take(ADDRESS_SIZE_BYTES) so a 32-byte
   EVM stack word cannot index past the 20-byte `bytes` array and panic.
   The address lives in the low bytes of the word, so after reversing
   (little- to big-endian) we only need the first 20 bytes.

2. FromStr: replace format! + hex::decode (two heap allocations) with a
   stack-allocated [u8; 40] padding buffer and hex::decode_to_slice,
   making address parsing allocation-free.

3. Address::LEN: source from jet_ir::ADDRESS_SIZE_BYTES instead of a
   duplicated literal so the canonical address length is defined once.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* chore: CI change 🤖

* refactor: self-referential LEN constant as single source of truth

Address::LEN = 20 is now the sole definition of address width.
Every use of the literal 20 (or 40 for hex) inside the type and
its impls is replaced:

- struct field:        [u8; Self::LEN]
- ZERO constant:       [0u8; Self::LEN]
- new / as_bytes*:     [u8; Self::LEN]
- From impls:          [u8; Address::LEN]
- FromStr stack bufs:  Address::HEX_LEN (= Self::LEN * 2, private const)
- tests:               Address::LEN / Address::HEX_LEN

Removes the previous jet_ir::ADDRESS_SIZE_BYTES dependency; the
canonical length now lives in Address itself.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* chore: CI change 🤖

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
@tcrypt25519
tcrypt25519 deleted the feat/address-type branch February 16, 2026 20:02
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.

Create real Address type

2 participants