diff --git a/CHANGELOG.md b/CHANGELOG.md index bdf92a128fb..d55e57e4196 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/crates/common/types/account.rs b/crates/common/types/account.rs index d211a8f3745..d329dff71d7 100644 --- a/crates/common/types/account.rs +++ b/crates/common/types/account.rs @@ -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. - pub jump_targets: Vec, + // Bitmap of valid JUMPDEST destinations. + pub jump_targets: Vec, } impl Code { @@ -58,16 +59,18 @@ impl Code { } } - fn compute_jump_targets(code: &[u8]) -> Vec { + fn compute_jump_targets(code: &[u8]) -> Vec { 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 => { @@ -78,7 +81,7 @@ impl Code { } i += 1; } - targets + bitmap } /// Estimates the size of the Code struct in bytes @@ -92,8 +95,8 @@ impl Code { pub fn size(&self) -> usize { let hash_size = size_of::(); let bytes_size = size_of::(); - let vec_size = size_of::>() + self.jump_targets.len() * size_of::(); - hash_size + bytes_size + vec_size + let bitmap_size = size_of::>() + self.jump_targets.len() * size_of::(); + hash_size + bytes_size + bitmap_size } } diff --git a/crates/storage/lib.rs b/crates/storage/lib.rs index a36ad55eff4..62b8b955c21 100644 --- a/crates/storage/lib.rs +++ b/crates/storage/lib.rs @@ -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"; diff --git a/crates/storage/store.rs b/crates/storage/store.rs index 94823407183..7e090aa6725 100644 --- a/crates/storage/store.rs +++ b/crates/storage/store.rs @@ -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: >::decode(targets)?, + jump_targets: >::decode(bitmap_bytes)?, }; // insert into cache and evict if needed diff --git a/crates/vm/levm/src/opcode_handlers/stack_memory_storage_flow.rs b/crates/vm/levm/src/opcode_handlers/stack_memory_storage_flow.rs index 0c104befb8f..ce269f425c6 100644 --- a/crates/vm/levm/src/opcode_handlers/stack_memory_storage_flow.rs +++ b/crates/vm/levm/src/opcode_handlers/stack_memory_storage_flow.rs @@ -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