-
Notifications
You must be signed in to change notification settings - Fork 1.1k
[pallet-revive] add EVM gas call syscalls #10422
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: torsten/gas-fixes
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| // This file is part of Substrate. | ||
|
|
||
| // Copyright (C) Parity Technologies (UK) Ltd. | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||
| // you may not use this file except in compliance with the License. | ||
| // You may obtain a copy of the License at | ||
| // | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, software | ||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
|
|
||
| #![no_std] | ||
| #![no_main] | ||
|
|
||
| include!("../panic_handler.rs"); | ||
|
|
||
| use uapi::{input, CallFlags, HostFn, HostFnImpl as api}; | ||
|
|
||
| #[no_mangle] | ||
| #[polkavm_derive::polkavm_export] | ||
| pub extern "C" fn deploy() {} | ||
|
|
||
| #[no_mangle] | ||
| #[polkavm_derive::polkavm_export] | ||
| pub extern "C" fn call() { | ||
| input!( | ||
| 256, | ||
| callee_addr: &[u8; 20], | ||
| gas: &[u8; 32], | ||
| ); | ||
|
|
||
| let mut value = [0; 32]; | ||
| api::value_transferred(&mut value); | ||
|
|
||
| api::call_evm(CallFlags::empty(), callee_addr, gas, &value, &[], None).unwrap(); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| // This file is part of Substrate. | ||
|
|
||
| // Copyright (C) Parity Technologies (UK) Ltd. | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||
| // you may not use this file except in compliance with the License. | ||
| // You may obtain a copy of the License at | ||
| // | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, software | ||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
|
|
||
| #![no_std] | ||
| #![no_main] | ||
| include!("../panic_handler.rs"); | ||
|
|
||
| use uapi::{input, CallFlags, HostFn, HostFnImpl as api, StorageFlags}; | ||
|
|
||
| #[no_mangle] | ||
| #[polkavm_derive::polkavm_export] | ||
| pub extern "C" fn deploy() {} | ||
|
|
||
| #[no_mangle] | ||
| #[polkavm_derive::polkavm_export] | ||
| pub extern "C" fn call() { | ||
| input!( | ||
| address: &[u8; 20], | ||
| gas: &[u8; 32], | ||
| ); | ||
|
|
||
| let mut key = [0u8; 32]; | ||
| key[0] = 1u8; | ||
|
|
||
| let mut value = [0u8; 32]; | ||
| let value = &mut &mut value[..]; | ||
| value[0] = 2u8; | ||
|
|
||
| api::set_storage(StorageFlags::empty(), &key, value); | ||
| api::get_storage(StorageFlags::empty(), &key, value).unwrap(); | ||
| assert!(value[0] == 2u8); | ||
|
|
||
| api::delegate_call_evm(CallFlags::empty(), address, gas, &[], None).unwrap(); | ||
|
|
||
| api::get_storage(StorageFlags::empty(), &key, value).unwrap(); | ||
| assert!(value[0] == 1u8); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -298,14 +298,49 @@ pub mod env { | |
| let (deposit_ptr, value_ptr) = extract_hi_lo(deposit_and_value); | ||
| let (input_data_len, input_data_ptr) = extract_hi_lo(input_data); | ||
| let (output_len_ptr, output_ptr) = extract_hi_lo(output_data); | ||
| let weight = Weight::from_parts(ref_time_limit, proof_size_limit); | ||
|
|
||
| self.charge_gas(RuntimeCosts::CopyFromContract(32))?; | ||
| let deposit_limit = memory.read_u256(deposit_ptr)?; | ||
|
|
||
| self.call( | ||
| memory, | ||
| CallFlags::from_bits(flags).ok_or(Error::<E::T>::InvalidCallFlags)?, | ||
| CallType::Call { value_ptr }, | ||
| callee_ptr, | ||
| deposit_ptr, | ||
| Weight::from_parts(ref_time_limit, proof_size_limit), | ||
| &CallResources::from_weight_and_deposit(weight, deposit_limit), | ||
| input_data_ptr, | ||
| input_data_len, | ||
| output_ptr, | ||
| output_len_ptr, | ||
| ) | ||
| } | ||
|
|
||
| /// Make a call to another contract. | ||
| /// See [`pallet_revive_uapi::HostFn::call_evm`]. | ||
| #[stable] | ||
| fn call_evm( | ||
| &mut self, | ||
| memory: &mut M, | ||
| flags: u32, | ||
| callee: u32, | ||
| value_ptr: u32, | ||
| gas_ptr: u32, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. shouldn't we pass gas as a u64?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why u64? It would move the overflow check into contract code.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was not thinking of solidity the uapi would be nicer if it was just since gas is just a u64. but then I guess since in revive you translate call instructions where the gas is a uint256 that would force you to do the overflow check in the contract
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah the gas argument is an u256 (since it's the only int type in Yul). However, actually considering it, an Such optimizations aren't planned for the initial Thinking it further, we could also use a "no gas" API method for when the gas to use is what
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All that make sense. when I wrote the comment I was mostly thinking of the use case of someone writing a contract in Rust with the low-level API where there I think it make sense to use a u64.
Yeah maybe in that case you call the regular call method with W(u64::max, u64::max) as it result in less math ops to compute the gas_left (@TorstenStueber might be better advisor here)
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah the new syscalls are specifically for Regarding the uncapped calls. I think this depends if we implement the "63/64" rule. Even if we don't implement it exactly like on Ethereum: The contract can't just supply the absolute maximum, instead it needs to be able to express that the call should get whatever is left.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds all good.
There is currently no such implementation itself. We can add a 63/64 later but the current logic is already complicated enough for the first iteration.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It's your call but If future compiler optimisation can optimize that away, picking the host call that use u64 might be interesting as well. If we revisit the whole pallet-transaction-payment, and gas mapping integration to make gas the main metering unit (instead of it being derived from being fees / gas_price) then this could also become a nice api for Rust contract as well |
||
| input_data: u64, | ||
| output_data: u64, | ||
| ) -> Result<ReturnErrorCode, TrapReason> { | ||
| let (input_data_len, input_data_ptr) = extract_hi_lo(input_data); | ||
| let (output_len_ptr, output_ptr) = extract_hi_lo(output_data); | ||
|
|
||
| self.charge_gas(RuntimeCosts::CopyFromContract(32))?; | ||
| let gas = memory.read_u256(gas_ptr)?; | ||
|
|
||
| self.call( | ||
| memory, | ||
| CallFlags::from_bits(flags).ok_or(Error::<E::T>::InvalidCallFlags)?, | ||
| CallType::Call { value_ptr }, | ||
| callee, | ||
| &CallResources::from_ethereum_gas(gas, true), | ||
| input_data_ptr, | ||
| input_data_len, | ||
| output_ptr, | ||
|
|
@@ -329,14 +364,48 @@ pub mod env { | |
| let (flags, address_ptr) = extract_hi_lo(flags_and_callee); | ||
| let (input_data_len, input_data_ptr) = extract_hi_lo(input_data); | ||
| let (output_len_ptr, output_ptr) = extract_hi_lo(output_data); | ||
| let weight = Weight::from_parts(ref_time_limit, proof_size_limit); | ||
|
|
||
| self.charge_gas(RuntimeCosts::CopyFromContract(32))?; | ||
| let deposit_limit = memory.read_u256(deposit_ptr)?; | ||
|
|
||
| self.call( | ||
| memory, | ||
| CallFlags::from_bits(flags).ok_or(Error::<E::T>::InvalidCallFlags)?, | ||
| CallType::DelegateCall, | ||
| address_ptr, | ||
| deposit_ptr, | ||
| Weight::from_parts(ref_time_limit, proof_size_limit), | ||
| &CallResources::from_weight_and_deposit(weight, deposit_limit), | ||
| input_data_ptr, | ||
| input_data_len, | ||
| output_ptr, | ||
| output_len_ptr, | ||
| ) | ||
| } | ||
|
|
||
| /// Same as `delegate_call` but with EVM gas. | ||
| /// See [`pallet_revive_uapi::HostFn::delegate_call_evm`]. | ||
| #[stable] | ||
| fn delegate_call_evm( | ||
| &mut self, | ||
| memory: &mut M, | ||
| flags: u32, | ||
| callee: u32, | ||
| gas_ptr: u32, | ||
| input_data: u64, | ||
| output_data: u64, | ||
| ) -> Result<ReturnErrorCode, TrapReason> { | ||
| let (input_data_len, input_data_ptr) = extract_hi_lo(input_data); | ||
| let (output_len_ptr, output_ptr) = extract_hi_lo(output_data); | ||
|
|
||
| self.charge_gas(RuntimeCosts::CopyFromContract(32))?; | ||
| let gas = memory.read_u256(gas_ptr)?; | ||
|
|
||
| self.call( | ||
| memory, | ||
| CallFlags::from_bits(flags).ok_or(Error::<E::T>::InvalidCallFlags)?, | ||
| CallType::DelegateCall, | ||
| callee, | ||
| &CallResources::from_ethereum_gas(gas, true), | ||
| input_data_ptr, | ||
| input_data_len, | ||
| output_ptr, | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.