mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-04-25 00:07:58 +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:
@@ -33,9 +33,6 @@ pub static GLOBAL_CALL_FLAGS: &str = "call_flags";
|
||||
/// The extra ABI data global variable name.
|
||||
pub static GLOBAL_EXTRA_ABI_DATA: &str = "extra_abi_data";
|
||||
|
||||
/// The active pointer global variable name.
|
||||
pub static GLOBAL_ACTIVE_POINTER: &str = "ptr_active";
|
||||
|
||||
/// The constant array global variable name prefix.
|
||||
pub static GLOBAL_CONST_ARRAY_PREFIX: &str = "const_array_";
|
||||
|
||||
@@ -74,10 +71,5 @@ pub const NO_SYSTEM_CALL_BIT: bool = false;
|
||||
pub const SYSTEM_CALL_BIT: bool = true;
|
||||
|
||||
/// The deployer call header size that consists of:
|
||||
/// - selector (4 bytes)
|
||||
/// - salt (32 bytes)
|
||||
/// - bytecode hash (32 bytes)
|
||||
/// - constructor arguments offset (32 bytes)
|
||||
/// - constructor arguments length (32 bytes)
|
||||
pub const DEPLOYER_CALL_HEADER_SIZE: usize =
|
||||
revive_common::BYTE_LENGTH_X32 + (revive_common::BYTE_LENGTH_WORD * 4);
|
||||
pub const DEPLOYER_CALL_HEADER_SIZE: usize = revive_common::BYTE_LENGTH_WORD;
|
||||
|
||||
@@ -24,6 +24,8 @@ pub static HASH_KECCAK_256: &str = "hash_keccak_256";
|
||||
|
||||
pub static INPUT: &str = "input";
|
||||
|
||||
pub static INSTANTIATE: &str = "instantiate";
|
||||
|
||||
pub static NOW: &str = "now";
|
||||
|
||||
pub static RETURN: &str = "seal_return";
|
||||
|
||||
@@ -128,7 +128,7 @@ impl<'ctx> Function<'ctx> {
|
||||
) {
|
||||
for attribute_kind in attributes.into_iter() {
|
||||
match attribute_kind {
|
||||
Attribute::Memory => todo!("`memory` attributes are not yet implemented"),
|
||||
Attribute::Memory => unimplemented!("`memory` attributes are not implemented"),
|
||||
attribute_kind @ Attribute::AlwaysInline if force => {
|
||||
let is_optimize_none_set = declaration
|
||||
.value
|
||||
|
||||
@@ -1,324 +0,0 @@
|
||||
//! The `deployer_call` function.
|
||||
|
||||
use inkwell::types::BasicType;
|
||||
|
||||
use crate::polkavm::context::address_space::AddressSpace;
|
||||
use crate::polkavm::context::function::Function;
|
||||
use crate::polkavm::context::pointer::Pointer;
|
||||
use crate::polkavm::context::Context;
|
||||
use crate::polkavm::Dependency;
|
||||
use crate::polkavm::WriteLLVM;
|
||||
|
||||
/// The `deployer_call` function.
|
||||
/// Calls the deployer system contract, which returns the newly deployed contract address or 0.
|
||||
/// The address is returned in the first 32-byte word of the return data. If it is 0, the 0 is
|
||||
/// returned. If the entire call has failed, there is also a 0 returned.
|
||||
#[derive(Debug)]
|
||||
pub struct DeployerCall {
|
||||
/// The address space where the calldata is allocated.
|
||||
/// Solidity uses the ordinary heap. Vyper uses the auxiliary heap.
|
||||
address_space: AddressSpace,
|
||||
}
|
||||
|
||||
impl DeployerCall {
|
||||
/// The default function name.
|
||||
pub const FUNCTION_NAME: &'static str = "__deployer_call";
|
||||
|
||||
/// The value argument index.
|
||||
pub const ARGUMENT_INDEX_VALUE: usize = 0;
|
||||
|
||||
/// The input offset argument index.
|
||||
pub const ARGUMENT_INDEX_INPUT_OFFSET: usize = 1;
|
||||
|
||||
/// The input length argument index.
|
||||
pub const ARGUMENT_INDEX_INPUT_LENGTH: usize = 2;
|
||||
|
||||
/// The signature hash argument index.
|
||||
pub const ARGUMENT_INDEX_SIGNATURE_HASH: usize = 3;
|
||||
|
||||
/// The salt argument index.
|
||||
pub const ARGUMENT_INDEX_SALT: usize = 4;
|
||||
|
||||
/// A shortcut constructor.
|
||||
pub fn new(address_space: AddressSpace) -> Self {
|
||||
Self { address_space }
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> WriteLLVM<D> for DeployerCall
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||
let function_type = context.function_type(
|
||||
vec![
|
||||
context.word_type().as_basic_type_enum(),
|
||||
context.word_type().as_basic_type_enum(),
|
||||
context.word_type().as_basic_type_enum(),
|
||||
context.word_type().as_basic_type_enum(),
|
||||
context.word_type().as_basic_type_enum(),
|
||||
],
|
||||
1,
|
||||
false,
|
||||
);
|
||||
let function = context.add_function(
|
||||
Self::FUNCTION_NAME,
|
||||
function_type,
|
||||
1,
|
||||
Some(inkwell::module::Linkage::External),
|
||||
)?;
|
||||
Function::set_frontend_runtime_attributes(
|
||||
context.llvm,
|
||||
function.borrow().declaration(),
|
||||
&context.optimizer,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||
context.set_current_function(Self::FUNCTION_NAME)?;
|
||||
|
||||
let value = context
|
||||
.current_function()
|
||||
.borrow()
|
||||
.get_nth_param(Self::ARGUMENT_INDEX_VALUE)
|
||||
.into_int_value();
|
||||
let input_offset = context
|
||||
.current_function()
|
||||
.borrow()
|
||||
.get_nth_param(Self::ARGUMENT_INDEX_INPUT_OFFSET)
|
||||
.into_int_value();
|
||||
let input_length = context
|
||||
.current_function()
|
||||
.borrow()
|
||||
.get_nth_param(Self::ARGUMENT_INDEX_INPUT_LENGTH)
|
||||
.into_int_value();
|
||||
let signature_hash = context
|
||||
.current_function()
|
||||
.borrow()
|
||||
.get_nth_param(Self::ARGUMENT_INDEX_SIGNATURE_HASH)
|
||||
.into_int_value();
|
||||
let salt = context
|
||||
.current_function()
|
||||
.borrow()
|
||||
.get_nth_param(Self::ARGUMENT_INDEX_SALT)
|
||||
.into_int_value();
|
||||
|
||||
let error_block = context.append_basic_block("deployer_call_error_block");
|
||||
let success_block = context.append_basic_block("deployer_call_success_block");
|
||||
let value_zero_block = context.append_basic_block("deployer_call_value_zero_block");
|
||||
let value_non_zero_block = context.append_basic_block("deployer_call_value_non_zero_block");
|
||||
let value_join_block = context.append_basic_block("deployer_call_value_join_block");
|
||||
|
||||
context.set_basic_block(context.current_function().borrow().entry_block());
|
||||
let _abi_data = crate::polkavm::utils::abi_data(
|
||||
context,
|
||||
input_offset,
|
||||
input_length,
|
||||
None,
|
||||
self.address_space,
|
||||
true,
|
||||
)?;
|
||||
|
||||
let signature_pointer = Pointer::new_with_offset(
|
||||
context,
|
||||
self.address_space,
|
||||
context.word_type(),
|
||||
input_offset,
|
||||
"deployer_call_signature_pointer",
|
||||
);
|
||||
context.build_store(signature_pointer, signature_hash)?;
|
||||
|
||||
let salt_offset = context.builder().build_int_add(
|
||||
input_offset,
|
||||
context.word_const(revive_common::BYTE_LENGTH_X32 as u64),
|
||||
"deployer_call_salt_offset",
|
||||
)?;
|
||||
let salt_pointer = Pointer::new_with_offset(
|
||||
context,
|
||||
self.address_space,
|
||||
context.word_type(),
|
||||
salt_offset,
|
||||
"deployer_call_salt_pointer",
|
||||
);
|
||||
context.build_store(salt_pointer, salt)?;
|
||||
|
||||
let arguments_offset_offset = context.builder().build_int_add(
|
||||
salt_offset,
|
||||
context.word_const((revive_common::BYTE_LENGTH_WORD * 2) as u64),
|
||||
"deployer_call_arguments_offset_offset",
|
||||
)?;
|
||||
let arguments_offset_pointer = Pointer::new_with_offset(
|
||||
context,
|
||||
self.address_space,
|
||||
context.word_type(),
|
||||
arguments_offset_offset,
|
||||
"deployer_call_arguments_offset_pointer",
|
||||
);
|
||||
context.build_store(
|
||||
arguments_offset_pointer,
|
||||
context.word_const(
|
||||
(crate::polkavm::DEPLOYER_CALL_HEADER_SIZE
|
||||
- (revive_common::BYTE_LENGTH_X32 + revive_common::BYTE_LENGTH_WORD))
|
||||
as u64,
|
||||
),
|
||||
)?;
|
||||
|
||||
let arguments_length_offset = context.builder().build_int_add(
|
||||
arguments_offset_offset,
|
||||
context.word_const(revive_common::BYTE_LENGTH_WORD as u64),
|
||||
"deployer_call_arguments_length_offset",
|
||||
)?;
|
||||
let arguments_length_pointer = Pointer::new_with_offset(
|
||||
context,
|
||||
self.address_space,
|
||||
context.word_type(),
|
||||
arguments_length_offset,
|
||||
"deployer_call_arguments_length_pointer",
|
||||
);
|
||||
let arguments_length_value = context.builder().build_int_sub(
|
||||
input_length,
|
||||
context.word_const(crate::polkavm::DEPLOYER_CALL_HEADER_SIZE as u64),
|
||||
"deployer_call_arguments_length",
|
||||
)?;
|
||||
context.build_store(arguments_length_pointer, arguments_length_value)?;
|
||||
|
||||
let result_pointer =
|
||||
context.build_alloca(context.word_type(), "deployer_call_result_pointer");
|
||||
context.build_store(result_pointer, context.word_const(0))?;
|
||||
let deployer_call_result_type = context.structure_type(&[
|
||||
context
|
||||
.llvm()
|
||||
.ptr_type(AddressSpace::Generic.into())
|
||||
.as_basic_type_enum(),
|
||||
context.bool_type().as_basic_type_enum(),
|
||||
]);
|
||||
let deployer_call_result_pointer =
|
||||
context.build_alloca(deployer_call_result_type, "deployer_call_result_pointer");
|
||||
context.build_store(
|
||||
deployer_call_result_pointer,
|
||||
deployer_call_result_type.const_zero(),
|
||||
)?;
|
||||
let is_value_zero = context.builder().build_int_compare(
|
||||
inkwell::IntPredicate::EQ,
|
||||
value,
|
||||
context.word_const(0),
|
||||
"deployer_call_is_value_zero",
|
||||
)?;
|
||||
context.build_conditional_branch(is_value_zero, value_zero_block, value_non_zero_block)?;
|
||||
|
||||
context.set_basic_block(value_zero_block);
|
||||
//let deployer_call_result = context
|
||||
// .build_call(
|
||||
// context.llvm_runtime().far_call,
|
||||
// crate::polkavm::utils::external_call_arguments(
|
||||
// context,
|
||||
// abi_data,
|
||||
// context.field_const(zkevm_opcode_defs::ADDRESS_CONTRACT_DEPLOYER.into()),
|
||||
// vec![],
|
||||
// None,
|
||||
// )
|
||||
// .as_slice(),
|
||||
// "deployer_call_ordinary",
|
||||
// )
|
||||
// .expect("Always returns a value");
|
||||
//context.build_store(deployer_call_result_pointer, deployer_call_result)?;
|
||||
context.build_unconditional_branch(value_join_block);
|
||||
|
||||
context.set_basic_block(value_non_zero_block);
|
||||
//let deployer_call_result = context
|
||||
// .build_call(
|
||||
// context.llvm_runtime().far_call,
|
||||
// crate::polkavm::utils::external_call_arguments(
|
||||
// context,
|
||||
// abi_data.as_basic_value_enum(),
|
||||
// context.field_const(zkevm_opcode_defs::ADDRESS_MSG_VALUE.into()),
|
||||
// vec![
|
||||
// value,
|
||||
// context.field_const(zkevm_opcode_defs::ADDRESS_CONTRACT_DEPLOYER.into()),
|
||||
// context.field_const(u64::from(crate::polkavm::r#const::SYSTEM_CALL_BIT)),
|
||||
// ],
|
||||
// None,
|
||||
// )
|
||||
// .as_slice(),
|
||||
// "deployer_call_system",
|
||||
// )
|
||||
// .expect("Always returns a value");
|
||||
//context.build_store(deployer_call_result_pointer, deployer_call_result)?;
|
||||
context.build_unconditional_branch(value_join_block);
|
||||
|
||||
context.set_basic_block(value_join_block);
|
||||
let result_abi_data_pointer = context.build_gep(
|
||||
deployer_call_result_pointer,
|
||||
&[
|
||||
context.word_const(0),
|
||||
context
|
||||
.integer_type(revive_common::BIT_LENGTH_X32)
|
||||
.const_zero(),
|
||||
],
|
||||
context
|
||||
.llvm()
|
||||
.ptr_type(AddressSpace::Generic.into())
|
||||
.as_basic_type_enum(),
|
||||
"deployer_call_result_abi_data_pointer",
|
||||
);
|
||||
let result_abi_data =
|
||||
context.build_load(result_abi_data_pointer, "deployer_call_result_abi_data")?;
|
||||
|
||||
let result_status_code_pointer = context.build_gep(
|
||||
deployer_call_result_pointer,
|
||||
&[
|
||||
context.word_const(0),
|
||||
context
|
||||
.integer_type(revive_common::BIT_LENGTH_X32)
|
||||
.const_int(1, false),
|
||||
],
|
||||
context.bool_type().as_basic_type_enum(),
|
||||
"contract_call_external_result_status_code_pointer",
|
||||
);
|
||||
let result_status_code_boolean = context
|
||||
.build_load(
|
||||
result_status_code_pointer,
|
||||
"contract_call_external_result_status_code_boolean",
|
||||
)?
|
||||
.into_int_value();
|
||||
|
||||
context.build_conditional_branch(result_status_code_boolean, success_block, error_block)?;
|
||||
|
||||
context.set_basic_block(success_block);
|
||||
let result_abi_data_pointer = Pointer::new(
|
||||
context.word_type(),
|
||||
AddressSpace::Generic,
|
||||
result_abi_data.into_pointer_value(),
|
||||
);
|
||||
let address_or_status_code = context.build_load(
|
||||
result_abi_data_pointer,
|
||||
"deployer_call_address_or_status_code",
|
||||
)?;
|
||||
context.build_store(result_pointer, address_or_status_code)?;
|
||||
context.build_unconditional_branch(context.current_function().borrow().return_block());
|
||||
|
||||
context.set_basic_block(error_block);
|
||||
let result_abi_data_pointer = Pointer::new(
|
||||
context.byte_type(),
|
||||
AddressSpace::Generic,
|
||||
result_abi_data.into_pointer_value(),
|
||||
);
|
||||
context.write_abi_pointer(
|
||||
result_abi_data_pointer,
|
||||
crate::polkavm::GLOBAL_RETURN_DATA_POINTER,
|
||||
);
|
||||
context.write_abi_data_size(
|
||||
result_abi_data_pointer,
|
||||
crate::polkavm::GLOBAL_RETURN_DATA_SIZE,
|
||||
);
|
||||
context.build_unconditional_branch(context.current_function().borrow().return_block());
|
||||
|
||||
context.set_basic_block(context.current_function().borrow().return_block());
|
||||
let result = context.build_load(result_pointer, "deployer_call_result")?;
|
||||
context.build_return(Some(&result));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
//! The entry function.
|
||||
|
||||
use inkwell::types::BasicType;
|
||||
use inkwell::values::BasicValue;
|
||||
|
||||
use crate::polkavm::context::address_space::AddressSpace;
|
||||
use crate::polkavm::context::function::runtime::Runtime;
|
||||
@@ -8,7 +9,6 @@ use crate::polkavm::context::Context;
|
||||
use crate::polkavm::r#const::*;
|
||||
use crate::polkavm::Dependency;
|
||||
use crate::polkavm::WriteLLVM;
|
||||
use crate::PolkaVMPointer as Pointer;
|
||||
|
||||
/// The entry function.
|
||||
/// The function is a wrapper managing the runtime and deploy code calling logic.
|
||||
@@ -23,8 +23,11 @@ impl Entry {
|
||||
/// The number of mandatory arguments.
|
||||
pub const MANDATORY_ARGUMENTS_COUNT: usize = 2;
|
||||
|
||||
/// Reserve 1kb for calldata.
|
||||
pub const MAX_CALLDATA_SIZE: usize = 1024;
|
||||
/// Reserve 1mb for calldata.
|
||||
pub const MAX_CALLDATA_SIZE: usize = 1024 * 1024;
|
||||
|
||||
/// Reserve 1mb for returndata.
|
||||
pub const MAX_RETURNDATA_SIZE: usize = 1024 * 1024;
|
||||
|
||||
/// Initializes the global variables.
|
||||
/// The pointers are not initialized, because it's not possible to create a null pointer.
|
||||
@@ -40,6 +43,14 @@ impl Entry {
|
||||
calldata_type.get_undef(),
|
||||
);
|
||||
|
||||
let returndata_type = context.array_type(context.byte_type(), Self::MAX_RETURNDATA_SIZE);
|
||||
context.set_global(
|
||||
crate::polkavm::GLOBAL_RETURN_DATA_POINTER,
|
||||
returndata_type,
|
||||
AddressSpace::Stack,
|
||||
returndata_type.get_undef(),
|
||||
);
|
||||
|
||||
context.set_global(
|
||||
crate::polkavm::GLOBAL_HEAP_MEMORY_POINTER,
|
||||
context.llvm().ptr_type(AddressSpace::Generic.into()),
|
||||
@@ -61,9 +72,9 @@ impl Entry {
|
||||
);
|
||||
context.set_global(
|
||||
crate::polkavm::GLOBAL_RETURN_DATA_SIZE,
|
||||
context.word_type(),
|
||||
context.xlen_type(),
|
||||
AddressSpace::Stack,
|
||||
context.word_const(0),
|
||||
context.xlen_type().const_zero().as_basic_value_enum(),
|
||||
);
|
||||
|
||||
context.set_global(
|
||||
@@ -162,27 +173,6 @@ impl Entry {
|
||||
calldata_size_casted,
|
||||
);
|
||||
|
||||
// Store calldata end pointer
|
||||
let input_pointer = Pointer::new(
|
||||
input_pointer.get_type(),
|
||||
AddressSpace::Generic,
|
||||
input_pointer,
|
||||
);
|
||||
let calldata_end_pointer = context.build_gep(
|
||||
input_pointer,
|
||||
&[calldata_size_casted],
|
||||
context
|
||||
.llvm()
|
||||
.ptr_type(AddressSpace::Generic.into())
|
||||
.as_basic_type_enum(),
|
||||
"return_data_abi_initializer",
|
||||
);
|
||||
context.write_abi_pointer(
|
||||
calldata_end_pointer,
|
||||
crate::polkavm::GLOBAL_RETURN_DATA_POINTER,
|
||||
);
|
||||
context.write_abi_pointer(calldata_end_pointer, crate::polkavm::GLOBAL_ACTIVE_POINTER);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
pub mod default_call;
|
||||
pub mod deploy_code;
|
||||
pub mod deployer_call;
|
||||
pub mod entry;
|
||||
pub mod runtime_code;
|
||||
|
||||
@@ -13,14 +12,13 @@ use crate::polkavm::Dependency;
|
||||
use crate::polkavm::WriteLLVM;
|
||||
|
||||
use self::default_call::DefaultCall;
|
||||
use self::deployer_call::DeployerCall;
|
||||
|
||||
/// The front-end runtime functions.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Runtime {
|
||||
/// The address space where the calldata is allocated.
|
||||
/// Solidity uses the ordinary heap. Vyper uses the auxiliary heap.
|
||||
address_space: AddressSpace,
|
||||
_address_space: AddressSpace,
|
||||
}
|
||||
|
||||
impl Runtime {
|
||||
@@ -34,8 +32,8 @@ impl Runtime {
|
||||
pub const FUNCTION_RUNTIME_CODE: &'static str = "__runtime";
|
||||
|
||||
/// A shortcut constructor.
|
||||
pub fn new(address_space: AddressSpace) -> Self {
|
||||
Self { address_space }
|
||||
pub fn new(_address_space: AddressSpace) -> Self {
|
||||
Self { _address_space }
|
||||
}
|
||||
|
||||
/// Returns the corresponding runtime function.
|
||||
@@ -52,18 +50,6 @@ impl Runtime {
|
||||
.borrow()
|
||||
.declaration()
|
||||
}
|
||||
|
||||
/// Returns the corresponding runtime function.
|
||||
pub fn deployer_call<'ctx, D>(context: &Context<'ctx, D>) -> FunctionDeclaration<'ctx>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
context
|
||||
.get_function(DeployerCall::FUNCTION_NAME)
|
||||
.expect("Always exists")
|
||||
.borrow()
|
||||
.declaration()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> WriteLLVM<D> for Runtime
|
||||
@@ -74,7 +60,6 @@ where
|
||||
//DefaultCall::new(context.llvm_runtime().far_call).declare(context)?;
|
||||
DefaultCall::new(context.llvm_runtime().static_call).declare(context)?;
|
||||
DefaultCall::new(context.llvm_runtime().delegate_call).declare(context)?;
|
||||
DeployerCall::new(self.address_space).declare(context)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -83,7 +68,6 @@ where
|
||||
//DefaultCall::new(context.llvm_runtime().far_call).into_llvm(context)?;
|
||||
DefaultCall::new(context.llvm_runtime().static_call).into_llvm(context)?;
|
||||
DefaultCall::new(context.llvm_runtime().delegate_call).into_llvm(context)?;
|
||||
DeployerCall::new(self.address_space).into_llvm(context)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -136,7 +136,9 @@ where
|
||||
module: &inkwell::module::Module<'ctx>,
|
||||
) {
|
||||
module
|
||||
.link_in_module(pallet_contracts_pvm_llapi::module(llvm, "polkavm_guest").unwrap())
|
||||
.link_in_module(
|
||||
pallet_contracts_pvm_llapi::polkavm_guest::module(llvm, "polkavm_guest").unwrap(),
|
||||
)
|
||||
.expect("the PolkaVM guest API module should be linkable");
|
||||
|
||||
for export in runtime_api::EXPORTS {
|
||||
@@ -164,7 +166,7 @@ where
|
||||
size: u32,
|
||||
) {
|
||||
module
|
||||
.link_in_module(pallet_contracts_pvm_llapi::min_stack_size(
|
||||
.link_in_module(pallet_contracts_pvm_llapi::polkavm_guest::min_stack_size(
|
||||
llvm,
|
||||
"polkavm_stack_size",
|
||||
size,
|
||||
@@ -1139,26 +1141,13 @@ where
|
||||
self.builder.build_unreachable().unwrap();
|
||||
}
|
||||
|
||||
/// Builds a long contract exit sequence.
|
||||
/// The deploy code does not return the runtime code like in EVM. Instead, it returns some
|
||||
/// additional contract metadata, e.g. the array of immutables.
|
||||
/// The deploy code uses the auxiliary heap for the return, because otherwise it is not possible
|
||||
/// to allocate memory together with the Yul allocator safely.
|
||||
/// Builds a contract exit sequence.
|
||||
pub fn build_exit(
|
||||
&self,
|
||||
flags: inkwell::values::IntValue<'ctx>,
|
||||
offset: inkwell::values::IntValue<'ctx>,
|
||||
length: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<()> {
|
||||
// TODO:
|
||||
//let return_forward_mode = if self.code_type() == Some(CodeType::Deploy)
|
||||
// && return_function == self.llvm_runtime().r#return
|
||||
//{
|
||||
// zkevm_opcode_defs::RetForwardPageType::UseAuxHeap
|
||||
//} else {
|
||||
// zkevm_opcode_defs::RetForwardPageType::UseHeap
|
||||
//};
|
||||
|
||||
let offset_truncated = self.safe_truncate_int_to_xlen(offset)?;
|
||||
let length_truncated = self.safe_truncate_int_to_xlen(length)?;
|
||||
let offset_into_heap = self.build_heap_gep(offset_truncated, length_truncated)?;
|
||||
|
||||
@@ -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")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ pub mod metadata_hash;
|
||||
pub mod utils;
|
||||
|
||||
pub use self::r#const::*;
|
||||
use self::utils::keccak256;
|
||||
|
||||
use crate::debug_config::DebugConfig;
|
||||
use crate::optimizer::settings::Settings as OptimizerSettings;
|
||||
@@ -56,7 +57,7 @@ pub fn build_assembly_text(
|
||||
assembly_text.to_owned(),
|
||||
metadata_hash,
|
||||
bytecode.to_owned(),
|
||||
Default::default(),
|
||||
keccak256(bytecode),
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user