mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-04-26 04:07:57 +00:00
Contract calls (#19)
This commit is contained in:
Generated
+25
-25
@@ -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]]
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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)]
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
|
||||
Reference in New Issue
Block a user