//! Some LLVM IR generator utilies. use inkwell::values::BasicValue; use crate::polkavm::context::address_space::AddressSpace; use crate::polkavm::context::function::llvm_runtime::LLVMRuntime; use crate::polkavm::context::Context; use crate::polkavm::Dependency; /// Clamps `value` to `max_value`, if `value` is bigger than `max_value`. pub fn clamp<'ctx, D>( context: &mut Context<'ctx, D>, value: inkwell::values::IntValue<'ctx>, max_value: inkwell::values::IntValue<'ctx>, name: &str, ) -> anyhow::Result> where D: Dependency + Clone, { let in_bounds_block = context.append_basic_block(format!("{name}_is_bounds_block").as_str()); let join_block = context.append_basic_block(format!("{name}_join_block").as_str()); let pointer = context.build_alloca(context.word_type(), format!("{name}_pointer").as_str()); context.build_store(pointer, max_value)?; let is_in_bounds = context.builder().build_int_compare( inkwell::IntPredicate::ULE, value, max_value, format!("{name}_is_in_bounds").as_str(), )?; context.build_conditional_branch(is_in_bounds, in_bounds_block, join_block)?; context.set_basic_block(in_bounds_block); context.build_store(pointer, value)?; context.build_unconditional_branch(join_block); context.set_basic_block(join_block); let result = context.build_load(pointer, name)?; Ok(result.into_int_value()) } /// Generates an exception. pub fn throw(context: &Context) where D: Dependency + Clone, { context.build_call( context.llvm_runtime().cxa_throw, &[context .llvm() .ptr_type(AddressSpace::Stack.into()) .get_undef() .as_basic_value_enum(); 3], LLVMRuntime::FUNCTION_CXA_THROW, ); context.build_unreachable(); } /// Returns the full list of arguments for an external call. /// Performs the extra ABI data padding and adds the mimic call extra argument. pub fn external_call_arguments<'ctx, D>( _context: &Context<'ctx, D>, abi_data: inkwell::values::BasicValueEnum<'ctx>, address: inkwell::values::IntValue<'ctx>, _extra_abi_data: Vec>, mimic: Option>, ) -> Vec> where D: Dependency + Clone, { let mut result = Vec::with_capacity( crate::polkavm::context::function::runtime::entry::Entry::MANDATORY_ARGUMENTS_COUNT + crate::polkavm::EXTRA_ABI_DATA_SIZE + usize::from(mimic.is_some()), ); result.push(abi_data); result.push(address.as_basic_value_enum()); //result.extend( // pad_extra_abi_data(context, extra_abi_data) // .into_iter() // .map(|value| value.as_basic_value_enum()), //); if let Some(mimic) = mimic { result.push(mimic.as_basic_value_enum()); } result } /// Generates an ABI data for an external call. /// If `gas` is `None`, it is fetched from the contract context. pub fn abi_data<'ctx, D>( context: &mut Context<'ctx, D>, input_offset: inkwell::values::IntValue<'ctx>, input_length: inkwell::values::IntValue<'ctx>, gas: Option>, address_space: AddressSpace, is_system_call: bool, ) -> anyhow::Result> where D: Dependency + Clone, { let input_offset = crate::polkavm::utils::clamp( context, input_offset, context.word_const(u32::MAX as u64), "abi_data_input_offset", )?; let input_length = crate::polkavm::utils::clamp( context, input_length, context.word_const(u32::MAX as u64), "abi_data_input_length", )?; let gas = match gas { Some(gas) => gas, None => crate::polkavm::evm::ether_gas::gas(context)?.into_int_value(), }; let gas = crate::polkavm::utils::clamp( context, gas, context.word_const(u32::MAX as u64), "abi_data_gas", )?; let input_offset_shifted = context.builder().build_left_shift( input_offset, context.word_const((revive_common::BIT_LENGTH_X32 * 2) as u64), "abi_data_input_offset_shifted", )?; let input_length_shifted = context.builder().build_left_shift( input_length, context.word_const((revive_common::BIT_LENGTH_X32 * 3) as u64), "abi_data_input_length_shifted", )?; let gas_shifted = context.builder().build_left_shift( gas, context.word_const((revive_common::BIT_LENGTH_X32 * 6) as u64), "abi_data_gas_shifted", )?; let mut abi_data = context.builder().build_int_add( input_offset_shifted, input_length_shifted, "abi_data_offset_and_length", )?; abi_data = context .builder() .build_int_add(abi_data, gas_shifted, "abi_data_add_gas")?; if let AddressSpace::HeapAuxiliary = address_space { let auxiliary_heap_marker_shifted = context.builder().build_left_shift( context.word_const(zkevm_opcode_defs::FarCallForwardPageType::UseAuxHeap as u64), context.word_const((revive_common::BIT_LENGTH_X32 * 7) as u64), "abi_data_auxiliary_heap_marker_shifted", )?; abi_data = context.builder().build_int_add( abi_data, auxiliary_heap_marker_shifted, "abi_data_add_heap_auxiliary_marker", )?; } if is_system_call { let auxiliary_heap_marker_shifted = context.builder().build_left_shift( context.word_const(zkevm_opcode_defs::FarCallForwardPageType::UseAuxHeap as u64), context.word_const( ((revive_common::BIT_LENGTH_X32 * 7) + (revive_common::BIT_LENGTH_BYTE * 3)) as u64, ), "abi_data_system_call_marker_shifted", )?; abi_data = context.builder().build_int_add( abi_data, auxiliary_heap_marker_shifted, "abi_data_add_system_call_marker", )?; } Ok(abi_data.as_basic_value_enum()) } /// Pads the extra ABI data with `i256::undef`, so it always consists of 10 values. pub fn pad_extra_abi_data<'ctx, D>( context: &Context<'ctx, D>, initial_data: Vec>, ) -> [inkwell::values::IntValue<'ctx>; crate::polkavm::EXTRA_ABI_DATA_SIZE] where D: Dependency + Clone, { let mut padded_data = initial_data; padded_data.extend(vec![ context.word_undef(); crate::polkavm::EXTRA_ABI_DATA_SIZE - padded_data.len() ]); padded_data.try_into().expect("Always valid") } /// Computes the `keccak256` hash for `preimage`. pub fn keccak256(preimage: &[u8]) -> String { use sha3::Digest; let hash_bytes = sha3::Keccak256::digest(preimage); hash_bytes .into_iter() .map(|byte| format!("{byte:02x}")) .collect::>() .join("") } #[cfg(test)] mod tests { #[test] fn keccak256() { assert_eq!( super::keccak256("zksync".as_bytes()), "0238fb1ab06c28c32885f9a4842207ac480c2467df26b6c58e201679628c5a5b" ); } }