Updated call semantics (#56)

- Update pallet-revive dependency
- Implement calls according to pallet-revive call semantics
- Switch to the new return data API in pallet revive and get rid of return data buffer
- Remove a bunch of resulting dead code
This commit is contained in:
Cyrill Leutwiler
2024-09-28 20:03:03 +02:00
committed by GitHub
parent 066acc4663
commit 6585973e99
24 changed files with 1001 additions and 886 deletions
Generated
+478 -462
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -66,7 +66,7 @@ log = { version = "0.4" }
# polkadot-sdk and friends # polkadot-sdk and friends
codec = { version = "3.6.12", default-features = false, package = "parity-scale-codec" } codec = { version = "3.6.12", default-features = false, package = "parity-scale-codec" }
scale-info = { version = "2.11.1", default-features = false } scale-info = { version = "2.11.1", default-features = false }
polkadot-sdk = { git = "https://github.com/paritytech/polkadot-sdk", rev = "71c768a9e1a467c629adc68423e47e37c855cd77" } polkadot-sdk = { git = "https://github.com/paritytech/polkadot-sdk", rev = "c77095f51119d2eccdc54d2f3518bed0ffbd6d53" }
# llvm # llvm
[workspace.dependencies.inkwell] [workspace.dependencies.inkwell]
+1 -1
View File
@@ -20,7 +20,7 @@ pub const BIT_LENGTH_ETH_ADDRESS: usize =
pub const BIT_LENGTH_WORD: usize = crate::byte_length::BYTE_LENGTH_WORD * BIT_LENGTH_BYTE; pub const BIT_LENGTH_WORD: usize = crate::byte_length::BYTE_LENGTH_WORD * BIT_LENGTH_BYTE;
/// Bit length of the runtime value type. /// Bit length of the runtime value type.
pub const BIT_LENGTH_VALUE: usize = crate::byte_length::BYTE_LENGTH_VALUE * BIT_LENGTH_BYTE; pub const BIT_LENGTH_VALUE: usize = BIT_LENGTH_WORD;
/// Bit length of thre runimte block number type. /// Bit length of thre runimte block number type.
pub const BIT_LENGTH_BLOCK_NUMBER: usize = pub const BIT_LENGTH_BLOCK_NUMBER: usize =
+8 -8
View File
@@ -1,10 +1,10 @@
{ {
"Baseline": 878, "Baseline": 912,
"Computation": 4305, "Computation": 4413,
"DivisionArithmetics": 39774, "DivisionArithmetics": 40689,
"ERC20": 53405, "ERC20": 54374,
"Events": 1693, "Events": 1726,
"FibonacciIterative": 2917, "FibonacciIterative": 3015,
"Flipper": 3570, "Flipper": 3612,
"SHA1": 32557 "SHA1": 32865
} }
+53 -10
View File
@@ -2,19 +2,62 @@
pragma solidity ^0.8; pragma solidity ^0.8;
contract Call { /* runner.json
{
"differential": true,
"actions": [
{
"Upload": {
"code": {
"Solidity": {
"contract": "Callee"
}
}
}
},
{
"Instantiate": {
"code": {
"Solidity": {
"contract": "Caller"
}
}
}
},
{
"Call": {
"dest": {
"Instantiated": 0
},
"value": 123,
"data": "1eb16e5b000000000000000000000000d8b934580fce35a11b58c6d73adee468a2833fa8"
}
},
{
"Call": {
"dest": {
"Instantiated": 0
},
"data": "5a6535fc00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000004cafebabe00000000000000000000000000000000000000000000000000000000"
}
}
]
}
*/
contract Callee {
function echo(bytes memory payload) public pure returns (bytes memory) {
return payload;
}
}
contract Caller {
function value_transfer(address payable destination) public payable { function value_transfer(address payable destination) public payable {
destination.transfer(msg.value); destination.transfer(msg.value);
} }
function echo(bytes memory payload) public pure returns (bytes memory) { function call(bytes memory payload) public returns (bytes memory) {
return payload; Callee callee = new Callee();
} return callee.echo(payload);
function call(
address callee,
bytes memory payload
) public pure returns (bytes memory) {
return Call(callee).echo(payload);
} }
} }
+57 -6
View File
@@ -1,13 +1,64 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.24; pragma solidity ^0.8;
/* runner.json
{
"differential": true,
"actions": [
{
"Upload": {
"code": {
"Solidity": {
"contract": "CreateA"
}
}
}
},
{
"Instantiate": {
"code": {
"Solidity": {
"contract": "CreateB"
}
}
}
},
{
"Call": {
"dest": {
"Instantiated": 0
},
"value": 10000
}
},
{
"Call": {
"dest": {
"Instantiated": 0
}
}
},
{
"Call": {
"dest": {
"Instantiated": 0
}
}
},
{
"Call": {
"dest": {
"Instantiated": 0
}
}
}
]
}
*/
contract CreateA { contract CreateA {
address creator; constructor() payable {}
constructor() payable {
creator = msg.sender;
}
} }
contract CreateB { contract CreateB {
+13 -11
View File
@@ -4,30 +4,32 @@ pragma solidity ^0.8.24;
/* runner.json /* runner.json
{ {
"actions": [ "differential": true,
"actions": [
{ {
"Instantiate": {} "Instantiate": {
"code": {
"Solidity": {
"contract": "TestSha3"
}
}
}
}, },
{ {
"Call": { "Call": {
"dest": { "dest": {
"Instantiated": 0 "Instantiated": 0
}, },
"data": "f9fbd5540000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c68656c6c6f20776f726c64210000000000000000000000000000000000000000" "data": "f9fbd5540000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c68656c6c6f20776f726c64210000000000000000000000000000000000000000"
} }
},
{
"VerifyCall": {
"success": true,
"output": "57caa176af1ac0433c5df30e8dabcd2ec1af1e92a26eced5f719b88458777cd6"
}
} }
] ]
} }
*/ */
contract TestSha3 { contract TestSha3 {
function test(string memory _pre) external payable returns (bytes32 hash) { function test(string memory _pre) external payable returns (bytes32) {
hash = keccak256(bytes(_pre)); bytes32 hash = keccak256(bytes(_pre));
return bytes32(uint(hash) + 1);
} }
} }
@@ -0,0 +1,53 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;
/* runner.json
{
"differential": true,
"actions": [
{
"Upload": {
"code": {
"Solidity": {
"contract": "Callee"
}
}
}
},
{
"Instantiate": {
"code": {
"Solidity": {
"contract": "ReturnDataOob"
}
}
}
},
{
"Call": {
"dest": {
"Instantiated": 0
}
}
}
]
}
*/
contract Callee {
function echo(bytes memory payload) public pure returns (bytes memory) {
return payload;
}
}
contract ReturnDataOob {
fallback() external {
new Callee().echo(hex"1234");
assembly {
let pos := mload(64)
let size := add(returndatasize(), 1)
returndatacopy(pos, 0, size)
}
}
}
+54
View File
@@ -0,0 +1,54 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;
/* runner.json
{
"differential": true,
"actions": [
{
"Instantiate": {
"code": {
"Solidity": {
"contract": "Transfer"
}
},
"value": 11
}
},
{
"Call": {
"dest": {
"Instantiated": 0
},
"data": "1c8d16b30000000000000000000000000303030303030303030303030303030303030303000000000000000000000000000000000000000000000000000000000000000a"
}
},
{
"Call": {
"dest": {
"Instantiated": 0
},
"data": "fb9e8d0500000000000000000000000003030303030303030303030303030303030303030000000000000000000000000000000000000000000000000000000000000001"
}
}
]
}
*/
contract Transfer {
constructor() payable {
transfer_self(msg.value);
}
function address_self() internal view returns (address payable) {
return payable(address(this));
}
function transfer_self(uint _amount) public payable {
transfer_to(address_self(), _amount);
}
function transfer_to(address payable _dest, uint _amount) public payable {
_dest.transfer(_amount);
}
}
+27 -104
View File
@@ -6,6 +6,10 @@ use SpecsAction::*;
use crate::cases::Contract; use crate::cases::Contract;
/// Parameters:
/// - The function name of the test
/// - The contract name to fill in empty code based on the file path
/// - The contract source file
macro_rules! test_spec { macro_rules! test_spec {
($test_name:ident, $contract_name:literal, $source_file:literal) => { ($test_name:ident, $contract_name:literal, $source_file:literal) => {
#[test] #[test]
@@ -34,6 +38,10 @@ test_spec!(storage, "Storage", "Storage.sol");
test_spec!(mstore8, "MStore8", "MStore8.sol"); test_spec!(mstore8, "MStore8", "MStore8.sol");
test_spec!(address, "Context", "Context.sol"); test_spec!(address, "Context", "Context.sol");
test_spec!(balance, "Value", "Value.sol"); test_spec!(balance, "Value", "Value.sol");
test_spec!(create, "CreateB", "Create.sol");
test_spec!(call, "Caller", "Call.sol");
test_spec!(transfer, "Transfer", "Transfer.sol");
test_spec!(return_data_oob, "ReturnDataOob", "ReturnDataOob.sol");
fn instantiate(path: &str, contract: &str) -> Vec<SpecsAction> { fn instantiate(path: &str, contract: &str) -> Vec<SpecsAction> {
vec![Instantiate { vec![Instantiate {
@@ -224,63 +232,7 @@ fn signed_remainder() {
} }
/* /*
#[test] // These test were implement for the mock-runtime and need to be ported yet.
fn events() {
assert_success(&Contract::event(U256::ZERO), true);
assert_success(&Contract::event(U256::from(123)), true);
}
#[test]
fn balance() {
let (_, output) = assert_success(&Contract::value_balance_of(Default::default()), false);
let expected = U256::ZERO;
let received = U256::from_be_slice(&output.data);
assert_eq!(expected, received);
let expected = U256::from(54589);
let (mut state, address) = State::new_deployed(Contract::value_balance_of(Default::default()));
state.accounts_mut().get_mut(&address).unwrap().value = expected;
let contract = Contract::value_balance_of(address);
let (_, output) = state
.transaction()
.with_default_account(&contract.pvm_runtime)
.calldata(contract.calldata)
.call();
assert_eq!(ReturnFlags::Success, output.flags);
let received = U256::from_be_slice(&output.data);
assert_eq!(expected, received)
}
#[test]
fn create2() {
let mut state = State::default();
let contract_a = Contract::create_a();
state.upload_code(&contract_a.pvm_runtime);
let contract = Contract::create_b();
let (state, output) = state
.transaction()
.with_default_account(&contract.pvm_runtime)
.calldata(contract.calldata)
.call();
assert_eq!(output.flags, ReturnFlags::Success);
assert_eq!(state.accounts().len(), 2);
for address in state.accounts().keys() {
if *address != Transaction::default_address() {
let derived_address = Transaction::default_address().create2(
B256::from(U256::from(1)),
keccak256(&contract_a.pvm_runtime).0,
);
assert_eq!(*address, derived_address);
}
}
}
#[test] #[test]
fn create2_failure() { fn create2_failure() {
@@ -308,29 +260,30 @@ fn create2_failure() {
assert_eq!(output.flags, ReturnFlags::Revert); assert_eq!(output.flags, ReturnFlags::Revert);
} }
#[test]
fn create_with_value() {
let mut state = State::default();
state.upload_code(&Contract::create_a().pvm_runtime);
let amount = U256::from(123);
let contract = Contract::create_b(); #[test]
let (state, output) = state fn balance() {
let (_, output) = assert_success(&Contract::value_balance_of(Default::default()), false);
let expected = U256::ZERO;
let received = U256::from_be_slice(&output.data);
assert_eq!(expected, received);
let expected = U256::from(54589);
let (mut state, address) = State::new_deployed(Contract::value_balance_of(Default::default()));
state.accounts_mut().get_mut(&address).unwrap().value = expected;
let contract = Contract::value_balance_of(address);
let (_, output) = state
.transaction() .transaction()
.with_default_account(&contract.pvm_runtime) .with_default_account(&contract.pvm_runtime)
.callvalue(amount) .calldata(contract.calldata)
.call(); .call();
assert_eq!(output.flags, ReturnFlags::Success); assert_eq!(ReturnFlags::Success, output.flags);
assert_eq!(state.accounts().len(), 2);
for (address, account) in state.accounts() { let received = U256::from_be_slice(&output.data);
if *address == Transaction::default_address() { assert_eq!(expected, received)
assert_eq!(account.value, U256::ZERO);
} else {
assert_eq!(account.value, amount);
}
}
} }
#[test] #[test]
@@ -356,34 +309,4 @@ fn code_size() {
let received = U256::from_be_slice(&output.data); let received = U256::from_be_slice(&output.data);
assert_eq!(expected, received); assert_eq!(expected, received);
} }
#[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);
}
*/ */
@@ -21,12 +21,6 @@ pub static GLOBAL_CALLDATA_POINTER: &str = "ptr_calldata";
/// The calldata size global variable name. /// The calldata size global variable name.
pub static GLOBAL_CALLDATA_SIZE: &str = "calldatasize"; pub static GLOBAL_CALLDATA_SIZE: &str = "calldatasize";
/// The return data pointer global variable name.
pub static GLOBAL_RETURN_DATA_POINTER: &str = "ptr_return_data";
/// The return data size pointer global variable name.
pub static GLOBAL_RETURN_DATA_SIZE: &str = "returndatasize";
/// The call flags global variable name. /// The call flags global variable name.
pub static GLOBAL_CALL_FLAGS: &str = "call_flags"; pub static GLOBAL_CALL_FLAGS: &str = "call_flags";
@@ -36,15 +30,6 @@ pub static GLOBAL_CONST_ARRAY_PREFIX: &str = "const_array_";
/// The global verbatim getter identifier prefix. /// The global verbatim getter identifier prefix.
pub static GLOBAL_VERBATIM_GETTER_PREFIX: &str = "get_global::"; pub static GLOBAL_VERBATIM_GETTER_PREFIX: &str = "get_global::";
/// The static word size.
pub static GLOBAL_I256_SIZE: &str = "i256_size";
/// The static value size.
pub static GLOBAL_I160_SIZE: &str = "i160_size";
/// The static i64 size.
pub static GLOBAL_I64_SIZE: &str = "i64_size";
/// The external call data offset in the auxiliary heap. /// The external call data offset in the auxiliary heap.
pub const HEAP_AUX_OFFSET_EXTERNAL_CALL: u64 = 0; pub const HEAP_AUX_OFFSET_EXTERNAL_CALL: u64 = 0;
@@ -41,7 +41,9 @@ pub mod imports {
pub static RETURN: &str = "seal_return"; pub static RETURN: &str = "seal_return";
pub static RETURNDATACOPY: &str = "returndatacopy"; pub static RETURNDATACOPY: &str = "return_data_copy";
pub static RETURNDATASIZE: &str = "return_data_size";
pub static SET_STORAGE: &str = "set_storage"; pub static SET_STORAGE: &str = "set_storage";
@@ -49,7 +51,7 @@ pub mod imports {
/// All imported runtime API symbols. /// All imported runtime API symbols.
/// Useful for configuring common attributes and linkage. /// Useful for configuring common attributes and linkage.
pub static IMPORTS: [&str; 17] = [ pub static IMPORTS: [&str; 18] = [
ADDRESS, ADDRESS,
BALANCE, BALANCE,
BLOCK_NUMBER, BLOCK_NUMBER,
@@ -65,6 +67,7 @@ pub mod imports {
NOW, NOW,
RETURN, RETURN,
RETURNDATACOPY, RETURNDATACOPY,
RETURNDATASIZE,
SET_STORAGE, SET_STORAGE,
VALUE_TRANSFERRED, VALUE_TRANSFERRED,
]; ];
@@ -1,7 +1,6 @@
//! The entry function. //! The entry function.
use inkwell::types::BasicType; use inkwell::types::BasicType;
use inkwell::values::BasicValue;
use crate::polkavm::context::address_space::AddressSpace; use crate::polkavm::context::address_space::AddressSpace;
use crate::polkavm::context::function::runtime; use crate::polkavm::context::function::runtime;
@@ -26,9 +25,6 @@ impl Entry {
/// Reserve 1kb for calldata. /// Reserve 1kb for calldata.
pub const MAX_CALLDATA_SIZE: usize = 1024; pub const MAX_CALLDATA_SIZE: usize = 1024;
/// Reserve 1kb for returndata.
pub const MAX_RETURNDATA_SIZE: usize = 1024;
/// Initializes the global variables. /// Initializes the global variables.
/// The pointers are not initialized, because it's not possible to create a null pointer. /// The pointers are not initialized, because it's not possible to create a null pointer.
pub fn initialize_globals<D>(context: &mut Context<D>) -> anyhow::Result<()> pub fn initialize_globals<D>(context: &mut Context<D>) -> anyhow::Result<()>
@@ -43,14 +39,6 @@ impl Entry {
calldata_type.get_undef(), calldata_type.get_undef(),
); );
let returndata_type = context.array_type(context.byte_type(), Self::MAX_RETURNDATA_SIZE);
context.set_global(
crate::polkavm::GLOBAL_RETURN_DATA_POINTER,
returndata_type,
AddressSpace::Stack,
returndata_type.get_undef(),
);
context.set_global( context.set_global(
crate::polkavm::GLOBAL_HEAP_MEMORY_POINTER, crate::polkavm::GLOBAL_HEAP_MEMORY_POINTER,
context.llvm().ptr_type(AddressSpace::Heap.into()), context.llvm().ptr_type(AddressSpace::Heap.into()),
@@ -70,12 +58,6 @@ impl Entry {
AddressSpace::Stack, AddressSpace::Stack,
context.word_undef(), context.word_undef(),
); );
context.set_global(
crate::polkavm::GLOBAL_RETURN_DATA_SIZE,
context.xlen_type(),
AddressSpace::Stack,
context.xlen_type().const_zero().as_basic_value_enum(),
);
context.set_global( context.set_global(
crate::polkavm::GLOBAL_CALL_FLAGS, crate::polkavm::GLOBAL_CALL_FLAGS,
@@ -84,33 +66,6 @@ impl Entry {
context.word_const(0), context.word_const(0),
); );
context.set_global(
crate::polkavm::GLOBAL_I256_SIZE,
context.xlen_type(),
AddressSpace::Stack,
context.integer_const(
crate::polkavm::XLEN,
revive_common::BYTE_LENGTH_X64 as u64 * 4,
),
);
context.set_global(
crate::polkavm::GLOBAL_I160_SIZE,
context.xlen_type(),
AddressSpace::Stack,
context.integer_const(
crate::polkavm::XLEN,
revive_common::BYTE_LENGTH_X64 as u64 * 2 + revive_common::BYTE_LENGTH_X32 as u64,
),
);
context.set_global(
crate::polkavm::GLOBAL_I64_SIZE,
context.xlen_type(),
AddressSpace::Stack,
context.integer_const(crate::polkavm::XLEN, revive_common::BYTE_LENGTH_X64 as u64),
);
Ok(()) Ok(())
} }
+8 -36
View File
@@ -610,9 +610,8 @@ where
} }
let pointer = self.build_alloca(r#type, name); let pointer = self.build_alloca(r#type, name);
self.set_basic_block(current_block); self.set_basic_block(current_block);
return pointer; pointer
} }
/// Builds an aligned stack allocation at the current position. /// Builds an aligned stack allocation at the current position.
@@ -633,43 +632,16 @@ where
Pointer::new(r#type, AddressSpace::Stack, pointer) Pointer::new(r#type, AddressSpace::Stack, pointer)
} }
/// Allocate an int of size `bit_length` on the stack. /// Load the address at given pointer and zero extend it to the VM word size.
/// Returns the allocation pointer and the length pointer. pub fn build_load_address(
///
/// Useful helper for passing runtime API parameters on the stack.
pub fn build_stack_parameter(
&self,
bit_length: usize,
name: &str,
) -> (Pointer<'ctx>, Pointer<'ctx>) {
let buffer_pointer = self.build_alloca(self.integer_type(bit_length), name);
let symbol = match bit_length {
revive_common::BIT_LENGTH_WORD => GLOBAL_I256_SIZE,
revive_common::BIT_LENGTH_ETH_ADDRESS => GLOBAL_I160_SIZE,
revive_common::BIT_LENGTH_BLOCK_NUMBER => GLOBAL_I64_SIZE,
_ => panic!("invalid stack parameter bit width: {bit_length}"),
};
let length_pointer = self.get_global(symbol).expect("should be declared");
(buffer_pointer, length_pointer.into())
}
/// Load the integer at given pointer and zero extend it to the VM word size.
pub fn build_load_word(
&self, &self,
pointer: Pointer<'ctx>, pointer: Pointer<'ctx>,
bit_length: usize,
name: &str,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> { ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
let value = self.build_load( let address = self.build_byte_swap(self.build_load(pointer, "address_pointer")?)?;
pointer.cast(self.integer_type(bit_length)), Ok(self
&format!("load_{name}"), .builder()
)?; .build_int_z_extend(address.into_int_value(), self.word_type(), "address_zext")?
let value_extended = self.builder().build_int_z_extend( .into())
value.into_int_value(),
self.word_type(),
&format!("zext_{name}"),
)?;
Ok(value_extended.as_basic_value_enum())
} }
/// Builds a stack load instruction. /// Builds a stack load instruction.
+49 -39
View File
@@ -7,16 +7,14 @@ use crate::polkavm::context::Context;
use crate::polkavm::Dependency; use crate::polkavm::Dependency;
use crate::polkavm_const::runtime_api; use crate::polkavm_const::runtime_api;
static STATIC_CALL_FLAG: u32 = 0b0001_0000; const STATIC_CALL_FLAG: u32 = 0b0001_0000;
const REENTRANT_CALL_FLAG: u32 = 0b0000_1000;
/// Translates a contract call. /// Translates a contract call.
///
/// If the `simulation_address` is specified, the call is
/// substituted with another instruction according to the specification.
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn call<'ctx, D>( pub fn call<'ctx, D>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx, D>,
gas: inkwell::values::IntValue<'ctx>, _gas: inkwell::values::IntValue<'ctx>,
address: inkwell::values::IntValue<'ctx>, address: inkwell::values::IntValue<'ctx>,
value: Option<inkwell::values::IntValue<'ctx>>, value: Option<inkwell::values::IntValue<'ctx>>,
input_offset: inkwell::values::IntValue<'ctx>, input_offset: inkwell::values::IntValue<'ctx>,
@@ -29,59 +27,71 @@ pub fn call<'ctx, D>(
where where
D: Dependency + Clone, D: Dependency + Clone,
{ {
let address_pointer = context.build_alloca(context.word_type(), "address_ptr"); let address_type = context.integer_type(revive_common::BIT_LENGTH_ETH_ADDRESS);
context.build_store(address_pointer, address)?; let address_pointer = context.build_alloca_at_entry(address_type, "address_pointer");
let address_truncated =
context
.builder()
.build_int_truncate(address, address_type, "address_truncated")?;
let address_swapped = context.build_byte_swap(address_truncated.into())?;
context.build_store(address_pointer, address_swapped)?;
let value_pointer = if let Some(value) = value { let value = value.unwrap_or_else(|| context.word_const(0));
let value_pointer = context.build_alloca(context.value_type(), "value"); let value_pointer = context.build_alloca_at_entry(context.word_type(), "value_pointer");
context.build_store(value_pointer, value)?; context.build_store(value_pointer, value)?;
value_pointer
} else {
context.sentinel_pointer()
};
let input_offset = context.safe_truncate_int_to_xlen(input_offset)?; let input_offset = context.safe_truncate_int_to_xlen(input_offset)?;
let input_length = context.safe_truncate_int_to_xlen(input_length)?; let input_length = context.safe_truncate_int_to_xlen(input_length)?;
let output_offset = context.safe_truncate_int_to_xlen(output_offset)?; let output_offset = context.safe_truncate_int_to_xlen(output_offset)?;
let output_length = context.safe_truncate_int_to_xlen(output_length)?; let output_length = context.safe_truncate_int_to_xlen(output_length)?;
let gas = context // TODO: What to supply here? Is there a weight to gas?
let _gas = context
.builder() .builder()
.build_int_truncate(gas, context.integer_type(64), "gas")?; .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 input_pointer = context.build_heap_gep(input_offset, input_length)?;
let output_pointer = context.build_heap_gep(output_offset, output_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)?; let output_length_pointer = context.build_alloca_at_entry(context.xlen_type(), "output_length");
context.build_store(output_length_pointer.into(), output_length)?; context.build_store(output_length_pointer, output_length)?;
let argument_pointer = revive_runtime_api::calling_convention::Spill::new( let flags = if static_call {
REENTRANT_CALL_FLAG | STATIC_CALL_FLAG
} else {
REENTRANT_CALL_FLAG
};
let flags = context.xlen_type().const_int(flags as u64, false);
let argument_type = revive_runtime_api::calling_convention::call(context.llvm());
let argument_pointer = context.build_alloca_at_entry(argument_type, "call_arguments");
let arguments = &[
flags.as_basic_value_enum(),
address_pointer.value.as_basic_value_enum(),
context.integer_const(64, 0).as_basic_value_enum(),
context.integer_const(64, 0).as_basic_value_enum(),
context.sentinel_pointer().value.as_basic_value_enum(),
value_pointer.value.as_basic_value_enum(),
input_pointer.value.as_basic_value_enum(),
input_length.as_basic_value_enum(),
output_pointer.value.as_basic_value_enum(),
output_length_pointer.value.as_basic_value_enum(),
];
revive_runtime_api::calling_convention::spill(
context.builder(), context.builder(),
revive_runtime_api::calling_convention::call(context.llvm()), argument_pointer.value,
"call_arguments", argument_type,
)? arguments,
.next(context.xlen_type().const_int(flags as u64, false))? )?;
.next(address_pointer.value)?
.next(gas)?
.skip()
.next(context.sentinel_pointer().value)?
.next(value_pointer.value)?
.next(input_pointer.value)?
.next(input_length)?
.next(output_pointer.value)?
.next(output_length_pointer.value)?
.done();
let name = runtime_api::imports::CALL; let name = runtime_api::imports::CALL;
let arguments = context.builder().build_ptr_to_int( let argument_pointer = context.builder().build_ptr_to_int(
argument_pointer, argument_pointer.value,
context.xlen_type(), context.xlen_type(),
"argument_pointer", "call_argument_pointer",
)?; )?;
let success = context let success = context
.build_runtime_call(name, &[arguments.into()]) .build_runtime_call(name, &[argument_pointer.into()])
.unwrap_or_else(|| panic!("{name} should return a value")) .unwrap_or_else(|| panic!("{name} should return a value"))
.into_int_value(); .into_int_value();
+10 -6
View File
@@ -149,11 +149,7 @@ where
runtime_api::imports::ADDRESS, runtime_api::imports::ADDRESS,
&[pointer.to_int(context).into()], &[pointer.to_int(context).into()],
); );
let value = context.build_byte_swap(context.build_load(pointer, "address")?)?; context.build_load_address(pointer)
Ok(context
.builder()
.build_int_z_extend(value.into_int_value(), context.word_type(), "address_zext")?
.into())
} }
/// Translates the `caller` instruction. /// Translates the `caller` instruction.
@@ -163,5 +159,13 @@ pub fn caller<'ctx, D>(
where where
D: Dependency + Clone, D: Dependency + Clone,
{ {
context.build_runtime_call_to_getter(runtime_api::imports::CALLER) let pointer = context.build_alloca_at_entry(
context.integer_type(revive_common::BIT_LENGTH_ETH_ADDRESS),
"address_output",
);
context.build_runtime_call(
runtime_api::imports::CALLER,
&[pointer.to_int(context).into()],
);
context.build_load_address(pointer)
} }
+54 -54
View File
@@ -9,26 +9,14 @@ use crate::polkavm::context::Context;
use crate::polkavm::Dependency; use crate::polkavm::Dependency;
use crate::polkavm_const::runtime_api; use crate::polkavm_const::runtime_api;
/// Translates the contract `create` instruction. /// Translates the contract `create` and `create2` instruction.
/// The instruction is simulated by a call to a system contract. ///
/// A `salt` value of `None` is equivalent to `create1`.
pub fn create<'ctx, D>( pub fn create<'ctx, D>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx, D>,
value: inkwell::values::IntValue<'ctx>, value: inkwell::values::IntValue<'ctx>,
input_offset: inkwell::values::IntValue<'ctx>, input_offset: inkwell::values::IntValue<'ctx>,
input_length: inkwell::values::IntValue<'ctx>, input_length: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
self::create2(context, value, input_offset, input_length, None)
}
/// Translates the contract `create2` instruction.
pub fn create2<'ctx, D>(
context: &mut Context<'ctx, D>,
value: inkwell::values::IntValue<'ctx>,
input_offset: inkwell::values::IntValue<'ctx>,
input_length: inkwell::values::IntValue<'ctx>,
salt: Option<inkwell::values::IntValue<'ctx>>, salt: Option<inkwell::values::IntValue<'ctx>>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where where
@@ -37,9 +25,6 @@ where
let input_offset = context.safe_truncate_int_to_xlen(input_offset)?; let input_offset = context.safe_truncate_int_to_xlen(input_offset)?;
let input_length = context.safe_truncate_int_to_xlen(input_length)?; let input_length = context.safe_truncate_int_to_xlen(input_length)?;
let value_pointer = context.build_alloca(context.value_type(), "value");
context.build_store(value_pointer, value)?;
let code_hash_pointer = context.build_heap_gep(input_offset, input_length)?; let code_hash_pointer = context.build_heap_gep(input_offset, input_length)?;
let input_data_pointer = context.build_gep( let input_data_pointer = context.build_gep(
@@ -48,53 +33,68 @@ where
.xlen_type() .xlen_type()
.const_int(revive_common::BYTE_LENGTH_WORD as u64, false)], .const_int(revive_common::BYTE_LENGTH_WORD as u64, false)],
context.byte_type(), context.byte_type(),
"value_ptr_parameter_offset", "input_ptr_parameter_offset",
); );
let salt_pointer = context.build_alloca(context.word_type(), "salt"); let value_pointer = context.build_alloca_at_entry(context.value_type(), "transferred_value");
context.build_store(salt_pointer, salt.unwrap_or_else(|| context.word_const(0)))?; context.build_store(value_pointer, value)?;
let (address_pointer, address_length_pointer) = let salt_pointer = match salt {
context.build_stack_parameter(revive_common::BIT_LENGTH_ETH_ADDRESS, "address_pointer"); Some(salt) => {
let salt_pointer = context.build_alloca_at_entry(context.word_type(), "salt_pointer");
context.build_store(salt_pointer, salt)?;
salt_pointer
}
None => context.sentinel_pointer(),
};
let address_pointer = context.build_alloca_at_entry(
context.integer_type(revive_common::BIT_LENGTH_ETH_ADDRESS),
"address_pointer",
);
context.build_store(address_pointer, context.word_const(0))?; context.build_store(address_pointer, context.word_const(0))?;
let argument_pointer = revive_runtime_api::calling_convention::Spill::new( let argument_type = revive_runtime_api::calling_convention::instantiate(context.llvm());
let argument_pointer = context.build_alloca_at_entry(argument_type, "instantiate_arguments");
let arguments = &[
code_hash_pointer.value.as_basic_value_enum(),
context.integer_const(64, 0).as_basic_value_enum(),
context.integer_const(64, 0).as_basic_value_enum(),
context.sentinel_pointer().value.as_basic_value_enum(),
value_pointer.value.as_basic_value_enum(),
input_data_pointer.value.as_basic_value_enum(),
input_length.as_basic_value_enum(),
address_pointer.value.as_basic_value_enum(),
context.sentinel_pointer().value.as_basic_value_enum(),
context.sentinel_pointer().value.as_basic_value_enum(),
salt_pointer.value.as_basic_value_enum(),
];
revive_runtime_api::calling_convention::spill(
context.builder(), context.builder(),
revive_runtime_api::calling_convention::instantiate(context.llvm()), argument_pointer.value,
"create2_arguments", argument_type,
)? arguments,
.next(code_hash_pointer.value)? )?;
.skip()
.skip()
.next(context.sentinel_pointer().value)?
.next(value_pointer.value)?
.next(input_data_pointer.value)?
.next(input_length)?
.next(address_pointer.value)?
.next(address_length_pointer.value)?
.next(context.sentinel_pointer().value)?
.next(context.sentinel_pointer().value)?
.next(salt_pointer.value)?
.next(
context
.xlen_type()
.const_int(revive_common::BYTE_LENGTH_WORD as u64, false),
)?
.done();
let argument_pointer = context.builder().build_ptr_to_int(
argument_pointer.value,
context.xlen_type(),
"instantiate_argument_pointer",
)?;
context.build_runtime_call( context.build_runtime_call(
runtime_api::imports::INSTANTIATE, runtime_api::imports::INSTANTIATE,
&[context &[argument_pointer.into()],
.builder()
.build_ptr_to_int(argument_pointer, context.xlen_type(), "argument_pointer")?
.into()],
); );
context.build_load_word( let address = context.build_byte_swap(context.build_load(address_pointer, "address")?)?;
address_pointer, Ok(context
revive_common::BIT_LENGTH_ETH_ADDRESS, .builder()
"address", .build_int_z_extend(
) address.into_int_value(),
context.word_type(),
"address_zext",
)?
.into())
} }
/// Translates the contract hash instruction, which is actually used to set the hash of the contract /// Translates the contract hash instruction, which is actually used to set the hash of the contract
@@ -29,11 +29,7 @@ where
runtime_api::imports::VALUE_TRANSFERRED, runtime_api::imports::VALUE_TRANSFERRED,
&[output_pointer.to_int(context).into()], &[output_pointer.to_int(context).into()],
); );
context.build_load_word( context.build_load(output_pointer, "value_transferred")
output_pointer,
revive_common::BIT_LENGTH_VALUE,
"value_transferred",
)
} }
/// Translates the `balance` instructions. /// Translates the `balance` instructions.
@@ -1,7 +1,5 @@
//! Translates the return data instructions. //! Translates the return data instructions.
use inkwell::values::BasicValue;
use crate::polkavm::context::Context; use crate::polkavm::context::Context;
use crate::polkavm::Dependency; use crate::polkavm::Dependency;
use crate::polkavm_const::runtime_api; use crate::polkavm_const::runtime_api;
@@ -13,13 +11,17 @@ pub fn size<'ctx, D>(
where where
D: Dependency + Clone, D: Dependency + Clone,
{ {
let value = context let output_pointer = context.build_alloca_at_entry(context.word_type(), "return_data_size");
.get_global_value(crate::polkavm::GLOBAL_RETURN_DATA_SIZE)? let output_pointer_parameter = context.builder().build_ptr_to_int(
.into_int_value(); output_pointer.value,
Ok(context context.xlen_type(),
.builder() "return_data_copy_output_pointer",
.build_int_z_extend(value, context.word_type(), "calldatasize_extended")? )?;
.as_basic_value_enum()) context.build_runtime_call(
runtime_api::imports::RETURNDATASIZE,
&[output_pointer_parameter.into()],
);
context.build_load(output_pointer, "return_data_size_load")
} }
/// Translates the return data copy, trapping if /// Translates the return data copy, trapping if
@@ -39,16 +41,49 @@ where
let destination_offset = context.safe_truncate_int_to_xlen(destination_offset)?; let destination_offset = context.safe_truncate_int_to_xlen(destination_offset)?;
let size = context.safe_truncate_int_to_xlen(size)?; let size = context.safe_truncate_int_to_xlen(size)?;
let destination_offset = context.builder().build_ptr_to_int( let output_pointer = context.builder().build_ptr_to_int(
context.build_heap_gep(destination_offset, size)?.value, context.build_heap_gep(destination_offset, size)?.value,
context.xlen_type(), context.xlen_type(),
"destination_offset", "return_data_copy_output_pointer",
)?;
let output_length_pointer = context.build_alloca_at_entry(
context.xlen_type(),
"return_data_copy_output_length_pointer",
);
context.build_store(output_length_pointer, size)?;
let output_length_pointer_int = context.builder().build_ptr_to_int(
output_length_pointer.value,
context.xlen_type(),
"return_data_copy_output_length_pointer_int",
)?; )?;
context.build_runtime_call( context.build_runtime_call(
runtime_api::imports::RETURNDATACOPY, runtime_api::imports::RETURNDATACOPY,
&[destination_offset.into(), source_offset.into(), size.into()], &[
output_pointer.into(),
output_length_pointer_int.into(),
source_offset.into(),
],
); );
// Trap on OOB (will be different in EOF code)
let overflow_block = context.append_basic_block("return_data_overflow");
let non_overflow_block = context.append_basic_block("return_data_non_overflow");
let is_overflow = context.builder().build_int_compare(
inkwell::IntPredicate::UGT,
size,
context
.build_load(output_length_pointer, "bytes_written")?
.into_int_value(),
"is_overflow",
)?;
context.build_conditional_branch(is_overflow, overflow_block, non_overflow_block)?;
context.set_basic_block(overflow_block);
context.build_call(context.intrinsics().trap, &[], "invalid_trap");
context.build_unreachable();
context.set_basic_block(non_overflow_block);
Ok(()) Ok(())
} }
+54 -19
View File
@@ -275,10 +275,10 @@ impl Specs {
origin, origin,
value, value,
gas_limit, gas_limit,
storage_deposit_limit,
code, code,
data, data,
salt, salt,
..
} => { } => {
let Code::Solidity { let Code::Solidity {
path: Some(path), path: Some(path),
@@ -289,21 +289,41 @@ impl Specs {
else { else {
panic!("the differential runner requires Code::Solidity source"); panic!("the differential runner requires Code::Solidity source");
}; };
assert_ne!(solc_optimizer, Some(false), "solc_optimizer must be enabled in differntial mode");
assert_ne!(pipeline, Some(revive_solidity::SolcPipeline::EVMLA), "yul pipeline must be enabled in differntial mode"); assert_ne!(
assert!(storage_deposit_limit.is_none(), "storage deposit limit is not supported in differential mode"); solc_optimizer,
assert!(salt.0.is_none(), "salt is not supported in differential mode"); Some(false),
assert_eq!(origin, TestAddress::default(), "configuring the origin is not supported in differential mode"); "solc_optimizer must be enabled in differntial mode"
);
assert_ne!(
pipeline,
Some(revive_solidity::SolcPipeline::EVMLA),
"yul pipeline must be enabled in differntial mode"
);
assert!(
salt.0.is_none(),
"salt is not supported in differential mode"
);
assert_eq!(
origin,
TestAddress::default(),
"configuring the origin is not supported in differential mode"
);
let deploy_code = match std::fs::read_to_string(&path) { let deploy_code = match std::fs::read_to_string(&path) {
Ok(solidity_source) => compile_evm_deploy_code(&contract, &solidity_source), Ok(solidity_source) => {
hex::encode(compile_evm_deploy_code(&contract, &solidity_source))
}
Err(err) => panic!( Err(err) => panic!(
"failed to read solidity source\n . path: '{}'\n . error: {:?}", "failed to read solidity source\n . path: '{}'\n . error: {:?}",
path.display(), path.display(),
err err
), ),
}; };
let deploy_code = hex::encode(deploy_code); let mut vm = evm
let mut vm = evm.code_blob(deploy_code.as_bytes().to_vec()).sender(origin.to_eth_addr(&[]).0.into()).deploy(true); .code_blob(deploy_code.as_bytes().to_vec())
.sender(origin.to_eth_addr(&[]).0.into())
.deploy(true);
if !data.is_empty() { if !data.is_empty() {
vm = vm.input(data.into()); vm = vm.input(data.into());
} }
@@ -318,7 +338,13 @@ impl Specs {
let deployed_account = log.account_deployed.expect("no account was created"); let deployed_account = log.account_deployed.expect("no account was created");
let account_pvm = TestAddress::Instantiated(deployed_accounts.len()); let account_pvm = TestAddress::Instantiated(deployed_accounts.len());
deployed_accounts.push(deployed_account); deployed_accounts.push(deployed_account);
derived_specs.actions.append(&mut SpecsAction::derive_verification(&log, deployed_account, account_pvm)); derived_specs
.actions
.append(&mut SpecsAction::derive_verification(
&log,
deployed_account,
account_pvm,
));
evm = Evm::from_genesis(log.state_dump.into()); evm = Evm::from_genesis(log.state_dump.into());
} }
Call { Call {
@@ -326,16 +352,23 @@ impl Specs {
dest, dest,
value, value,
gas_limit, gas_limit,
storage_deposit_limit,
data, data,
..
} => { } => {
assert_eq!(origin, TestAddress::default(), "configuring the origin is not supported in differential mode"); assert_eq!(
assert!(storage_deposit_limit.is_none(), "storage deposit limit is not supported in differential mode"); origin,
TestAddress::default(),
"configuring the origin is not supported in differential mode"
);
let TestAddress::Instantiated(n) = dest else { let TestAddress::Instantiated(n) = dest else {
panic!("the differential runner requires TestAccountId::Instantiated(n) as dest"); panic!("the differential runner requires TestAccountId::Instantiated(n) as dest");
}; };
let address = deployed_accounts.get(n).unwrap_or_else(|| panic!("no account at index {n} ")); let address = deployed_accounts
let mut vm = evm.receiver(*address).sender(origin.to_eth_addr(&[]).0.into()); .get(n)
.unwrap_or_else(|| panic!("no account at index {n} "));
let mut vm = evm
.receiver(*address)
.sender(origin.to_eth_addr(&[]).0.into());
if !data.is_empty() { if !data.is_empty() {
vm = vm.input(data.into()); vm = vm.input(data.into());
} }
@@ -347,10 +380,13 @@ impl Specs {
} }
let log = vm.run(); let log = vm.run();
derived_specs.actions.append(&mut SpecsAction::derive_verification(&log, *address, dest)); derived_specs
.actions
.append(&mut SpecsAction::derive_verification(&log, *address, dest));
evm = Evm::from_genesis(log.state_dump.into()); evm = Evm::from_genesis(log.state_dump.into());
} }
_ => panic!("only instantiate and call action allowed in differential mode, got: {action:?}"), Upload { .. } => continue,
other => derived_specs.actions.push(other),
} }
} }
@@ -405,7 +441,7 @@ impl Specs {
pallet_revive::Code::Existing(_) => continue, pallet_revive::Code::Existing(_) => continue,
pallet_revive::Code::Upload(bytes) => bytes, pallet_revive::Code::Upload(bytes) => bytes,
}, },
storage_deposit_limit.unwrap_or_default(), storage_deposit_limit.unwrap_or(DEPOSIT_LIMIT),
) )
.unwrap_or_else(|error| panic!("code upload failed: {error:?}")), .unwrap_or_else(|error| panic!("code upload failed: {error:?}")),
Call { Call {
@@ -445,7 +481,6 @@ impl Specs {
expected, expected,
} => { } => {
let address = contract.to_eth_addr(&results); let address = contract.to_eth_addr(&results);
dbg!(contract.to_account_id(&results));
let Ok(value) = Contracts::get_storage(address, key) else { let Ok(value) = Contracts::get_storage(address, key) else {
panic!("error reading storage for address {address}"); panic!("error reading storage for address {address}");
}; };
+21 -47
View File
@@ -3,7 +3,7 @@ use inkwell::{
context::Context, context::Context,
module::Module, module::Module,
types::{BasicType, StructType}, types::{BasicType, StructType},
values::{BasicValue, PointerValue}, values::{BasicValueEnum, PointerValue},
}; };
/// Creates a module that sets the PolkaVM minimum stack size to [`size`] if linked in. /// Creates a module that sets the PolkaVM minimum stack size to [`size`] if linked in.
@@ -21,56 +21,33 @@ pub fn min_stack_size<'context>(
module module
} }
pub struct Spill<'a, 'ctx> { /// Helper for building function calls with stack spilled arguments.
/// - `pointer`: points to a struct of the packed argument struct type
/// - `type`: the packed argument struct type
/// - `arguments`: a correctly ordered list of the struct field values
pub fn spill<'ctx>(
builder: &Builder<'ctx>,
pointer: PointerValue<'ctx>, pointer: PointerValue<'ctx>,
builder: &'a Builder<'ctx>,
r#type: StructType<'ctx>, r#type: StructType<'ctx>,
current_field: u32, arguments: &[BasicValueEnum<'ctx>],
} ) -> anyhow::Result<()> {
for index in 0..r#type.get_field_types().len() {
impl<'a, 'ctx> Spill<'a, 'ctx> { let field_pointer = builder.build_struct_gep(
pub fn new(
builder: &'a Builder<'ctx>,
r#type: StructType<'ctx>,
name: &str,
) -> anyhow::Result<Self> {
Ok(Self {
pointer: builder.build_alloca(r#type, name)?,
builder,
r#type, r#type,
current_field: 0, pointer,
}) index as u32,
} &format!("spill_parameter_{}", index),
pub fn next<V: BasicValue<'ctx>>(mut self, value: V) -> anyhow::Result<Self> {
let field_pointer = self.builder.build_struct_gep(
self.r#type,
self.pointer,
self.current_field,
&format!("spill_parameter_{}", self.current_field),
)?; )?;
self.builder.build_store(field_pointer, value)?; let field_value = arguments
self.current_field += 1; .get(index)
Ok(self) .ok_or_else(|| anyhow::anyhow!("invalid index {index} for struct type {}", r#type))?;
builder.build_store(field_pointer, *field_value)?;
} }
pub fn skip(mut self) -> Self { Ok(())
self.current_field += 1;
self
}
pub fn done(self) -> PointerValue<'ctx> {
assert!(
self.r#type
.get_field_type_at_index(self.current_field)
.is_none(),
"there must not be any missing parameters"
);
self.pointer
}
} }
/// Returns a packed struct argument type for the `instantiate` API.
pub fn instantiate(context: &Context) -> StructType { pub fn instantiate(context: &Context) -> StructType {
context.struct_type( context.struct_type(
&[ &[
@@ -90,21 +67,18 @@ pub fn instantiate(context: &Context) -> StructType {
context.i32_type().as_basic_type_enum(), context.i32_type().as_basic_type_enum(),
// address_ptr: u32, // address_ptr: u32,
context.ptr_type(Default::default()).as_basic_type_enum(), context.ptr_type(Default::default()).as_basic_type_enum(),
// address_len_ptr: u32,
context.ptr_type(Default::default()).as_basic_type_enum(),
// output_ptr: u32, // output_ptr: u32,
context.ptr_type(Default::default()).as_basic_type_enum(), context.ptr_type(Default::default()).as_basic_type_enum(),
// output_len_ptr: u32, // output_len_ptr: u32,
context.ptr_type(Default::default()).as_basic_type_enum(), context.ptr_type(Default::default()).as_basic_type_enum(),
// salt_ptr: u32, // salt_ptr: u32,
context.ptr_type(Default::default()).as_basic_type_enum(), context.ptr_type(Default::default()).as_basic_type_enum(),
// salt_len: u32
context.i32_type().as_basic_type_enum(),
], ],
true, true,
) )
} }
/// Returns a packed struct argument type for the `call` API.
pub fn call(context: &Context) -> StructType { pub fn call(context: &Context) -> StructType {
context.struct_type( context.struct_type(
&[ &[
+3 -1
View File
@@ -55,7 +55,9 @@ POLKAVM_IMPORT(void, input, uint32_t, uint32_t)
POLKAVM_IMPORT(void, seal_return, uint32_t, 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, return_data_copy, uint32_t, uint32_t, uint32_t)
POLKAVM_IMPORT(void, return_data_size, uint32_t)
POLKAVM_IMPORT(void, value_transferred, uint32_t) POLKAVM_IMPORT(void, value_transferred, uint32_t)
@@ -1119,6 +1119,7 @@ where
value, value,
input_offset, input_offset,
input_length, input_length,
None,
) )
.map(Some) .map(Some)
} }
@@ -1130,7 +1131,7 @@ where
let input_length = arguments[2].into_int_value(); let input_length = arguments[2].into_int_value();
let salt = arguments[3].into_int_value(); let salt = arguments[3].into_int_value();
revive_llvm_context::polkavm_evm_create::create2( revive_llvm_context::polkavm_evm_create::create(
context, context,
value, value,
input_offset, input_offset,
@@ -831,6 +831,7 @@ impl FunctionCall {
value, value,
input_offset, input_offset,
input_length, input_length,
None,
) )
.map(Some) .map(Some)
} }
@@ -842,7 +843,7 @@ impl FunctionCall {
let input_length = arguments[2].into_int_value(); let input_length = arguments[2].into_int_value();
let salt = arguments[3].into_int_value(); let salt = arguments[3].into_int_value();
revive_llvm_context::polkavm_evm_create::create2( revive_llvm_context::polkavm_evm_create::create(
context, context,
value, value,
input_offset, input_offset,