mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-06-16 06:11:08 +00:00
Constructors and contract creation (#11)
Implement constructor logic and support create/create2 in the mock runtime Signed-off-by: xermicus <cyrill@parity.io>
This commit is contained in:
@@ -5,9 +5,9 @@ use num::Zero;
|
||||
|
||||
use crate::polkavm::context::argument::Argument;
|
||||
use crate::polkavm::context::code_type::CodeType;
|
||||
use crate::polkavm::context::function::runtime::Runtime;
|
||||
use crate::polkavm::context::Context;
|
||||
use crate::polkavm::Dependency;
|
||||
use crate::polkavm_const::runtime_api;
|
||||
|
||||
/// Translates the contract `create` instruction.
|
||||
/// The instruction is simulated by a call to a system contract.
|
||||
@@ -20,32 +20,10 @@ pub fn create<'ctx, D>(
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
let signature_hash_string =
|
||||
crate::polkavm::utils::keccak256(crate::polkavm::DEPLOYER_SIGNATURE_CREATE.as_bytes());
|
||||
let signature_hash = context.word_const_str_hex(signature_hash_string.as_str());
|
||||
|
||||
let salt = context.word_const(0);
|
||||
|
||||
let function = Runtime::deployer_call(context);
|
||||
let result = context
|
||||
.build_call(
|
||||
function,
|
||||
&[
|
||||
value.as_basic_value_enum(),
|
||||
input_offset.as_basic_value_enum(),
|
||||
input_length.as_basic_value_enum(),
|
||||
signature_hash.as_basic_value_enum(),
|
||||
salt.as_basic_value_enum(),
|
||||
],
|
||||
"create_deployer_call",
|
||||
)
|
||||
.expect("Always exists");
|
||||
|
||||
Ok(result)
|
||||
self::create2(context, value, input_offset, input_length, None)
|
||||
}
|
||||
|
||||
/// Translates the contract `create2` instruction.
|
||||
/// The instruction is simulated by a call to a system contract.
|
||||
pub fn create2<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
value: inkwell::values::IntValue<'ctx>,
|
||||
@@ -56,28 +34,72 @@ pub fn create2<'ctx, D>(
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
let signature_hash_string =
|
||||
crate::polkavm::utils::keccak256(crate::polkavm::DEPLOYER_SIGNATURE_CREATE2.as_bytes());
|
||||
let signature_hash = context.word_const_str_hex(signature_hash_string.as_str());
|
||||
let input_offset = context.safe_truncate_int_to_xlen(input_offset)?;
|
||||
let input_length = context.safe_truncate_int_to_xlen(input_length)?;
|
||||
|
||||
let salt = salt.unwrap_or_else(|| context.word_const(0));
|
||||
let value_pointer = context.build_alloca(context.value_type(), "value");
|
||||
context.build_store(value_pointer, value)?;
|
||||
|
||||
let function = Runtime::deployer_call(context);
|
||||
let result = context
|
||||
.build_call(
|
||||
function,
|
||||
&[
|
||||
value.as_basic_value_enum(),
|
||||
input_offset.as_basic_value_enum(),
|
||||
input_length.as_basic_value_enum(),
|
||||
signature_hash.as_basic_value_enum(),
|
||||
salt.as_basic_value_enum(),
|
||||
],
|
||||
"create2_deployer_call",
|
||||
)
|
||||
.expect("Always exists");
|
||||
let code_hash_pointer = context.build_heap_gep(input_offset, input_length)?;
|
||||
|
||||
Ok(result)
|
||||
let input_data_pointer = context.build_gep(
|
||||
code_hash_pointer,
|
||||
&[context
|
||||
.xlen_type()
|
||||
.const_int(revive_common::BYTE_LENGTH_WORD as u64, false)],
|
||||
context.byte_type(),
|
||||
"value_ptr_parameter_offset",
|
||||
);
|
||||
|
||||
let salt_pointer = context.build_alloca(context.word_type(), "salt");
|
||||
context.build_store(salt_pointer, salt.unwrap_or_else(|| context.word_const(0)))?;
|
||||
|
||||
let (address_pointer, address_length_pointer) =
|
||||
context.build_stack_parameter(revive_common::BIT_LENGTH_ETH_ADDRESS, "address_pointer");
|
||||
|
||||
let sentinel = context
|
||||
.xlen_type()
|
||||
.const_all_ones()
|
||||
.const_to_pointer(context.llvm().ptr_type(Default::default()));
|
||||
|
||||
let argument_pointer = pallet_contracts_pvm_llapi::calling_convention::Spill::new(
|
||||
context.builder(),
|
||||
pallet_contracts_pvm_llapi::calling_convention::instantiate(context.llvm()),
|
||||
"create2_arguments",
|
||||
)?
|
||||
.next(code_hash_pointer.value)?
|
||||
.skip()
|
||||
.skip()
|
||||
.next(sentinel)?
|
||||
.next(value_pointer.value)?
|
||||
.next(input_data_pointer.value)?
|
||||
.next(input_length)?
|
||||
.next(address_pointer.value)?
|
||||
.next(address_length_pointer.value)?
|
||||
.next(sentinel)?
|
||||
.next(sentinel)?
|
||||
.next(salt_pointer.value)?
|
||||
.next(
|
||||
context
|
||||
.xlen_type()
|
||||
.const_int(revive_common::BYTE_LENGTH_WORD as u64, false),
|
||||
)?
|
||||
.done();
|
||||
|
||||
context.builder().build_direct_call(
|
||||
context.runtime_api_method(runtime_api::INSTANTIATE),
|
||||
&[context
|
||||
.builder()
|
||||
.build_ptr_to_int(argument_pointer, context.xlen_type(), "argument_pointer")?
|
||||
.into()],
|
||||
"create2",
|
||||
)?;
|
||||
|
||||
context.build_load_word(
|
||||
address_pointer,
|
||||
revive_common::BIT_LENGTH_ETH_ADDRESS,
|
||||
"address",
|
||||
)
|
||||
}
|
||||
|
||||
/// Translates the contract hash instruction, which is actually used to set the hash of the contract
|
||||
@@ -121,15 +143,8 @@ where
|
||||
Ok(Argument::new_with_original(hash_value, hash_string))
|
||||
}
|
||||
|
||||
/// Translates the deployer call header size instruction, Usually, the header consists of:
|
||||
/// - the deployer contract method signature
|
||||
/// - the salt if the call is `create2`, or zero if the call is `create1`
|
||||
/// - the hash of the bytecode of the contract whose instance is being created
|
||||
/// - the offset of the constructor arguments
|
||||
/// - the length of the constructor arguments
|
||||
/// If the call is `create1`, the space for the salt is still allocated, because the memory for the
|
||||
/// header is allocated by the Yul or EVM legacy assembly before it is known which version of
|
||||
/// `create` is going to be used.
|
||||
/// Translates the deploy call header size instruction. the header consists of
|
||||
/// the hash of the bytecode of the contract whose instance is being created.
|
||||
/// Represents `datasize` in Yul and `PUSH #[$]` in the EVM legacy assembly.
|
||||
pub fn header_size<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
//! Translates the transaction return operations.
|
||||
|
||||
use crate::polkavm::context::address_space::AddressSpace;
|
||||
use crate::polkavm::context::code_type::CodeType;
|
||||
use crate::polkavm::context::pointer::Pointer;
|
||||
use crate::polkavm::context::Context;
|
||||
use crate::polkavm::Dependency;
|
||||
|
||||
/// Translates the `return` instruction.
|
||||
/// Unlike in EVM, zkSync constructors return the array of contract immutables.
|
||||
pub fn r#return<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
offset: inkwell::values::IntValue<'ctx>,
|
||||
@@ -16,66 +12,15 @@ pub fn r#return<'ctx, D>(
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
match context.code_type() {
|
||||
None => {
|
||||
anyhow::bail!("Return is not available if the contract part is undefined");
|
||||
}
|
||||
Some(CodeType::Deploy) => {
|
||||
let immutables_offset_pointer = Pointer::new_with_offset(
|
||||
context,
|
||||
AddressSpace::HeapAuxiliary,
|
||||
context.word_type(),
|
||||
context.word_const(crate::polkavm::HEAP_AUX_OFFSET_CONSTRUCTOR_RETURN_DATA),
|
||||
"immutables_offset_pointer",
|
||||
);
|
||||
context.build_store(
|
||||
immutables_offset_pointer,
|
||||
context.word_const(revive_common::BYTE_LENGTH_WORD as u64),
|
||||
)?;
|
||||
|
||||
let immutables_number_pointer = Pointer::new_with_offset(
|
||||
context,
|
||||
AddressSpace::HeapAuxiliary,
|
||||
context.word_type(),
|
||||
context.word_const(
|
||||
crate::polkavm::HEAP_AUX_OFFSET_CONSTRUCTOR_RETURN_DATA
|
||||
+ (revive_common::BYTE_LENGTH_WORD as u64),
|
||||
),
|
||||
"immutables_number_pointer",
|
||||
);
|
||||
let immutable_values_size = context.immutables_size()?;
|
||||
context.build_store(
|
||||
immutables_number_pointer,
|
||||
context
|
||||
.word_const((immutable_values_size / revive_common::BYTE_LENGTH_WORD) as u64),
|
||||
)?;
|
||||
let immutables_size = context.builder().build_int_mul(
|
||||
context.word_const(immutable_values_size as u64),
|
||||
context.word_const(2),
|
||||
"immutables_size",
|
||||
)?;
|
||||
let return_data_length = context.builder().build_int_add(
|
||||
immutables_size,
|
||||
context.word_const((revive_common::BYTE_LENGTH_WORD * 2) as u64),
|
||||
"return_data_length",
|
||||
)?;
|
||||
|
||||
context.build_exit(
|
||||
context.integer_const(crate::polkavm::XLEN, 0),
|
||||
context.word_const(crate::polkavm::HEAP_AUX_OFFSET_CONSTRUCTOR_RETURN_DATA),
|
||||
return_data_length,
|
||||
)?;
|
||||
}
|
||||
Some(CodeType::Runtime) => {
|
||||
context.build_exit(
|
||||
context.integer_const(crate::polkavm::XLEN, 0),
|
||||
offset,
|
||||
length,
|
||||
)?;
|
||||
}
|
||||
if context.code_type().is_none() {
|
||||
anyhow::bail!("Return is not available if the contract part is undefined");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
context.build_exit(
|
||||
context.integer_const(crate::polkavm::XLEN, 0),
|
||||
offset,
|
||||
length,
|
||||
)
|
||||
}
|
||||
|
||||
/// Translates the `revert` instruction.
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
//! Translates the return data instructions.
|
||||
|
||||
use inkwell::types::BasicType;
|
||||
use inkwell::values::BasicValue;
|
||||
|
||||
use crate::polkavm::context::address_space::AddressSpace;
|
||||
use crate::polkavm::context::pointer::Pointer;
|
||||
use crate::polkavm::context::Context;
|
||||
use crate::polkavm::Dependency;
|
||||
|
||||
@@ -15,13 +12,19 @@ pub fn size<'ctx, D>(
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
match context.get_global_value(crate::polkavm::GLOBAL_RETURN_DATA_SIZE) {
|
||||
Ok(global) => Ok(global),
|
||||
Err(_error) => Ok(context.word_const(0).as_basic_value_enum()),
|
||||
}
|
||||
let value = context
|
||||
.get_global_value(crate::polkavm::GLOBAL_RETURN_DATA_SIZE)?
|
||||
.into_int_value();
|
||||
Ok(context
|
||||
.builder()
|
||||
.build_int_z_extend(value, context.word_type(), "calldatasize_extended")?
|
||||
.as_basic_value_enum())
|
||||
}
|
||||
|
||||
/// Translates the return data copy.
|
||||
/// Translates the return data copy, trapping if
|
||||
/// - Destination, offset or size exceed the VM register size (XLEN)
|
||||
/// - `source_offset + size` overflows (in XLEN)
|
||||
/// - `source_offset + size` is beyond `RETURNDATASIZE`
|
||||
pub fn copy<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
destination_offset: inkwell::values::IntValue<'ctx>,
|
||||
@@ -31,57 +34,59 @@ pub fn copy<'ctx, D>(
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
let error_block = context.append_basic_block("return_data_copy_error_block");
|
||||
let join_block = context.append_basic_block("return_data_copy_join_block");
|
||||
let source_offset = context.safe_truncate_int_to_xlen(source_offset)?;
|
||||
let destination_offset = context.safe_truncate_int_to_xlen(destination_offset)?;
|
||||
let size = context.safe_truncate_int_to_xlen(size)?;
|
||||
|
||||
let return_data_size = self::size(context)?.into_int_value();
|
||||
let copy_slice_end =
|
||||
context
|
||||
.builder()
|
||||
.build_int_add(source_offset, size, "return_data_copy_slice_end")?;
|
||||
let is_copy_out_of_bounds = context.builder().build_int_compare(
|
||||
let block_copy = context.append_basic_block("copy_block");
|
||||
let block_trap = context.append_basic_block("trap_block");
|
||||
let block_check_out_of_bounds = context.append_basic_block("check_out_of_bounds_block");
|
||||
let is_overflow = context.builder().build_int_compare(
|
||||
inkwell::IntPredicate::UGT,
|
||||
copy_slice_end,
|
||||
return_data_size,
|
||||
"return_data_copy_is_out_of_bounds",
|
||||
source_offset,
|
||||
context.builder().build_int_sub(
|
||||
context.xlen_type().const_all_ones(),
|
||||
size,
|
||||
"offset_plus_size_max_value",
|
||||
)?,
|
||||
"is_returndata_size_out_of_bounds",
|
||||
)?;
|
||||
context.build_conditional_branch(is_copy_out_of_bounds, error_block, join_block)?;
|
||||
context.build_conditional_branch(is_overflow, block_trap, block_check_out_of_bounds)?;
|
||||
|
||||
context.set_basic_block(error_block);
|
||||
crate::polkavm::evm::r#return::revert(context, context.word_const(0), context.word_const(0))?;
|
||||
context.set_basic_block(block_check_out_of_bounds);
|
||||
let is_out_of_bounds = context.builder().build_int_compare(
|
||||
inkwell::IntPredicate::UGT,
|
||||
context.builder().build_int_add(
|
||||
source_offset,
|
||||
context
|
||||
.get_global_value(crate::polkavm::GLOBAL_RETURN_DATA_SIZE)?
|
||||
.into_int_value(),
|
||||
"returndata_end_pointer",
|
||||
)?,
|
||||
context
|
||||
.xlen_type()
|
||||
.const_int(crate::PolkaVMEntryFunction::MAX_CALLDATA_SIZE as u64, false),
|
||||
"is_return_data_copy_overflow",
|
||||
)?;
|
||||
context.build_conditional_branch(is_out_of_bounds, block_trap, block_copy)?;
|
||||
|
||||
context.set_basic_block(join_block);
|
||||
let destination = Pointer::new_with_offset(
|
||||
context,
|
||||
AddressSpace::Heap,
|
||||
context.byte_type(),
|
||||
destination_offset,
|
||||
"return_data_copy_destination_pointer",
|
||||
);
|
||||
|
||||
let return_data_pointer_global =
|
||||
context.get_global(crate::polkavm::GLOBAL_RETURN_DATA_POINTER)?;
|
||||
let return_data_pointer_pointer = return_data_pointer_global.into();
|
||||
let return_data_pointer =
|
||||
context.build_load(return_data_pointer_pointer, "return_data_pointer")?;
|
||||
let source = context.build_gep(
|
||||
Pointer::new(
|
||||
context.byte_type(),
|
||||
return_data_pointer_pointer.address_space,
|
||||
return_data_pointer.into_pointer_value(),
|
||||
),
|
||||
&[source_offset],
|
||||
context.byte_type().as_basic_type_enum(),
|
||||
"return_data_source_pointer",
|
||||
);
|
||||
context.set_basic_block(block_trap);
|
||||
context.build_call(context.intrinsics().trap, &[], "invalid_returndata_copy");
|
||||
context.build_unreachable();
|
||||
|
||||
context.set_basic_block(block_copy);
|
||||
context.build_memcpy(
|
||||
context.intrinsics().memory_copy_from_generic,
|
||||
destination,
|
||||
source,
|
||||
context.build_heap_gep(destination_offset, size)?,
|
||||
context.build_gep(
|
||||
context
|
||||
.get_global(crate::polkavm::GLOBAL_RETURN_DATA_POINTER)?
|
||||
.into(),
|
||||
&[context.xlen_type().const_zero(), source_offset],
|
||||
context.byte_type(),
|
||||
"source_offset_gep",
|
||||
),
|
||||
size,
|
||||
"return_data_copy_memcpy_from_return_data",
|
||||
)?;
|
||||
|
||||
todo!("Build heap GEP to allocate if necessary")
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user