From caebdfcc90394a980ae5df5d0b6a1ea9ca333450 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89lo=C3=AFs?= Date: Wed, 11 Sep 2024 15:55:41 +0200 Subject: [PATCH 1/4] propagate OOG from pov and MBIP5 --- Cargo.lock | 1 + frame/evm/src/runner/stack.rs | 51 ++++++++++++++++-------------- primitives/evm/Cargo.toml | 3 ++ primitives/evm/src/lib.rs | 3 ++ primitives/evm/src/storage_oog.rs | 52 +++++++++++++++++++++++++++++++ 5 files changed, 87 insertions(+), 23 deletions(-) create mode 100644 primitives/evm/src/storage_oog.rs diff --git a/Cargo.lock b/Cargo.lock index 91e7dbb4a9..5cf4a3b808 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2840,6 +2840,7 @@ dependencies = [ name = "fp-evm" version = "3.0.0-dev" dependencies = [ + "environmental", "evm", "frame-support", "num_enum", diff --git a/frame/evm/src/runner/stack.rs b/frame/evm/src/runner/stack.rs index 41c18bb907..35f7f2e9c1 100644 --- a/frame/evm/src/runner/stack.rs +++ b/frame/evm/src/runner/stack.rs @@ -262,7 +262,6 @@ where let fee = T::OnChargeTransaction::withdraw_fee(&source, total_fee) .map_err(|e| RunnerError { error: e, weight })?; - // Execute the EVM call. let vicinity = Vicinity { gas_price: base_fee, origin: source, @@ -281,28 +280,34 @@ where let state = SubstrateStackState::new(&vicinity, metadata, maybe_weight_info, storage_limit); let mut executor = StackExecutor::new_with_precompiles(state, config, precompiles); - let (reason, retv) = f(&mut executor); - - // Compute the storage gas cost based on the storage growth. - let storage_gas = match &executor.state().storage_meter { - Some(storage_meter) => storage_meter.storage_to_gas(storage_growth_ratio), - None => 0, - }; - - let pov_gas = match executor.state().weight_info() { - Some(weight_info) => weight_info - .proof_size_usage - .unwrap_or_default() - .saturating_mul(T::GasLimitPovSizeRatio::get()), - None => 0, - }; - - // Post execution. - let used_gas = executor.used_gas(); - let effective_gas = U256::from(core::cmp::max( - core::cmp::max(used_gas, pov_gas), - storage_gas, - )); + // Execute the EVM call. + let (reason, retv, used_gas, effective_gas) = + fp_evm::handle_storage_oog::(gas_limit, || { + let (reason, retv) = f(&mut executor); + + // Compute the storage gas cost based on the storage growth. + let storage_gas = match &executor.state().storage_meter { + Some(storage_meter) => storage_meter.storage_to_gas(storage_growth_ratio), + None => 0, + }; + + let pov_gas = match executor.state().weight_info() { + Some(weight_info) => weight_info + .proof_size_usage + .unwrap_or_default() + .saturating_mul(T::GasLimitPovSizeRatio::get()), + None => 0, + }; + + // Post execution. + let used_gas = executor.used_gas(); + let effective_gas = U256::from(core::cmp::max( + core::cmp::max(used_gas, pov_gas), + storage_gas, + )); + + (reason, retv, used_gas, effective_gas) + }); let actual_fee = effective_gas.saturating_mul(total_fee_per_gas); let actual_base_fee = effective_gas.saturating_mul(base_fee); diff --git a/primitives/evm/Cargo.toml b/primitives/evm/Cargo.toml index 9cacc4e27d..e90f783679 100644 --- a/primitives/evm/Cargo.toml +++ b/primitives/evm/Cargo.toml @@ -12,10 +12,12 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] evm = { workspace = true, features = ["with-codec"] } +environmental = { workspace = true } num_enum = { workspace = true, default-features = false } scale-codec = { package = "parity-scale-codec", workspace = true } scale-info = { workspace = true } serde = { workspace = true, optional = true } + # Substrate frame-support = { workspace = true } sp-core = { workspace = true } @@ -26,6 +28,7 @@ default = ["std"] std = [ "evm/std", "evm/with-serde", + "environmental/std", "num_enum/std", "serde/std", "scale-codec/std", diff --git a/primitives/evm/src/lib.rs b/primitives/evm/src/lib.rs index 5b2402f6f8..799e26c358 100644 --- a/primitives/evm/src/lib.rs +++ b/primitives/evm/src/lib.rs @@ -21,6 +21,7 @@ extern crate alloc; mod precompile; +mod storage_oog; mod validation; use alloc::{collections::BTreeMap, vec::Vec}; @@ -43,6 +44,7 @@ pub use self::{ Precompile, PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileResult, PrecompileSet, Transfer, }, + storage_oog::handle_storage_oog, validation::{ CheckEvmTransaction, CheckEvmTransactionConfig, CheckEvmTransactionInput, TransactionValidationError, @@ -140,6 +142,7 @@ impl WeightInfo { { let proof_size_usage = self.try_consume(cost, proof_size_limit, proof_size_usage)?; if proof_size_usage > proof_size_limit { + storage_oog::set_storage_oog(); return Err(ExitError::OutOfGas); } self.proof_size_usage = Some(proof_size_usage); diff --git a/primitives/evm/src/storage_oog.rs b/primitives/evm/src/storage_oog.rs new file mode 100644 index 0000000000..14c4fa1a17 --- /dev/null +++ b/primitives/evm/src/storage_oog.rs @@ -0,0 +1,52 @@ +// This file is part of Frontier. + +// 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. + +environmental::environmental!(STORAGE_OOG: bool); + +use crate::{ExitError, ExitReason}; +use sp_core::U256; + +pub fn handle_storage_oog(gas_limit: u64, f: F) -> (ExitReason, R, u64, U256) +where + F: FnOnce() -> (ExitReason, R, u64, U256), + R: Default, +{ + STORAGE_OOG::using_once(&mut false, || { + let (reason, retv, used_gas, effective_gas) = f(); + + STORAGE_OOG::with(|storage_oog| { + if *storage_oog { + ( + ExitReason::Error(ExitError::OutOfGas), + Default::default(), + used_gas, + U256([gas_limit, 0, 0, 0]), + ) + } else { + (reason, retv, used_gas, effective_gas) + } + }) + // This should always return `Some`, but let's play it safe. + .expect("STORAGE_OOG not defined") + }) +} + +pub(super) fn set_storage_oog() { + STORAGE_OOG::with(|storage_oog| { + *storage_oog = true; + }); +} From 264ca1448ba3ec4a5ebbfcd68f139c3d996f6ab8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89lo=C3=AFs?= Date: Thu, 12 Sep 2024 16:39:09 +0200 Subject: [PATCH 2/4] propagate OOG from storage meter --- frame/evm/src/runner/meter.rs | 9 +++++---- primitives/evm/src/lib.rs | 2 +- primitives/evm/src/storage_oog.rs | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/frame/evm/src/runner/meter.rs b/frame/evm/src/runner/meter.rs index 6011a7919a..a7b91e30b6 100644 --- a/frame/evm/src/runner/meter.rs +++ b/frame/evm/src/runner/meter.rs @@ -51,12 +51,13 @@ impl StorageMeter { /// Records the given amount of storage usage. The amount is added to the current usage. /// If the limit is reached, an error is returned. pub fn record(&mut self, amount: u64) -> Result<(), MeterError> { - let usage = self - .usage - .checked_add(amount) - .ok_or(MeterError::LimitExceeded)?; + let usage = self.usage.checked_add(amount).ok_or_else(|| { + fp_evm::set_storage_oog(); + MeterError::LimitExceeded + })?; if usage > self.limit { + fp_evm::set_storage_oog(); return Err(MeterError::LimitExceeded); } self.usage = usage; diff --git a/primitives/evm/src/lib.rs b/primitives/evm/src/lib.rs index 799e26c358..3390d2e144 100644 --- a/primitives/evm/src/lib.rs +++ b/primitives/evm/src/lib.rs @@ -44,7 +44,7 @@ pub use self::{ Precompile, PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileResult, PrecompileSet, Transfer, }, - storage_oog::handle_storage_oog, + storage_oog::{handle_storage_oog, set_storage_oog}, validation::{ CheckEvmTransaction, CheckEvmTransactionConfig, CheckEvmTransactionInput, TransactionValidationError, diff --git a/primitives/evm/src/storage_oog.rs b/primitives/evm/src/storage_oog.rs index 14c4fa1a17..dd4ad32092 100644 --- a/primitives/evm/src/storage_oog.rs +++ b/primitives/evm/src/storage_oog.rs @@ -45,7 +45,7 @@ where }) } -pub(super) fn set_storage_oog() { +pub fn set_storage_oog() { STORAGE_OOG::with(|storage_oog| { *storage_oog = true; }); From d3e3ec34036425de591b96f471e1fb4a8b9755b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89lo=C3=AFs?= Date: Fri, 13 Sep 2024 12:19:34 +0200 Subject: [PATCH 3/4] propagate oog for more pov cases --- frame/evm/src/runner/stack.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/frame/evm/src/runner/stack.rs b/frame/evm/src/runner/stack.rs index 35f7f2e9c1..6d2f40f90b 100644 --- a/frame/evm/src/runner/stack.rs +++ b/frame/evm/src/runner/stack.rs @@ -1143,6 +1143,7 @@ where let actual_size = Pallet::::account_code_metadata(address).size; if actual_size > pre_size { + fp_evm::set_storage_oog(); return Err(ExitError::OutOfGas); } // Refund unused proof size From 09d0188f26534e686ea2ab458ea15d54487be4fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89lo=C3=AFs?= Date: Mon, 16 Sep 2024 14:15:03 +0200 Subject: [PATCH 4/4] set storage oog for try_consume check case --- primitives/evm/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/primitives/evm/src/lib.rs b/primitives/evm/src/lib.rs index 3390d2e144..2d7b3869e4 100644 --- a/primitives/evm/src/lib.rs +++ b/primitives/evm/src/lib.rs @@ -118,6 +118,7 @@ impl WeightInfo { fn try_consume(&self, cost: u64, limit: u64, usage: u64) -> Result { let usage = usage.checked_add(cost).ok_or(ExitError::OutOfGas)?; if usage > limit { + storage_oog::set_storage_oog(); return Err(ExitError::OutOfGas); } Ok(usage)