Contract calls (#19)

This commit is contained in:
Cyrill Leutwiler
2024-06-01 20:48:20 +02:00
committed by GitHub
parent 532721f3be
commit 1ba806be1f
27 changed files with 540 additions and 929 deletions
Generated
+25 -25
View File
@@ -47,9 +47,9 @@ dependencies = [
[[package]]
name = "alloy-rlp"
version = "0.3.4"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d58d9f5da7b40e9bfff0b7e7816700be4019db97d4b6359fe7f94a9e22e42ac"
checksum = "b155716bab55763c95ba212806cf43d05bcc70e5f35b02bad20cf5ec7fe11fed"
dependencies = [
"arrayvec",
"bytes",
@@ -68,7 +68,7 @@ dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn 2.0.65",
"syn 2.0.66",
"syn-solidity",
"tiny-keccak",
]
@@ -284,7 +284,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.65",
"syn 2.0.66",
]
[[package]]
@@ -473,7 +473,7 @@ dependencies = [
"heck 0.5.0",
"proc-macro2",
"quote",
"syn 2.0.65",
"syn 2.0.66",
]
[[package]]
@@ -500,9 +500,9 @@ dependencies = [
[[package]]
name = "const-hex"
version = "1.11.4"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ff96486ccc291d36a958107caf2c0af8c78c0af7d31ae2f35ce055130de1a6"
checksum = "94fb8a24a26d37e1ffd45343323dc9fe6654ceea44c12f2fcb3d7ac29e610bc6"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -1047,7 +1047,7 @@ source = "git+https://github.com/TheDan64/inkwell.git?rev=6c0fb56b3554e939f9ca61
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.65",
"syn 2.0.66",
]
[[package]]
@@ -1519,9 +1519,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.83"
version = "1.0.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43"
checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6"
dependencies = [
"unicode-ident",
]
@@ -2013,22 +2013,22 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.202"
version = "1.0.203"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395"
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.202"
version = "1.0.203"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838"
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.65",
"syn 2.0.66",
]
[[package]]
@@ -2200,9 +2200,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.65"
version = "2.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106"
checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5"
dependencies = [
"proc-macro2",
"quote",
@@ -2218,7 +2218,7 @@ dependencies = [
"paste",
"proc-macro2",
"quote",
"syn 2.0.65",
"syn 2.0.66",
]
[[package]]
@@ -2265,7 +2265,7 @@ checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.65",
"syn 2.0.66",
]
[[package]]
@@ -2416,7 +2416,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.65",
"syn 2.0.66",
"wasm-bindgen-shared",
]
@@ -2438,7 +2438,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.65",
"syn 2.0.66",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@@ -2677,14 +2677,14 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.65",
"syn 2.0.66",
]
[[package]]
name = "zeroize"
version = "1.7.0"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
dependencies = [
"zeroize_derive",
]
@@ -2697,7 +2697,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.65",
"syn 2.0.66",
]
[[package]]
+20
View File
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;
contract Call {
function value_transfer(address payable destination) public payable {
destination.transfer(msg.value);
}
function echo(bytes memory payload) public pure returns (bytes memory) {
return payload;
}
function call(
address callee,
bytes memory payload
) public pure returns (bytes memory) {
return Call(callee).echo(payload);
}
}
+49
View File
@@ -135,6 +135,19 @@ sol!(
}
);
sol!(
contract Call {
function value_transfer(address payable destination) public payable;
function echo(bytes memory payload) public payable returns (bytes memory);
function call(
address callee,
bytes memory payload
) public payable returns (bytes memory);
}
);
impl Contract {
/// Execute the contract.
///
@@ -441,6 +454,42 @@ impl Contract {
calldata: MCopy::memcpyCall::new((payload,)).abi_encode(),
}
}
pub fn call_value_transfer(destination: Address) -> Self {
let code = include_str!("../contracts/Call.sol");
let name = "Call";
Self {
name,
evm_runtime: crate::compile_evm_bin_runtime(name, code),
pvm_runtime: crate::compile_blob(name, code),
calldata: Call::value_transferCall::new((destination,)).abi_encode(),
}
}
pub fn call_call(callee: Address, payload: Vec<u8>) -> Self {
let code = include_str!("../contracts/Call.sol");
let name = "Call";
Self {
name,
evm_runtime: crate::compile_evm_bin_runtime(name, code),
pvm_runtime: crate::compile_blob(name, code),
calldata: Call::callCall::new((callee, payload)).abi_encode(),
}
}
pub fn call_constructor() -> Self {
let code = include_str!("../contracts/Call.sol");
let name = "Call";
Self {
name,
evm_runtime: crate::compile_evm_bin_runtime(name, code),
pvm_runtime: crate::compile_blob(name, code),
calldata: Default::default(),
}
}
}
#[cfg(test)]
+165 -17
View File
@@ -82,6 +82,8 @@ struct Frame {
output: CallOutput,
/// The export to call.
export: Export,
/// The returndata from the last contract call.
returndata: Vec<u8>,
}
impl Default for Frame {
@@ -93,6 +95,7 @@ impl Default for Frame {
input: Default::default(),
output: Default::default(),
export: Default::default(),
returndata: Default::default(),
}
}
}
@@ -224,8 +227,8 @@ impl TransactionBuilder {
.unwrap_or_else(|| panic!("contract code not found: {blob_hash}"));
let (mut instance, _) = prepare(code, None);
let export = match self.context.top_frame().export {
Export::Call => runtime_api::CALL,
Export::Deploy(_) => runtime_api::DEPLOY,
Export::Call => runtime_api::exports::CALL,
Export::Deploy(_) => runtime_api::exports::DEPLOY,
};
let export = instance.module().lookup_export(export).unwrap();
self.call_on(&mut instance, export)
@@ -354,7 +357,7 @@ fn link_host_functions(engine: &Engine) -> Linker<Transaction> {
linker
.func_wrap(
runtime_api::INPUT,
runtime_api::imports::INPUT,
|caller: Caller<Transaction>, out_ptr: u32, out_len_ptr: u32| -> Result<(), Trap> {
let (mut caller, transaction) = caller.split();
@@ -371,7 +374,7 @@ fn link_host_functions(engine: &Engine) -> Linker<Transaction> {
linker
.func_wrap(
runtime_api::RETURN,
runtime_api::imports::RETURN,
|caller: Caller<Transaction>,
flags: u32,
data_ptr: u32,
@@ -390,7 +393,7 @@ fn link_host_functions(engine: &Engine) -> Linker<Transaction> {
linker
.func_wrap(
runtime_api::VALUE_TRANSFERRED,
runtime_api::imports::VALUE_TRANSFERRED,
|caller: Caller<Transaction>, out_ptr: u32, out_len_ptr: u32| -> Result<(), Trap> {
let (mut caller, transaction) = caller.split();
@@ -426,7 +429,7 @@ fn link_host_functions(engine: &Engine) -> Linker<Transaction> {
linker
.func_wrap(
runtime_api::SET_STORAGE,
runtime_api::imports::SET_STORAGE,
|caller: Caller<Transaction>,
key_ptr: u32,
key_len: u32,
@@ -461,7 +464,7 @@ fn link_host_functions(engine: &Engine) -> Linker<Transaction> {
linker
.func_wrap(
runtime_api::GET_STORAGE,
runtime_api::imports::GET_STORAGE,
|caller: Caller<Transaction>,
key_ptr: u32,
key_len: u32,
@@ -495,7 +498,7 @@ fn link_host_functions(engine: &Engine) -> Linker<Transaction> {
linker
.func_wrap(
runtime_api::HASH_KECCAK_256,
runtime_api::imports::HASH_KECCAK_256,
|caller: Caller<Transaction>,
input_ptr: u32,
input_len: u32,
@@ -516,7 +519,7 @@ fn link_host_functions(engine: &Engine) -> Linker<Transaction> {
linker
.func_wrap(
runtime_api::NOW,
runtime_api::imports::NOW,
|caller: Caller<Transaction>, out_ptr: u32, out_len_ptr: u32| {
let (mut caller, _) = caller.split();
@@ -537,7 +540,7 @@ fn link_host_functions(engine: &Engine) -> Linker<Transaction> {
linker
.func_wrap(
runtime_api::BLOCK_NUMBER,
runtime_api::imports::BLOCK_NUMBER,
|caller: Caller<Transaction>, out_ptr: u32, out_len_ptr: u32| {
let (mut caller, _) = caller.split();
@@ -558,7 +561,7 @@ fn link_host_functions(engine: &Engine) -> Linker<Transaction> {
linker
.func_wrap(
runtime_api::ADDRESS,
runtime_api::imports::ADDRESS,
|caller: Caller<Transaction>, out_ptr: u32, out_len_ptr: u32| {
let (mut caller, transaction) = caller.split();
@@ -580,7 +583,7 @@ fn link_host_functions(engine: &Engine) -> Linker<Transaction> {
linker
.func_wrap(
runtime_api::CALLER,
runtime_api::imports::CALLER,
|caller: Caller<Transaction>, out_ptr: u32, out_len_ptr: u32| {
let (mut caller, transaction) = caller.split();
@@ -602,7 +605,7 @@ fn link_host_functions(engine: &Engine) -> Linker<Transaction> {
linker
.func_wrap(
runtime_api::DEPOSIT_EVENT,
runtime_api::imports::DEPOSIT_EVENT,
|caller: Caller<Transaction>,
topics_ptr: u32,
topics_len: u32,
@@ -639,7 +642,7 @@ fn link_host_functions(engine: &Engine) -> Linker<Transaction> {
linker
.func_wrap(
runtime_api::INSTANTIATE,
runtime_api::imports::INSTANTIATE,
|caller: Caller<Transaction>, argument_ptr: u32| {
let (mut caller, transaction) = caller.split();
@@ -732,7 +735,122 @@ fn link_host_functions(engine: &Engine) -> Linker<Transaction> {
linker
.func_wrap(
runtime_api::CODE_SIZE,
runtime_api::imports::CALL,
|caller: Caller<Transaction>, argument_ptr: u32| -> Result<u32, Trap> {
let (mut caller, transaction) = caller.split();
#[derive(Debug)]
#[repr(packed)]
struct Arguments {
_flags: u32,
address_ptr: u32,
_ref_time_limit: u64,
proof_size_limit: u64,
deposit_ptr: u32,
value_ptr: u32,
input_data_ptr: u32,
input_data_len: u32,
output_ptr: u32,
output_len_ptr: u32,
}
let mut buffer = [0; std::mem::size_of::<Arguments>()];
caller.read_memory_into_slice(argument_ptr, &mut buffer)?;
let arguments: Arguments = unsafe { std::mem::transmute(buffer) };
assert_eq!({ arguments.proof_size_limit }, 0);
assert_eq!({ arguments.deposit_ptr }, u32::MAX);
let amount = if arguments.value_ptr != u32::MAX {
let value = caller.read_memory_into_vec(arguments.value_ptr, 32)?;
U256::from_le_slice(&value)
} else {
U256::ZERO
};
match transaction.top_account_mut().value.checked_sub(amount) {
Some(deducted) => transaction.top_account_mut().value = deducted,
None => {
log::info!("call failed: insufficient balance {amount}");
return Ok(1);
}
}
let bytes = caller.read_memory_into_vec(arguments.address_ptr, 32)?;
let word = U256::from_le_slice(&bytes);
let address = Address::from_word(word.into());
log::info!("call {address}");
if !transaction.state.accounts.contains_key(&address) {
log::info!(
"balance transfer {amount} from {} to {address}",
transaction.top_frame().callee
);
transaction
.state
.accounts
.entry(address)
.or_insert_with(|| Account {
value: amount,
contract: None,
storage: Default::default(),
});
return Ok(0);
}
if transaction.stack.len() >= Transaction::CALL_STACK_SIZE {
log::info!("deployment faild: maximum stack depth reached");
return Ok(1);
}
let calldata = caller
.read_memory_into_vec(arguments.input_data_ptr, arguments.input_data_len)?;
let (state, output) = transaction
.state
.clone()
.transaction()
.callee(address)
.callvalue(amount)
.calldata(calldata)
.call();
let bytes_to_copy = caller.read_u32(arguments.output_len_ptr)? as usize;
let output_size = output.data.len();
assert!(
bytes_to_copy <= output_size,
"output buffer of {bytes_to_copy}b too small for {output_size}b"
);
transaction.top_frame_mut().returndata = output.data.to_vec();
caller.write_memory(
arguments.output_ptr,
&transaction.top_frame().returndata[..bytes_to_copy],
)?;
caller.write_memory(arguments.output_len_ptr, &output.data.len().to_le_bytes())?;
assert_eq!(
transaction.top_frame().returndata.len(),
caller.read_u32(arguments.output_len_ptr)? as usize
);
let success = if output.flags == ReturnFlags::Success {
log::info!("call succeeded");
transaction.state = state;
0
} else {
log::info!("call failed: callee reverted {:?}", output.flags);
1
};
Ok(success)
},
)
.unwrap();
linker
.func_wrap(
runtime_api::imports::CODE_SIZE,
|caller: Caller<Transaction>, address_ptr: u32| {
let (caller, transaction) = caller.split();
@@ -754,6 +872,36 @@ fn link_host_functions(engine: &Engine) -> Linker<Transaction> {
)
.unwrap();
linker
.func_wrap(
runtime_api::imports::RETURNDATACOPY,
|caller: Caller<Transaction>,
destination_ptr: u32,
offset: u32,
size: u32|
-> Result<(), Trap> {
let (mut caller, transaction) = caller.split();
let offset = offset as usize;
let slice_end = offset
.checked_add(size as usize)
.expect("offset + size overflows");
assert!(
slice_end <= transaction.top_frame().returndata.len(),
"offset + size is larger than RETURNDATASIZE"
);
caller.write_memory(
destination_ptr,
&transaction.top_frame().returndata[offset..slice_end],
)?;
Ok(())
},
)
.unwrap();
linker
}
@@ -772,7 +920,7 @@ pub fn instantiate_module(
module: &Module,
engine: &Engine,
) -> (Instance<Transaction>, ExportIndex) {
let export = module.lookup_export(runtime_api::CALL).unwrap();
let export = module.lookup_export(runtime_api::imports::CALL).unwrap();
let func = link_host_functions(engine).instantiate_pre(module).unwrap();
let instance = func.instantiate().unwrap();
@@ -789,7 +937,7 @@ pub fn prepare(code: &[u8], config: Option<Config>) -> (Instance<Transaction>, E
module_config.set_gas_metering(Some(GasMeteringKind::Sync));
let module = Module::from_blob(&engine, &module_config, blob).unwrap();
let export = module.lookup_export(runtime_api::CALL).unwrap();
let export = module.lookup_export(runtime_api::exports::CALL).unwrap();
let func = link_host_functions(&engine)
.instantiate_pre(&module)
.unwrap();
+31
View File
@@ -523,6 +523,36 @@ fn ext_code_size() {
assert_eq!(received, expected);
}
#[test]
fn value_transfer() {
// Succeeds in remix (shanghai) but traps the interpreter
let (state, _) = assert_success(&Contract::call_value_transfer(Default::default()), false);
assert_eq!(state.accounts().len(), 2);
assert!(state.accounts().get(&Address::default()).is_some());
}
#[test]
fn echo() {
let (state, address) = State::new_deployed(Contract::call_constructor());
let expected = vec![1, 2, 3, 4, 5];
let contract = Contract::call_call(address, expected.clone());
let (_, output) = state
.transaction()
.with_default_account(&contract.pvm_runtime)
.calldata(contract.calldata)
.call();
assert_eq!(output.flags, ReturnFlags::Success);
let received = alloy_primitives::Bytes::abi_decode(&output.data, true)
.unwrap()
.to_vec();
assert_eq!(expected, received);
}
#[test]
fn mcopy() {
let expected = vec![1, 2, 3];
@@ -532,5 +562,6 @@ fn mcopy() {
let received = alloy_primitives::Bytes::abi_decode(&output.data, true)
.unwrap()
.to_vec();
assert_eq!(expected, received);
}
@@ -1,57 +1,68 @@
//! Runtime API import and export symbols.
/// The contract deploy export.
pub static CALL: &str = "call";
pub mod exports {
/// The contract deploy export.
pub static CALL: &str = "call";
/// The contract call export.
pub static DEPLOY: &str = "deploy";
/// The contract call export.
pub static DEPLOY: &str = "deploy";
/// All exported symbols.
/// Useful for configuring common attributes and linkage.
pub static EXPORTS: [&str; 2] = [CALL, DEPLOY];
/// All exported symbols.
/// Useful for configuring common attributes and linkage.
pub static EXPORTS: [&str; 2] = [CALL, DEPLOY];
}
pub static ADDRESS: &str = "address";
pub mod imports {
pub static ADDRESS: &str = "address";
pub static BLOCK_NUMBER: &str = "block_number";
pub static BLOCK_NUMBER: &str = "block_number";
pub static CALLER: &str = "caller";
pub static CALL: &str = "seal_call";
pub static CODE_SIZE: &str = "code_size";
pub static CALLER: &str = "caller";
pub static DEPOSIT_EVENT: &str = "deposit_event";
pub static CODE_SIZE: &str = "code_size";
pub static GET_STORAGE: &str = "get_storage";
pub static DEPOSIT_EVENT: &str = "deposit_event";
pub static HASH_KECCAK_256: &str = "hash_keccak_256";
pub static GET_STORAGE: &str = "get_storage";
pub static INPUT: &str = "input";
pub static HASH_KECCAK_256: &str = "hash_keccak_256";
pub static INSTANTIATE: &str = "instantiate";
pub static INPUT: &str = "input";
pub static NOW: &str = "now";
pub static INSTANTIATE: &str = "instantiate";
pub static RETURN: &str = "seal_return";
pub static NOW: &str = "now";
pub static SET_STORAGE: &str = "set_storage";
pub static RETURN: &str = "seal_return";
pub static VALUE_TRANSFERRED: &str = "value_transferred";
pub static RETURNDATACOPY: &str = "returndatacopy";
/// All imported runtime API symbols..
/// Useful for configuring common attributes and linkage.
pub static IMPORTS: [&str; 12] = [
ADDRESS,
BLOCK_NUMBER,
CALLER,
DEPOSIT_EVENT,
GET_STORAGE,
HASH_KECCAK_256,
INPUT,
INSTANTIATE,
NOW,
RETURN,
SET_STORAGE,
VALUE_TRANSFERRED,
];
pub static SET_STORAGE: &str = "set_storage";
pub static VALUE_TRANSFERRED: &str = "value_transferred";
/// All imported runtime API symbols.
/// Useful for configuring common attributes and linkage.
pub static IMPORTS: [&str; 15] = [
ADDRESS,
BLOCK_NUMBER,
CALL,
CALLER,
CODE_SIZE,
DEPOSIT_EVENT,
GET_STORAGE,
HASH_KECCAK_256,
INPUT,
INSTANTIATE,
NOW,
RETURN,
RETURNDATACOPY,
SET_STORAGE,
VALUE_TRANSFERRED,
];
}
/// PolkaVM __sbrk API symbol to extend the heap memory.
pub static SBRK: &str = "__sbrk";
@@ -11,11 +11,6 @@ use crate::polkavm::context::function::Function;
/// The functions are automatically linked to the LLVM implementations if the signatures match.
#[derive(Debug)]
pub struct LLVMRuntime<'ctx> {
/// The LLVM personality function, used for exception handling.
pub personality: FunctionDeclaration<'ctx>,
/// The LLVM exception throwing function.
pub cxa_throw: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub shl: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
@@ -37,29 +32,6 @@ pub struct LLVMRuntime<'ctx> {
/// The corresponding LLVM runtime function.
pub sha3: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub system_request: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
//pub far_call: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub far_call_byref: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub static_call: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub static_call_byref: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub delegate_call: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub delegate_call_byref: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub mimic_call: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub mimic_call_byref: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub r#return: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
@@ -139,30 +111,6 @@ impl<'ctx> LLVMRuntime<'ctx> {
module: &inkwell::module::Module<'ctx>,
optimizer: &Optimizer,
) -> Self {
let personality = Self::declare(
module,
Self::FUNCTION_PERSONALITY,
llvm.i32_type().fn_type(&[], false),
None,
);
let cxa_throw = Self::declare(
module,
Self::FUNCTION_CXA_THROW,
llvm.void_type().fn_type(
vec![
llvm.ptr_type(AddressSpace::Stack.into())
.as_basic_type_enum()
.into();
3
]
.as_slice(),
false,
),
Some(inkwell::module::Linkage::External),
);
Function::set_cxa_throw_attributes(llvm, cxa_throw);
let shl = Self::declare(
module,
Self::FUNCTION_SHL,
@@ -288,138 +236,6 @@ impl<'ctx> LLVMRuntime<'ctx> {
false,
);
let system_request = Self::declare(
module,
Self::FUNCTION_SYSTEM_REQUEST,
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.fn_type(
vec![
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.as_basic_type_enum()
.into(),
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.as_basic_type_enum()
.into(),
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.as_basic_type_enum()
.into(),
llvm.ptr_type(AddressSpace::Stack.into())
.as_basic_type_enum()
.into(),
]
.as_slice(),
false,
),
Some(inkwell::module::Linkage::External),
);
Function::set_default_attributes(llvm, system_request, optimizer);
let external_call_arguments: Vec<inkwell::types::BasicMetadataTypeEnum> = vec![
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.as_basic_type_enum()
.into();
crate::polkavm::context::function::runtime::entry::Entry::MANDATORY_ARGUMENTS_COUNT
+ crate::polkavm::EXTRA_ABI_DATA_SIZE
];
let mut mimic_call_arguments = external_call_arguments.clone();
mimic_call_arguments.push(
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.as_basic_type_enum()
.into(),
);
let mut external_call_arguments_by_ref: Vec<inkwell::types::BasicMetadataTypeEnum> = vec![
llvm.ptr_type(AddressSpace::Generic.into())
.as_basic_type_enum()
.into(),
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.as_basic_type_enum()
.into(),
];
external_call_arguments_by_ref.extend::<Vec<inkwell::types::BasicMetadataTypeEnum>>(vec![
llvm.custom_width_int_type(
revive_common::BIT_LENGTH_WORD as u32
)
.as_basic_type_enum()
.into();
crate::polkavm::EXTRA_ABI_DATA_SIZE
]);
let mut mimic_call_arguments_by_ref = external_call_arguments_by_ref.clone();
mimic_call_arguments_by_ref.push(
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.as_basic_type_enum()
.into(),
);
let external_call_result_type = llvm
.struct_type(
&[
llvm.ptr_type(AddressSpace::Generic.into())
.as_basic_type_enum(),
llvm.bool_type().as_basic_type_enum(),
],
false,
)
.as_basic_type_enum();
//let far_call = Self::declare(
// module,
// Self::FUNCTION_FARCALL,
// external_call_result_type.fn_type(external_call_arguments.as_slice(), false),
// Some(inkwell::module::Linkage::External),
//);
//Function::set_default_attributes(llvm, far_call, optimizer);
let static_call = Self::declare(
module,
Self::FUNCTION_STATICCALL,
external_call_result_type.fn_type(external_call_arguments.as_slice(), false),
Some(inkwell::module::Linkage::External),
);
Function::set_default_attributes(llvm, static_call, optimizer);
let delegate_call = Self::declare(
module,
Self::FUNCTION_DELEGATECALL,
external_call_result_type.fn_type(external_call_arguments.as_slice(), false),
Some(inkwell::module::Linkage::External),
);
Function::set_default_attributes(llvm, delegate_call, optimizer);
let mimic_call = Self::declare(
module,
Self::FUNCTION_MIMICCALL,
external_call_result_type.fn_type(mimic_call_arguments.as_slice(), false),
Some(inkwell::module::Linkage::External),
);
Function::set_default_attributes(llvm, mimic_call, optimizer);
let far_call_byref = Self::declare(
module,
Self::FUNCTION_FARCALL_BYREF,
external_call_result_type.fn_type(external_call_arguments_by_ref.as_slice(), false),
Some(inkwell::module::Linkage::External),
);
Function::set_default_attributes(llvm, far_call_byref, optimizer);
let static_call_byref = Self::declare(
module,
Self::FUNCTION_STATICCALL_BYREF,
external_call_result_type.fn_type(external_call_arguments_by_ref.as_slice(), false),
Some(inkwell::module::Linkage::External),
);
Function::set_default_attributes(llvm, static_call_byref, optimizer);
let delegate_call_byref = Self::declare(
module,
Self::FUNCTION_DELEGATECALL_BYREF,
external_call_result_type.fn_type(external_call_arguments_by_ref.as_slice(), false),
Some(inkwell::module::Linkage::External),
);
Function::set_default_attributes(llvm, delegate_call_byref, optimizer);
let mimic_call_byref = Self::declare(
module,
Self::FUNCTION_MIMICCALL_BYREF,
external_call_result_type.fn_type(mimic_call_arguments_by_ref.as_slice(), false),
Some(inkwell::module::Linkage::External),
);
Function::set_default_attributes(llvm, mimic_call_byref, optimizer);
let r#return = Self::declare(
module,
Self::FUNCTION_RETURN,
@@ -454,9 +270,6 @@ impl<'ctx> LLVMRuntime<'ctx> {
Function::set_default_attributes(llvm, revert, optimizer);
Self {
personality,
cxa_throw,
shl,
shr,
sar,
@@ -469,18 +282,6 @@ impl<'ctx> LLVMRuntime<'ctx> {
sha3,
system_request,
//far_call,
static_call,
delegate_call,
mimic_call,
far_call_byref,
static_call_byref,
delegate_call_byref,
mimic_call_byref,
r#return,
revert,
}
@@ -506,42 +307,4 @@ impl<'ctx> LLVMRuntime<'ctx> {
value.set_linkage(inkwell::module::Linkage::External);
FunctionDeclaration::new(value.get_type(), value).into()
}
/// Modifies the external call function with `is_byref` and `is_system` modifiers.
pub fn modify(
&self,
function: FunctionDeclaration<'ctx>,
is_byref: bool,
) -> anyhow::Result<FunctionDeclaration<'ctx>> {
let modified = if
/*function == self.far_call {
match is_byref {
false => self.far_call,
true => self.far_call_byref,
}
} else if */
function == self.static_call {
match is_byref {
false => self.static_call,
true => self.static_call_byref,
}
} else if function == self.delegate_call {
match is_byref {
false => self.delegate_call,
true => self.delegate_call_byref,
}
} else if function == self.mimic_call {
match is_byref {
false => self.mimic_call,
true => self.mimic_call_byref,
}
} else {
anyhow::bail!(
"Cannot modify an external call function `{}`",
function.value.get_name().to_string_lossy()
);
};
Ok(modified)
}
}
@@ -1,244 +0,0 @@
//! The `default_call` function.
use inkwell::types::BasicType;
use crate::polkavm::context::function::declaration::Declaration as FunctionDeclaration;
use crate::polkavm::context::function::llvm_runtime::LLVMRuntime;
use crate::polkavm::context::function::Function;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use crate::polkavm::WriteLLVM;
/// The `default_call` function.
/// Generates a default contract call, if the `msg.value` is zero.
#[derive(Debug)]
pub struct DefaultCall {
/// The name of the inner function used for the low-level call.
inner_name: String,
/// The function name with the low-level function name as an element.
name: String,
}
#[allow(unused)]
impl DefaultCall {
/// The gas argument index.
pub const ARGUMENT_INDEX_GAS: usize = 0;
/// The address argument index.
pub const ARGUMENT_INDEX_ADDRESS: usize = 1;
/// The input offset argument index.
pub const ARGUMENT_INDEX_INPUT_OFFSET: usize = 2;
/// The input length argument index.
pub const ARGUMENT_INDEX_INPUT_LENGTH: usize = 3;
/// The output offset argument index.
pub const ARGUMENT_INDEX_OUTPUT_OFFSET: usize = 4;
/// The output length argument index.
pub const ARGUMENT_INDEX_OUTPUT_LENGTH: usize = 5;
/// A shortcut constructor.
pub fn new(call_function: FunctionDeclaration) -> Self {
let inner_name = call_function.value.get_name().to_string_lossy().to_string();
let name = Self::name(call_function);
Self { inner_name, name }
}
/// Returns the function name.
pub fn name(call_function: FunctionDeclaration) -> String {
let suffix = match call_function.value.get_name().to_string_lossy() {
name if name == LLVMRuntime::FUNCTION_FARCALL => "far",
name if name == LLVMRuntime::FUNCTION_STATICCALL => "static",
name if name == LLVMRuntime::FUNCTION_DELEGATECALL => "delegate",
name => panic!("Invalid low-level call inner function `{name}`"),
};
format!("__default_{suffix}_call")
}
/// Returns the low-level call function.
fn inner_function<'ctx, D>(&self, context: &Context<'ctx, D>) -> FunctionDeclaration<'ctx>
where
D: Dependency + Clone,
{
match self.inner_name.as_str() {
//name if name == LLVMRuntime::FUNCTION_FARCALL => context.llvm_runtime().far_call,
name if name == LLVMRuntime::FUNCTION_STATICCALL => context.llvm_runtime().static_call,
name if name == LLVMRuntime::FUNCTION_DELEGATECALL => {
context.llvm_runtime().delegate_call
}
name => panic!("Invalid low-level call inner function `{name}`"),
}
}
}
impl<D> WriteLLVM<D> for DefaultCall
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(),
context.word_type().as_basic_type_enum(),
],
1,
false,
);
let function = context.add_function(
self.name.as_str(),
function_type,
1,
Some(inkwell::module::Linkage::Private),
)?;
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.name.as_str())?;
/*
let gas = context
.current_function()
.borrow()
.get_nth_param(Self::ARGUMENT_INDEX_GAS)
.into_int_value();
let address = context
.current_function()
.borrow()
.get_nth_param(Self::ARGUMENT_INDEX_ADDRESS)
.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 output_offset = context
.current_function()
.borrow()
.get_nth_param(Self::ARGUMENT_INDEX_OUTPUT_OFFSET)
.into_int_value();
let output_length = context
.current_function()
.borrow()
.get_nth_param(Self::ARGUMENT_INDEX_OUTPUT_LENGTH)
.into_int_value();
*/
context.set_basic_block(context.current_function().borrow().entry_block());
let status_code_result_pointer = context.build_alloca(
context.word_type(),
"contract_call_result_status_code_pointer",
);
/*
context.build_store(status_code_result_pointer, context.field_const(0));
let abi_data = crate::polkavm::utils::abi_data(
context,
input_offset,
input_length,
Some(gas),
AddressSpace::Heap,
false,
)?
.into_int_value();
let result = context
.build_call(
self.inner_function(context),
crate::polkavm::utils::external_call_arguments(
context,
abi_data.as_basic_value_enum(),
address,
vec![],
None,
)
.as_slice(),
"contract_call_external",
)
.expect("IntrinsicFunction always returns a flag");
let result_abi_data = context
.builder()
.build_extract_value(
result.into_struct_value(),
0,
"contract_call_external_result_abi_data",
)
.expect("Always exists");
let result_abi_data_pointer = Pointer::new(
context.byte_type(),
AddressSpace::Generic,
result_abi_data.into_pointer_value(),
);
let result_abi_data_casted = result_abi_data_pointer.cast(context.field_type());
let result_status_code_boolean = context
.builder()
.build_extract_value(
result.into_struct_value(),
1,
"contract_call_external_result_status_code_boolean",
)
.expect("Always exists");
let result_status_code = context.builder().build_int_z_extend_or_bit_cast(
result_status_code_boolean.into_int_value(),
context.field_type(),
"contract_call_external_result_status_code",
)?;
context.build_store(status_code_result_pointer, result_status_code);
let source = result_abi_data_casted;
let destination = Pointer::new_with_offset(
context,
AddressSpace::Heap,
context.byte_type(),
output_offset,
"contract_call_destination",
);
context.build_memcpy_return_data(
context.intrinsics().memory_copy_from_generic,
destination,
source,
output_length,
"contract_call_memcpy_from_child",
);
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 status_code_result =
context.build_load(status_code_result_pointer, "contract_call_status_code")?;
context.build_return(Some(&status_code_result));
Ok(())
}
}
@@ -153,7 +153,7 @@ impl Entry {
context.integer_const(crate::polkavm::XLEN, Self::MAX_CALLDATA_SIZE as u64),
)?;
context.build_runtime_call(
runtime_api::INPUT,
runtime_api::imports::INPUT,
&[input_pointer_casted.into(), length_pointer_casted.into()],
);
@@ -215,11 +215,11 @@ impl Entry {
.ok_or_else(|| anyhow::anyhow!("Contract runtime code not found"))?;
context.set_basic_block(deploy_code_call_block);
context.build_invoke(deploy_code.borrow().declaration, &[], "deploy_code_call");
context.build_call(deploy_code.borrow().declaration, &[], "deploy_code_call");
context.build_unconditional_branch(context.current_function().borrow().return_block());
context.set_basic_block(runtime_code_call_block);
context.build_invoke(runtime_code.borrow().declaration, &[], "runtime_code_call");
context.build_call(runtime_code.borrow().declaration, &[], "runtime_code_call");
context.build_unconditional_branch(context.current_function().borrow().return_block());
Ok(())
@@ -235,7 +235,7 @@ where
let entry_function_type = context.function_type(entry_arguments, 0, false);
context.add_function(Runtime::FUNCTION_ENTRY, entry_function_type, 0, None)?;
for symbol in runtime_api::EXPORTS {
for symbol in runtime_api::exports::EXPORTS {
context.declare_extern_function(symbol)?;
}
@@ -258,7 +258,7 @@ where
true,
);
context.set_current_function(runtime_api::DEPLOY)?;
context.set_current_function(runtime_api::exports::DEPLOY)?;
context.set_basic_block(context.current_function().borrow().entry_block());
assert!(context
@@ -268,7 +268,7 @@ where
context.set_basic_block(context.current_function().borrow().return_block);
context.build_unreachable();
context.set_current_function(runtime_api::CALL)?;
context.set_current_function(runtime_api::exports::CALL)?;
context.set_basic_block(context.current_function().borrow().entry_block());
assert!(context
@@ -1,17 +1,10 @@
//! The front-end runtime functions.
pub mod default_call;
pub mod deploy_code;
pub mod entry;
pub mod runtime_code;
use crate::polkavm::context::address_space::AddressSpace;
use crate::polkavm::context::function::declaration::Declaration as FunctionDeclaration;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use crate::polkavm::WriteLLVM;
use self::default_call::DefaultCall;
/// The front-end runtime functions.
#[derive(Debug, Clone)]
@@ -35,40 +28,4 @@ impl Runtime {
pub fn new(_address_space: AddressSpace) -> Self {
Self { _address_space }
}
/// Returns the corresponding runtime function.
pub fn default_call<'ctx, D>(
context: &Context<'ctx, D>,
call_function: FunctionDeclaration<'ctx>,
) -> FunctionDeclaration<'ctx>
where
D: Dependency + Clone,
{
context
.get_function(DefaultCall::name(call_function).as_str())
.expect("Always exists")
.borrow()
.declaration()
}
}
impl<D> WriteLLVM<D> for Runtime
where
D: Dependency + Clone,
{
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
//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)?;
Ok(())
}
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
//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)?;
Ok(())
}
}
+12 -107
View File
@@ -141,7 +141,7 @@ where
)
.expect("the PolkaVM guest API module should be linkable");
for export in runtime_api::EXPORTS {
for export in runtime_api::exports::EXPORTS {
module
.get_function(export)
.expect("should be declared")
@@ -151,7 +151,7 @@ where
);
}
for import in runtime_api::IMPORTS {
for import in runtime_api::imports::IMPORTS {
module
.get_function(import)
.expect("should be declared")
@@ -433,8 +433,6 @@ where
let entry_block = self.llvm.append_basic_block(value, "entry");
let return_block = self.llvm.append_basic_block(value, "return");
value.set_personality_function(self.llvm_runtime.personality.value);
let r#return = match return_values_length {
0 => FunctionReturn::none(),
1 => {
@@ -705,7 +703,7 @@ where
.build_stack_parameter(revive_common::BIT_LENGTH_WORD, "storage_value_pointer");
self.build_runtime_call(
runtime_api::GET_STORAGE,
runtime_api::imports::GET_STORAGE,
&[
storage_key_pointer_casted.into(),
self.integer_const(crate::polkavm::XLEN, 32).into(),
@@ -809,7 +807,7 @@ where
.build_store(storage_value_pointer.value, storage_value_value)?;
self.build_runtime_call(
runtime_api::SET_STORAGE,
runtime_api::imports::SET_STORAGE,
&[
storage_key_pointer_casted.into(),
self.integer_const(crate::polkavm::XLEN, 32).into(),
@@ -957,106 +955,6 @@ where
call_site_value.try_as_basic_value().left()
}
/// Builds an invoke.
/// Is defaulted to a call if there is no global exception handler.
pub fn build_invoke(
&self,
function: FunctionDeclaration<'ctx>,
arguments: &[inkwell::values::BasicValueEnum<'ctx>],
name: &str,
) -> Option<inkwell::values::BasicValueEnum<'ctx>> {
if !self
.functions
.contains_key(Function::ZKSYNC_NEAR_CALL_ABI_EXCEPTION_HANDLER)
{
return self.build_call(function, arguments, name);
}
let return_pointer = if let Some(r#type) = function.r#type.get_return_type() {
let pointer = self.build_alloca(r#type, "invoke_return_pointer");
self.build_store(pointer, r#type.const_zero()).unwrap();
Some(pointer)
} else {
None
};
let success_block = self.append_basic_block("invoke_success_block");
let catch_block = self.append_basic_block("invoke_catch_block");
let current_block = self.basic_block();
self.set_basic_block(catch_block);
let landing_pad_type = self.structure_type(&[
self.llvm()
.ptr_type(AddressSpace::Stack.into())
.as_basic_type_enum(),
self.integer_type(revive_common::BIT_LENGTH_X32)
.as_basic_type_enum(),
]);
self.builder
.build_landing_pad(
landing_pad_type,
self.llvm_runtime.personality.value,
&[self
.llvm()
.ptr_type(AddressSpace::Stack.into())
.const_zero()
.as_basic_value_enum()],
false,
"invoke_catch_landing",
)
.unwrap();
crate::polkavm::utils::throw(self);
self.set_basic_block(current_block);
let call_site_value = self
.builder
.build_indirect_invoke(
function.r#type,
function.value.as_global_value().as_pointer_value(),
arguments,
success_block,
catch_block,
name,
)
.unwrap();
self.modify_call_site_value(arguments, call_site_value, function);
self.set_basic_block(success_block);
if let (Some(return_pointer), Some(mut return_value)) =
(return_pointer, call_site_value.try_as_basic_value().left())
{
if let Some(return_type) = function.r#type.get_return_type() {
if return_type.is_pointer_type() {
return_value = self
.builder()
.build_int_to_ptr(
return_value.into_int_value(),
return_type.into_pointer_type(),
format!("{name}_invoke_return_pointer_casted").as_str(),
)
.unwrap()
.as_basic_value_enum();
}
}
self.build_store(return_pointer, return_value).unwrap();
}
return_pointer.map(|pointer| self.build_load(pointer, "invoke_result").unwrap())
}
/// Builds an invoke of local call covered with an exception handler.
/// Yul does not the exception handling, so the user can declare a special handling function
/// called (see constant `ZKSYNC_NEAR_CALL_ABI_EXCEPTION_HANDLER`. If the enclosed function
/// panics, the control flow will be transferred to the exception handler.
pub fn build_invoke_near_call_abi(
&self,
_function: FunctionDeclaration<'ctx>,
_arguments: Vec<inkwell::values::BasicValueEnum<'ctx>>,
_name: &str,
) -> Option<inkwell::values::BasicValueEnum<'ctx>> {
unimplemented!()
}
/// Builds a memory copy call.
/// Sets the alignment to `1`, since all non-stack memory pages have such alignment.
pub fn build_memcpy(
&self,
@@ -1132,7 +1030,7 @@ where
)?;
self.build_runtime_call(
runtime_api::RETURN,
runtime_api::imports::RETURN,
&[flags.into(), offset_pointer.into(), length_pointer.into()],
);
self.build_unreachable();
@@ -1396,6 +1294,13 @@ where
self.llvm.custom_width_int_type(crate::polkavm::XLEN as u32)
}
/// Returns the register witdh sized type.
pub fn sentinel_pointer(&self) -> inkwell::values::PointerValue<'ctx> {
self.xlen_type()
.const_all_ones()
.const_to_pointer(self.llvm().ptr_type(Default::default()))
}
/// Returns the runtime value width sized type.
pub fn value_type(&self) -> inkwell::types::IntType<'ctx> {
self.llvm
+93 -67
View File
@@ -3,17 +3,104 @@
use inkwell::values::BasicValue;
use crate::polkavm::context::argument::Argument;
use crate::polkavm::context::function::declaration::Declaration as FunctionDeclaration;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use crate::polkavm_const::runtime_api;
static STATIC_CALL_FLAG: u32 = 0b0001_0000;
/// Translates a contract call.
/// If the `simulation_address` is specified, the call is substituted with another instruction
/// according to the specification.
///
/// If the `simulation_address` is specified, the call is
/// substituted with another instruction according to the specification.
#[allow(clippy::too_many_arguments)]
pub fn default<'ctx, D>(
pub fn call<'ctx, D>(
context: &mut Context<'ctx, D>,
gas: inkwell::values::IntValue<'ctx>,
address: inkwell::values::IntValue<'ctx>,
value: Option<inkwell::values::IntValue<'ctx>>,
input_offset: inkwell::values::IntValue<'ctx>,
input_length: inkwell::values::IntValue<'ctx>,
output_offset: inkwell::values::IntValue<'ctx>,
output_length: inkwell::values::IntValue<'ctx>,
_constants: Vec<Option<num::BigUint>>,
static_call: bool,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
let address_pointer = context.build_alloca(context.word_type(), "address_ptr");
context.build_store(address_pointer, address)?;
let value_pointer = if let Some(value) = value {
let value_pointer = context.build_alloca(context.value_type(), "value");
context.build_store(value_pointer, value)?;
value_pointer.value
} else {
context.sentinel_pointer()
};
let input_offset = context.safe_truncate_int_to_xlen(input_offset)?;
let input_length = context.safe_truncate_int_to_xlen(input_length)?;
let output_offset = context.safe_truncate_int_to_xlen(output_offset)?;
let output_length = context.safe_truncate_int_to_xlen(output_length)?;
let gas = context
.builder()
.build_int_truncate(gas, context.integer_type(64), "gas")?;
let flags = if static_call { STATIC_CALL_FLAG } else { 0 };
let input_pointer = context.build_heap_gep(input_offset, input_length)?;
let output_pointer = context.build_heap_gep(output_offset, output_length)?;
let output_length_pointer = context.get_global(crate::polkavm::GLOBAL_RETURN_DATA_SIZE)?;
context.build_store(output_length_pointer.into(), output_length)?;
let argument_pointer = pallet_contracts_pvm_llapi::calling_convention::Spill::new(
context.builder(),
pallet_contracts_pvm_llapi::calling_convention::call(context.llvm()),
"call_arguments",
)?
.next(context.xlen_type().const_int(flags as u64, false))?
.next(address_pointer.value)?
.next(gas)?
.skip()
.next(context.sentinel_pointer())?
.next(value_pointer)?
.next(input_pointer.value)?
.next(input_length)?
.next(output_pointer.value)?
.next(output_length_pointer.value)?
.done();
let name = runtime_api::imports::CALL;
let arguments = context.builder().build_ptr_to_int(
argument_pointer,
context.xlen_type(),
"argument_pointer",
)?;
let success = context
.build_runtime_call(name, &[arguments.into()])
.unwrap_or_else(|| panic!("{name} should return a value"))
.into_int_value();
let is_success = context.builder().build_int_compare(
inkwell::IntPredicate::EQ,
success,
context.xlen_type().const_zero(),
"is_success",
)?;
Ok(context
.builder()
.build_int_z_extend(is_success, context.word_type(), "success")?
.as_basic_value_enum())
}
#[allow(clippy::too_many_arguments)]
pub fn delegate_call<'ctx, D>(
_context: &mut Context<'ctx, D>,
_function: FunctionDeclaration<'ctx>,
_gas: inkwell::values::IntValue<'ctx>,
_address: inkwell::values::IntValue<'ctx>,
_value: Option<inkwell::values::IntValue<'ctx>>,
@@ -26,68 +113,7 @@ pub fn default<'ctx, D>(
where
D: Dependency + Clone,
{
todo!();
/*
let ordinary_block = context.append_basic_block("contract_call_ordinary_block");
let join_block = context.append_basic_block("contract_call_join_block");
let result_pointer = context.build_alloca(context.field_type(), "contract_call_result_pointer");
context.build_store(result_pointer, context.field_const(0));
context.builder().build_switch(
address,
ordinary_block,
&[(
context.field_const(zkevm_opcode_defs::ADDRESS_IDENTITY.into()),
identity_block,
)],
)?;
{
context.set_basic_block(identity_block);
let result = identity(context, output_offset, input_offset, output_length)?;
context.build_store(result_pointer, result);
context.build_unconditional_branch(join_block);
}
context.set_basic_block(ordinary_block);
let result = if let Some(value) = value {
default_wrapped(
context,
function,
gas,
value,
address,
input_offset,
input_length,
output_offset,
output_length,
)?
} else {
let function = Runtime::default_call(context, function);
context
.build_call(
function,
&[
gas.as_basic_value_enum(),
address.as_basic_value_enum(),
input_offset.as_basic_value_enum(),
input_length.as_basic_value_enum(),
output_offset.as_basic_value_enum(),
output_length.as_basic_value_enum(),
],
"default_call",
)
.expect("Always exists")
};
context.build_store(result_pointer, result);
context.build_unconditional_branch(join_block);
context.set_basic_block(join_block);
let result = context.build_load(result_pointer, "contract_call_result");
Ok(result)
*/
todo!()
}
/// Translates the Yul `linkersymbol` instruction.
@@ -58,7 +58,7 @@ where
"block_timestamp_output",
);
context.build_runtime_call(
runtime_api::BLOCK_NUMBER,
runtime_api::imports::BLOCK_NUMBER,
&[
output_pointer.to_int(context).into(),
output_length_pointer.to_int(context).into(),
@@ -83,7 +83,7 @@ where
"block_timestamp_output",
);
context.build_runtime_call(
runtime_api::NOW,
runtime_api::imports::NOW,
&[
output_pointer.to_int(context).into(),
output_length_pointer.to_int(context).into(),
@@ -174,7 +174,7 @@ where
let (output_pointer, output_length_pointer) =
context.build_stack_parameter(revive_common::BIT_LENGTH_ETH_ADDRESS, "address_output");
context.build_runtime_call(
runtime_api::ADDRESS,
runtime_api::imports::ADDRESS,
&[
output_pointer.to_int(context).into(),
output_length_pointer.to_int(context).into(),
@@ -197,7 +197,7 @@ where
let (output_pointer, output_length_pointer) =
context.build_stack_parameter(revive_common::BIT_LENGTH_ETH_ADDRESS, "caller_output");
context.build_runtime_call(
runtime_api::CALLER,
runtime_api::imports::CALLER,
&[
output_pointer.to_int(context).into(),
output_length_pointer.to_int(context).into(),
+7 -12
View File
@@ -56,11 +56,7 @@ where
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()));
context.build_store(address_pointer, context.word_const(0))?;
let argument_pointer = pallet_contracts_pvm_llapi::calling_convention::Spill::new(
context.builder(),
@@ -70,14 +66,14 @@ where
.next(code_hash_pointer.value)?
.skip()
.skip()
.next(sentinel)?
.next(context.sentinel_pointer())?
.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(context.sentinel_pointer())?
.next(context.sentinel_pointer())?
.next(salt_pointer.value)?
.next(
context
@@ -86,14 +82,13 @@ where
)?
.done();
context.builder().build_direct_call(
context.runtime_api_method(runtime_api::INSTANTIATE),
context.build_runtime_call(
runtime_api::imports::INSTANTIATE,
&[context
.builder()
.build_ptr_to_int(argument_pointer, context.xlen_type(), "argument_pointer")?
.into()],
"create2",
)?;
);
context.build_load_word(
address_pointer,
@@ -19,7 +19,7 @@ where
let output_pointer = context.build_alloca(context.word_type(), "output_pointer");
context.build_runtime_call(
runtime_api::HASH_KECCAK_256,
runtime_api::imports::HASH_KECCAK_256,
&[
input_pointer.to_int(context).into(),
length_casted.into(),
@@ -26,7 +26,7 @@ where
let (output_pointer, output_length_pointer) =
context.build_stack_parameter(revive_common::BIT_LENGTH_VALUE, "value_transferred_output");
context.build_runtime_call(
runtime_api::VALUE_TRANSFERRED,
runtime_api::imports::VALUE_TRANSFERRED,
&[
output_pointer.to_int(context).into(),
output_length_pointer.to_int(context).into(),
+1 -1
View File
@@ -75,7 +75,7 @@ where
]
};
let _ = context.build_runtime_call(runtime_api::DEPOSIT_EVENT, &arguments);
let _ = context.build_runtime_call(runtime_api::imports::DEPOSIT_EVENT, &arguments);
Ok(())
}
@@ -23,8 +23,11 @@ where
"address_pointer",
)?;
let value = context
.build_runtime_call(runtime_api::CODE_SIZE, &[address_pointer_casted.into()])
.unwrap_or_else(|| panic!("{} should return a value", runtime_api::CODE_SIZE))
.build_runtime_call(
runtime_api::imports::CODE_SIZE,
&[address_pointer_casted.into()],
)
.unwrap_or_else(|| panic!("{} should return a value", runtime_api::imports::CODE_SIZE))
.into_int_value();
Ok(context
@@ -4,6 +4,7 @@ use inkwell::values::BasicValue;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use crate::polkavm_const::runtime_api;
/// Translates the return data size.
pub fn size<'ctx, D>(
@@ -38,54 +39,16 @@ where
let destination_offset = context.safe_truncate_int_to_xlen(destination_offset)?;
let size = context.safe_truncate_int_to_xlen(size)?;
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,
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",
let destination_offset = context.builder().build_ptr_to_int(
context.build_heap_gep(destination_offset, size)?.value,
context.xlen_type(),
"destination_offset",
)?;
context.build_conditional_branch(is_overflow, block_trap, block_check_out_of_bounds)?;
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.build_runtime_call(
runtime_api::imports::RETURNDATACOPY,
&[destination_offset.into(), source_offset.into(), size.into()],
);
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.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",
)
Ok(())
}
-18
View File
@@ -3,7 +3,6 @@
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;
@@ -40,23 +39,6 @@ where
Ok(result.into_int_value())
}
/// Generates an exception.
pub fn throw<D>(context: &Context<D>)
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>(
@@ -5,16 +5,16 @@ use inkwell::{
values::{BasicValue, PointerValue},
};
pub struct Spill<'ctx> {
pub struct Spill<'a, 'ctx> {
pointer: PointerValue<'ctx>,
builder: &'ctx Builder<'ctx>,
builder: &'a Builder<'ctx>,
r#type: StructType<'ctx>,
current_field: u32,
}
impl<'ctx> Spill<'ctx> {
impl<'a, 'ctx> Spill<'a, 'ctx> {
pub fn new(
builder: &'ctx Builder<'ctx>,
builder: &'a Builder<'ctx>,
r#type: StructType<'ctx>,
name: &str,
) -> anyhow::Result<Self> {
@@ -88,3 +88,31 @@ pub fn instantiate(context: &Context) -> StructType {
true,
)
}
pub fn call(context: &Context) -> StructType {
context.struct_type(
&[
// flags: u32,
context.i32_type().as_basic_type_enum(),
// address_ptr:
context.ptr_type(Default::default()).as_basic_type_enum(),
// ref_time_limit: u64,
context.i64_type().as_basic_type_enum(),
// proof_size_limit: u64,
context.i64_type().as_basic_type_enum(),
// deposit_ptr: u32,
context.ptr_type(Default::default()).as_basic_type_enum(),
// value_ptr: u32,
context.ptr_type(Default::default()).as_basic_type_enum(),
// input_data_ptr: u32,
context.ptr_type(Default::default()).as_basic_type_enum(),
// input_data_len: u32,
context.i32_type().as_basic_type_enum(),
// output_ptr: u32,
context.ptr_type(Default::default()).as_basic_type_enum(),
// output_len_ptr: u32,
context.ptr_type(Default::default()).as_basic_type_enum(),
],
true,
)
}
@@ -63,6 +63,8 @@ POLKAVM_IMPORT(void, input, uint32_t, uint32_t)
POLKAVM_IMPORT(void, seal_return, uint32_t, uint32_t, uint32_t)
POLKAVM_IMPORT(void, returndatacopy, uint32_t, uint32_t, uint32_t)
POLKAVM_IMPORT(void, value_transferred, uint32_t, uint32_t)
POLKAVM_IMPORT(uint32_t, set_storage, uint32_t, uint32_t, uint32_t, uint32_t)
@@ -191,11 +191,6 @@ where
let mut entry = revive_llvm_context::PolkaVMEntryFunction::default();
entry.declare(context)?;
let mut runtime = revive_llvm_context::PolkaVMRuntime::new(
revive_llvm_context::PolkaVMAddressSpace::Heap,
);
runtime.declare(context)?;
revive_llvm_context::PolkaVMDeployCodeFunction::new(
revive_llvm_context::PolkaVMDummyLLVMWritable::default(),
)
@@ -207,8 +202,6 @@ where
entry.into_llvm(context)?;
runtime.into_llvm(context)?;
Ok(())
}
@@ -37,7 +37,7 @@ where
.integer_type(revive_common::BIT_LENGTH_BOOLEAN)
.const_int(0, false),
};
context.build_invoke(
context.build_call(
target,
&[is_deploy_code.as_basic_value_enum()],
format!("call_link_{}", EtherealIR::DEFAULT_ENTRY_FUNCTION_NAME).as_str(),
@@ -1026,19 +1026,16 @@ where
InstructionName::CALL => {
let mut arguments = self.pop_arguments_llvm(context);
let _gas = arguments.remove(0).into_int_value();
let _address = arguments.remove(0).into_int_value();
let _value = arguments.remove(0).into_int_value();
let _input_offset = arguments.remove(0).into_int_value();
let _input_size = arguments.remove(0).into_int_value();
let _output_offset = arguments.remove(0).into_int_value();
let _output_size = arguments.remove(0).into_int_value();
let gas = arguments.remove(0).into_int_value();
let address = arguments.remove(0).into_int_value();
let value = arguments.remove(0).into_int_value();
let input_offset = arguments.remove(0).into_int_value();
let input_size = arguments.remove(0).into_int_value();
let output_offset = arguments.remove(0).into_int_value();
let output_size = arguments.remove(0).into_int_value();
todo!()
/*
revive_llvm_context::polkavm_evm_call::default(
revive_llvm_context::polkavm_evm_call::call(
context,
context.llvm_runtime().far_call,
gas,
address,
Some(value),
@@ -1047,9 +1044,9 @@ where
output_offset,
output_size,
vec![],
false,
)
.map(Some)
*/
}
InstructionName::STATICCALL => {
let mut arguments = self.pop_arguments_llvm(context);
@@ -1061,9 +1058,8 @@ where
let output_offset = arguments.remove(0).into_int_value();
let output_size = arguments.remove(0).into_int_value();
revive_llvm_context::polkavm_evm_call::default(
revive_llvm_context::polkavm_evm_call::call(
context,
context.llvm_runtime().static_call,
gas,
address,
None,
@@ -1072,6 +1068,7 @@ where
output_offset,
output_size,
vec![],
true,
)
.map(Some)
}
@@ -1085,9 +1082,8 @@ where
let output_offset = arguments.remove(0).into_int_value();
let output_size = arguments.remove(0).into_int_value();
revive_llvm_context::polkavm_evm_call::default(
revive_llvm_context::polkavm_evm_call::delegate_call(
context,
context.llvm_runtime().delegate_call,
gas,
address,
None,
@@ -155,7 +155,7 @@ impl FunctionCall {
);
}
let return_value = context.build_invoke(
let return_value = context.build_call(
function.borrow().declaration(),
values.as_slice(),
format!("{name}_call").as_str(),
@@ -731,24 +731,21 @@ impl FunctionCall {
Name::Call => {
let arguments = self.pop_arguments::<D, 7>(context)?;
let _gas = arguments[0].value.into_int_value();
let _address = arguments[1].value.into_int_value();
let _value = arguments[2].value.into_int_value();
let _input_offset = arguments[3].value.into_int_value();
let _input_size = arguments[4].value.into_int_value();
let _output_offset = arguments[5].value.into_int_value();
let _output_size = arguments[6].value.into_int_value();
let gas = arguments[0].value.into_int_value();
let address = arguments[1].value.into_int_value();
let value = arguments[2].value.into_int_value();
let input_offset = arguments[3].value.into_int_value();
let input_size = arguments[4].value.into_int_value();
let output_offset = arguments[5].value.into_int_value();
let output_size = arguments[6].value.into_int_value();
let _simulation_address: Vec<Option<num::BigUint>> = arguments
let simulation_address: Vec<Option<num::BigUint>> = arguments
.into_iter()
.map(|mut argument| argument.constant.take())
.collect();
todo!()
/*
revive_llvm_context::polkavm_evm_call::default(
revive_llvm_context::polkavm_evm_call::call(
context,
context.llvm_runtime().far_call,
gas,
address,
Some(value),
@@ -757,9 +754,9 @@ impl FunctionCall {
output_offset,
output_size,
simulation_address,
false,
)
.map(Some)
*/
}
Name::StaticCall => {
let arguments = self.pop_arguments::<D, 6>(context)?;
@@ -776,9 +773,8 @@ impl FunctionCall {
.map(|mut argument| argument.constant.take())
.collect();
revive_llvm_context::polkavm_evm_call::default(
revive_llvm_context::polkavm_evm_call::call(
context,
context.llvm_runtime().static_call,
gas,
address,
None,
@@ -787,6 +783,7 @@ impl FunctionCall {
output_offset,
output_size,
simulation_address,
true,
)
.map(Some)
}
@@ -805,9 +802,8 @@ impl FunctionCall {
.map(|mut argument| argument.constant.take())
.collect();
revive_llvm_context::polkavm_evm_call::default(
revive_llvm_context::polkavm_evm_call::delegate_call(
context,
context.llvm_runtime().delegate_call,
gas,
address,
None,
@@ -186,11 +186,6 @@ where
let mut entry = revive_llvm_context::PolkaVMEntryFunction::default();
entry.declare(context)?;
let mut runtime = revive_llvm_context::PolkaVMRuntime::new(
revive_llvm_context::PolkaVMAddressSpace::Heap,
);
runtime.declare(context)?;
revive_llvm_context::PolkaVMDeployCodeFunction::new(
revive_llvm_context::PolkaVMDummyLLVMWritable::default(),
)
@@ -226,16 +221,8 @@ where
revive_llvm_context::PolkaVMDeployCodeFunction::new(self.code).into_llvm(context)?;
}
match self.inner_object {
Some(object) => {
object.into_llvm(context)?;
}
None => {
let runtime = revive_llvm_context::PolkaVMRuntime::new(
revive_llvm_context::PolkaVMAddressSpace::Heap,
);
runtime.into_llvm(context)?;
}
if let Some(object) = self.inner_object {
object.into_llvm(context)?;
}
Ok(())