diff --git a/crates/common/types/account.rs b/crates/common/types/account.rs index 228ada9ebaf..32f462616b4 100644 --- a/crates/common/types/account.rs +++ b/crates/common/types/account.rs @@ -24,8 +24,11 @@ use crate::{ pub struct Code { pub hash: H256, pub bytecode: Bytes, - // TODO: Consider using Arc<[u16]> (needs to enable serde rc feature) - pub jump_targets: Vec, + // TODO: Consider using Arc<[u32]> (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, } impl Code { @@ -40,8 +43,8 @@ impl Code { } } - fn compute_jump_targets(code: &[u8]) -> Vec { - debug_assert!(code.len() <= u16::MAX as usize); + fn compute_jump_targets(code: &[u8]) -> Vec { + debug_assert!(code.len() <= u32::MAX as usize); let mut targets = Vec::new(); let mut i = 0; while i < code.len() { @@ -49,7 +52,7 @@ impl Code { match code[i] { // OP_JUMPDEST 0x5B => { - targets.push(i as u16); + targets.push(i as u32); } // OP_PUSH1..32 c @ 0x60..0x80 => { diff --git a/crates/storage/store_db/rocksdb.rs b/crates/storage/store_db/rocksdb.rs index a7a23c047ef..35c0b9cec39 100644 --- a/crates/storage/store_db/rocksdb.rs +++ b/crates/storage/store_db/rocksdb.rs @@ -894,14 +894,16 @@ impl StoreEngine for Store { } for (code_hash, code) in update_batch.code_updates { - let mut buf = Vec::with_capacity(6 + code.bytecode.len() + 2 * code.jump_targets.len()); + let mut buf = Vec::with_capacity( + 6 + code.bytecode.len() + + code + .jump_targets + .iter() + .map(std::mem::size_of_val) + .sum::(), + ); code.bytecode.encode(&mut buf); - code.jump_targets - .into_iter() - .flat_map(|t| t.to_le_bytes()) - .collect::>() - .as_slice() - .encode(&mut buf); + code.jump_targets.encode(&mut buf); batch.put_cf(&cf_codes, code_hash.0, buf); } @@ -1268,14 +1270,16 @@ impl StoreEngine for Store { async fn add_account_code(&self, code: Code) -> Result<(), StoreError> { let hash_key = code.hash.0.to_vec(); - let mut buf = Vec::with_capacity(6 + code.bytecode.len() + 2 * code.jump_targets.len()); + let mut buf = Vec::with_capacity( + 6 + code.bytecode.len() + + code + .jump_targets + .iter() + .map(std::mem::size_of_val) + .sum::(), + ); code.bytecode.encode(&mut buf); - code.jump_targets - .into_iter() - .flat_map(|t| t.to_le_bytes()) - .collect::>() - .as_slice() - .encode(&mut buf); + code.jump_targets.encode(&mut buf); self.write_async(CF_ACCOUNT_CODES, hash_key, buf).await } @@ -1308,17 +1312,10 @@ impl StoreEngine for Store { }; let bytes = Bytes::from_owner(bytes); let (bytecode, targets) = decode_bytes(&bytes)?; - let (targets, rest) = decode_bytes(targets)?; - if !rest.is_empty() || !targets.len().is_multiple_of(2) { - return Err(StoreError::DecodeError); - } let code = Code { hash: code_hash, bytecode: Bytes::copy_from_slice(bytecode), - jump_targets: targets - .chunks_exact(2) - .map(|c| u16::from_le_bytes([c[0], c[1]])) - .collect(), + jump_targets: >::decode(targets)?, }; Ok(Some(code)) } @@ -1878,14 +1875,16 @@ impl StoreEngine for Store { for (code_hash, code) in account_codes { let key = code_hash.as_bytes().to_vec(); - let mut buf = Vec::with_capacity(6 + code.bytecode.len() + 2 * code.jump_targets.len()); + let mut buf = Vec::with_capacity( + 6 + code.bytecode.len() + + code + .jump_targets + .iter() + .map(std::mem::size_of_val) + .sum::(), + ); code.bytecode.encode(&mut buf); - code.jump_targets - .into_iter() - .flat_map(|t| t.to_le_bytes()) - .collect::>() - .as_slice() - .encode(&mut buf); + code.jump_targets.encode(&mut buf); batch_ops.push((CF_ACCOUNT_CODES.to_string(), key, buf)); } 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 316912d2620..23eaf4e108b 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,7 +291,7 @@ 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. - fn target_address_is_valid(call_frame: &mut CallFrame, jump_address: u16) -> bool { + fn target_address_is_valid(call_frame: &mut CallFrame, jump_address: u32) -> bool { call_frame .bytecode .jump_targets @@ -305,14 +305,16 @@ impl<'a> VM<'a> { /// to be equal to the specified address. If the address is not a /// valid JUMPDEST, it will return an error pub fn jump(call_frame: &mut CallFrame, jump_address: U256) -> Result<(), VMError> { - let jump_address_u16 = jump_address + let jump_address_u32 = jump_address .try_into() .map_err(|_err| ExceptionalHalt::VeryLargeNumber)?; #[expect(clippy::arithmetic_side_effects)] - if Self::target_address_is_valid(call_frame, jump_address_u16) { + if Self::target_address_is_valid(call_frame, jump_address_u32) { call_frame.increase_consumed_gas(gas_cost::JUMPDEST)?; - call_frame.pc = usize::from(jump_address_u16) + 1; + call_frame.pc = usize::try_from(jump_address_u32) + .map_err(|_err| ExceptionalHalt::VeryLargeNumber)? + + 1; Ok(()) } else { Err(ExceptionalHalt::InvalidJump.into())