Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Perf

### 2026-01-07

- Change jump_targets to a bitmap [#5739](https://github.com/lambdaclass/ethrex/pull/5739)

### 2025-12-23

- Remove needless allocs on store api [#5709](https://github.com/lambdaclass/ethrex/pull/5709)
Expand Down
19 changes: 11 additions & 8 deletions crates/common/types/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@ pub struct Code {
// endpoints to access that hash, saving one expensive Keccak hash.
pub hash: H256,
pub bytecode: Bytes,
// TODO: Consider using Arc<[u32]> (needs to enable serde rc feature)
// TODO: Consider using Arc<[u64]> (needs to enable serde rc feature)
// The valid addresses are 32-bit because, despite EIP-3860 restricting initcode size,
// this does not apply to previous forks. This is tested in the EEST tests, which would
// panic in debug mode.
Comment on lines 33 to 35
Copy link
Contributor

Choose a reason for hiding this comment

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

This comment should be updated, since it's about the specifics of the old implementation

pub jump_targets: Vec<u32>,
// Bitmap of valid JUMPDEST destinations.
pub jump_targets: Vec<u64>,
}

impl Code {
Expand All @@ -58,16 +59,18 @@ impl Code {
}
}

fn compute_jump_targets(code: &[u8]) -> Vec<u32> {
fn compute_jump_targets(code: &[u8]) -> Vec<u64> {
debug_assert!(code.len() <= u32::MAX as usize);
let mut targets = Vec::new();
let mut bitmap = vec![0u64; code.len().div_ceil(64)];
let mut i = 0;
while i < code.len() {
// TODO: we don't use the constants from the vm module to avoid a circular dependency
match code[i] {
// OP_JUMPDEST
0x5B => {
targets.push(i as u32);
let word = i / 64;
let bit = i % 64;
bitmap[word] |= 1u64 << bit;
}
// OP_PUSH1..32
c @ 0x60..0x80 => {
Expand All @@ -78,7 +81,7 @@ impl Code {
}
i += 1;
}
targets
bitmap
}

/// Estimates the size of the Code struct in bytes
Expand All @@ -92,8 +95,8 @@ impl Code {
pub fn size(&self) -> usize {
let hash_size = size_of::<H256>();
let bytes_size = size_of::<Bytes>();
let vec_size = size_of::<Vec<u32>>() + self.jump_targets.len() * size_of::<u32>();
hash_size + bytes_size + vec_size
let bitmap_size = size_of::<Vec<u64>>() + self.jump_targets.len() * size_of::<u64>();
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe use std::mem::size_of_val like in encode_code (which should probably be calling this function instead of calculating this).

hash_size + bytes_size + bitmap_size
}
}

Expand Down
2 changes: 1 addition & 1 deletion crates/storage/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pub use store::{

/// Store Schema Version, must be updated on any breaking change
/// An upgrade to a newer schema version invalidates currently stored data, requiring a re-sync.
pub const STORE_SCHEMA_VERSION: u64 = 1;
pub const STORE_SCHEMA_VERSION: u64 = 2;

/// Name of the file storing the metadata about the database
pub const STORE_METADATA_FILENAME: &str = "metadata.json";
5 changes: 2 additions & 3 deletions crates/storage/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -628,13 +628,12 @@ impl Store {
return Ok(None);
};
let bytes = Bytes::from_owner(bytes);
let (bytecode_slice, targets) = decode_bytes(&bytes)?;
let (bytecode_slice, bitmap_bytes) = decode_bytes(&bytes)?;
let bytecode = bytes.slice_ref(bytecode_slice);

let code = Code {
hash: code_hash,
bytecode,
jump_targets: <Vec<_>>::decode(targets)?,
jump_targets: <Vec<_>>::decode(bitmap_bytes)?,
};

// insert into cache and evict if needed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -291,12 +291,16 @@ impl<'a> VM<'a> {
/// - Checking that the byte at the requested target PC is a JUMPDEST (0x5B).
/// - Ensuring the byte is not blacklisted. In other words, the 0x5B value is not part of a
/// constant associated with a push instruction.
#[expect(clippy::as_conversions)]
fn target_address_is_valid(call_frame: &CallFrame, jump_address: u32) -> bool {
let index = jump_address as usize;
let word = index / 64;
let bit = index % 64;
call_frame
.bytecode
.jump_targets
.binary_search(&jump_address)
.is_ok()
.get(word)
.is_some_and(|entry| (entry >> bit) & 1 == 1)
}

/// JUMP* family (`JUMP` and `JUMP` ATTOW [DEC 2024]) helper
Expand Down