From d749fd7d2ea5b7cfd62f5fb708de4715ca8e599e Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Wed, 21 Dec 2022 15:54:32 +0200 Subject: [PATCH 01/13] integrity test for MaxCodeLen and CallStack::len() --- frame/contracts/src/lib.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index b76acf9d1db08..1c3b0bc30e5a1 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -123,6 +123,7 @@ use pallet_contracts_primitives::{ StorageDeposit, }; use scale_info::TypeInfo; +use smallvec::Array; use sp_core::crypto::UncheckedFrom; use sp_runtime::traits::{Convert, Hash, Saturating, StaticLookup}; use sp_std::{fmt::Debug, marker::PhantomData, prelude::*}; @@ -380,6 +381,37 @@ pub mod pallet { T::WeightInfo::on_process_deletion_queue_batch() } } + + fn integrity_test() { + const DEFAULT_HEAP_PAGES: u32 = 2048; + const PAGE_SIZE: u32 = 64 * 1024; + const MAX_RUNTIME_MEM: u32 = DEFAULT_HEAP_PAGES * PAGE_SIZE; + // Memory limits for a single contract + const STACK_MAX_SIZE: u32 = 16384 * 64; + let heap_max_size = T::Schedule::get().limits.memory_pages * PAGE_SIZE; + let stack_height = T::CallStack::size() as u32; + // In worst case, the decoded wasm contract code would be x16 times larger than the + // encoded one. This is because even a single-byte wasm instruction has 16-byte size in + // wasmi. Hence we need to check that with given `MaxCodeLen` and `CallStack`, this + // worst case won't break runtime heap memory limit. Not that maximum allowed heap + // memory and stack size per each contract (stack frame) should also be counted. + // + // This gives us the following expression: + // (MaxCodeLen * 16 + STACK_MAX_SIZE + heap_max_size) * stack_height < MAX_RUNTIME_MEM + let code_len_limit = MAX_RUNTIME_MEM + .saturating_div(stack_height) + .saturating_sub(heap_max_size) + .saturating_sub(STACK_MAX_SIZE) + .saturating_div(16); + + assert!( + T::MaxCodeLen::get() < code_len_limit, + "Given `CallStack` height {:?}, `MaxCodeLen` should be set less than {:?} (current value is {:?}).", + stack_height, + code_len_limit, + T::MaxCodeLen::get(), + ); + } } #[pallet::call] From 2f07003a7317542350382771bd71559514cbe186 Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Wed, 21 Dec 2022 16:04:47 +0200 Subject: [PATCH 02/13] integrity test for MaxDebugBufferLen --- frame/contracts/src/lib.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index 1c3b0bc30e5a1..62a1a82a2f96f 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -388,7 +388,7 @@ pub mod pallet { const MAX_RUNTIME_MEM: u32 = DEFAULT_HEAP_PAGES * PAGE_SIZE; // Memory limits for a single contract const STACK_MAX_SIZE: u32 = 16384 * 64; - let heap_max_size = T::Schedule::get().limits.memory_pages * PAGE_SIZE; + let heap_max_size = T::Schedule::get().limits.max_memory_size(); let stack_height = T::CallStack::size() as u32; // In worst case, the decoded wasm contract code would be x16 times larger than the // encoded one. This is because even a single-byte wasm instruction has 16-byte size in @@ -411,6 +411,12 @@ pub mod pallet { code_len_limit, T::MaxCodeLen::get(), ); + + assert!( + T::MaxDebugBufferLen::get() > heap_max_size, + "Debug buffer should be large enough to hold at least single message of a maximum size of {}", + heap_max_size, + ) } } From cdbc747cb81c4d9b8437c56c2bbbd308a8b1ed13 Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Thu, 12 Jan 2023 16:44:09 +0200 Subject: [PATCH 03/13] addressed review comments --- frame/contracts/src/lib.rs | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index 62a1a82a2f96f..979b3f9d4d6bb 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -383,40 +383,41 @@ pub mod pallet { } fn integrity_test() { - const DEFAULT_HEAP_PAGES: u32 = 2048; - const PAGE_SIZE: u32 = 64 * 1024; - const MAX_RUNTIME_MEM: u32 = DEFAULT_HEAP_PAGES * PAGE_SIZE; + // Total runtime memory is expected to have 1Gb upper limit + const MAX_RUNTIME_MEM: u32 = 1024 * 1024 * 1024; // Memory limits for a single contract - const STACK_MAX_SIZE: u32 = 16384 * 64; + // Value stack size: 1Mb per contract, default defined in wasmi + const STACK_MAX_SIZE: u32 = 1024 * 1024; + // Normally 16 mempages of 64kb = 1Mb per contract let heap_max_size = T::Schedule::get().limits.max_memory_size(); let stack_height = T::CallStack::size() as u32; // In worst case, the decoded wasm contract code would be x16 times larger than the // encoded one. This is because even a single-byte wasm instruction has 16-byte size in // wasmi. Hence we need to check that with given `MaxCodeLen` and `CallStack`, this - // worst case won't break runtime heap memory limit. Not that maximum allowed heap + // worst case won't break runtime heap memory limit. Also pallet stores the plain wasm + // code twice `PrefabWasmModule.code` and `PrefabWasmModule.original_code`, which gives + // us additional `MaxCodeLen*2` mem usage. Note that maximum allowed heap // memory and stack size per each contract (stack frame) should also be counted. + // Finally, we allow 50% of the runtime memory to be utilized by the contracts call + // stack, keeping the rest for other facilities, such as PoV and etc. // - // This gives us the following expression: - // (MaxCodeLen * 16 + STACK_MAX_SIZE + heap_max_size) * stack_height < MAX_RUNTIME_MEM + // This gives us the following formula: + // (MaxCodeLen * 18 + STACK_MAX_SIZE + heap_max_size) * stack_height < MAX_RUNTIME_MEM/2 let code_len_limit = MAX_RUNTIME_MEM + .saturating_div(2) .saturating_div(stack_height) .saturating_sub(heap_max_size) .saturating_sub(STACK_MAX_SIZE) - .saturating_div(16); + .saturating_div(18); assert!( T::MaxCodeLen::get() < code_len_limit, - "Given `CallStack` height {:?}, `MaxCodeLen` should be set less than {:?} (current value is {:?}).", + "Given `CallStack` height {:?}, `MaxCodeLen` should be set less than {:?} \ + (current value is {:?}), to avoid possible runtime oom issues.", stack_height, code_len_limit, T::MaxCodeLen::get(), ); - - assert!( - T::MaxDebugBufferLen::get() > heap_max_size, - "Debug buffer should be large enough to hold at least single message of a maximum size of {}", - heap_max_size, - ) } } From 0f04a5c429d88be3d873120e17d78e9d79039b9a Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Thu, 12 Jan 2023 17:41:40 +0200 Subject: [PATCH 04/13] fix append_debug_buffer() --- frame/contracts/src/exec.rs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/frame/contracts/src/exec.rs b/frame/contracts/src/exec.rs index 8ae70b804d69b..9ef141697e2cb 100644 --- a/frame/contracts/src/exec.rs +++ b/frame/contracts/src/exec.rs @@ -1336,7 +1336,18 @@ where fn append_debug_buffer(&mut self, msg: &str) -> bool { if let Some(buffer) = &mut self.debug_message { - let mut msg = msg.bytes(); + let err_msg = format!( + "Debug message too big (size={}) for debug buffer (bound={})", + msg.len(), + DebugBufferVec::::bound(), + ); + + let mut msg = if msg.len() > DebugBufferVec::::bound() { + err_msg.bytes() + } else { + msg.bytes() + }; + let num_drain = { let capacity = DebugBufferVec::::bound().checked_sub(buffer.len()).expect( " @@ -1349,16 +1360,7 @@ where msg.len().saturating_sub(capacity).min(buffer.len()) }; buffer.drain(0..num_drain); - buffer - .try_extend(&mut msg) - .map_err(|_| { - log::debug!( - target: "runtime::contracts", - "Debug message to big (size={}) for debug buffer (bound={})", - msg.len(), DebugBufferVec::::bound(), - ); - }) - .ok(); + buffer.try_extend(&mut msg).ok(); true } else { false From fe3e72a7405db6c030f9e6e0e7b6159d25b8341d Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Thu, 12 Jan 2023 18:03:25 +0200 Subject: [PATCH 05/13] ci fix --- frame/contracts/src/exec.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/contracts/src/exec.rs b/frame/contracts/src/exec.rs index 9ef141697e2cb..b5b630653667b 100644 --- a/frame/contracts/src/exec.rs +++ b/frame/contracts/src/exec.rs @@ -1336,7 +1336,7 @@ where fn append_debug_buffer(&mut self, msg: &str) -> bool { if let Some(buffer) = &mut self.debug_message { - let err_msg = format!( + let err_msg = scale_info::prelude::format!( "Debug message too big (size={}) for debug buffer (bound={})", msg.len(), DebugBufferVec::::bound(), From da876b5df71242f74ad174f3da3b928df88ae1b6 Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Thu, 12 Jan 2023 20:53:30 +0200 Subject: [PATCH 06/13] updated code_len_limit formula after further discussion --- frame/contracts/src/lib.rs | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index d0f11e1973677..e46cbbe84e070 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -377,30 +377,45 @@ pub mod pallet { fn integrity_test() { // Total runtime memory is expected to have 1Gb upper limit const MAX_RUNTIME_MEM: u32 = 1024 * 1024 * 1024; - // Memory limits for a single contract + // Memory limits for a single contract: // Value stack size: 1Mb per contract, default defined in wasmi const STACK_MAX_SIZE: u32 = 1024 * 1024; - // Normally 16 mempages of 64kb = 1Mb per contract + // Heap limit is normally 16 mempages of 64kb each = 1Mb per contract let heap_max_size = T::Schedule::get().limits.max_memory_size(); let stack_height = T::CallStack::size() as u32; - // In worst case, the decoded wasm contract code would be x16 times larger than the + // Check that given configured `MaxCodeLen`, runtime heap memory limit can't be broken. + // + // In worst case, the decoded wasm contract code would be `x16` times larger than the // encoded one. This is because even a single-byte wasm instruction has 16-byte size in - // wasmi. Hence we need to check that with given `MaxCodeLen` and `CallStack`, this - // worst case won't break runtime heap memory limit. Also pallet stores the plain wasm - // code twice `PrefabWasmModule.code` and `PrefabWasmModule.original_code`, which gives - // us additional `MaxCodeLen*2` mem usage. Note that maximum allowed heap - // memory and stack size per each contract (stack frame) should also be counted. + // wasmi. This gives us `MaxCodeLen*16` safety margin. + // + // Next, the pallet keeps both the original and instrumented wasm blobs for each + // contract, hence we add up `MaxCodeLen*2` more to the safety margin. + // + // Finally, the inefficiencies of the freeing-bump allocator + // being used in the client for the runtime memory allocations, could lead to possible + // memory grow up to `MaxCodeLen*4` for some contracts in extreme cases, wich should be + // taken into account as well. + // + // That being said, for every contract executed in runtime, at least `MaxCodeLen*22` + // memory should be avaiable. Note that maximum allowed heap memory and stack size per + // each contract (stack frame) should also be counted. + // // Finally, we allow 50% of the runtime memory to be utilized by the contracts call - // stack, keeping the rest for other facilities, such as PoV and etc. + // stack, keeping the rest for other facilities, such as PoV, etc. // // This gives us the following formula: - // (MaxCodeLen * 18 + STACK_MAX_SIZE + heap_max_size) * stack_height < MAX_RUNTIME_MEM/2 + // + // `(MaxCodeLen * 22 + STACK_MAX_SIZE + heap_max_size) * stack_height < + // MAX_RUNTIME_MEM/2` + // + // Hence the upper limit for the `MaxCodeLen` can be defined as follows: let code_len_limit = MAX_RUNTIME_MEM .saturating_div(2) .saturating_div(stack_height) .saturating_sub(heap_max_size) .saturating_sub(STACK_MAX_SIZE) - .saturating_div(18); + .saturating_div(22); assert!( T::MaxCodeLen::get() < code_len_limit, From eab6fafac0c1a70535f34706f2651635efbb5226 Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Fri, 13 Jan 2023 11:30:58 +0200 Subject: [PATCH 07/13] enlarged mem safe margin after discussion --- frame/contracts/src/lib.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index e46cbbe84e070..023c123c5f753 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -394,10 +394,10 @@ pub mod pallet { // // Finally, the inefficiencies of the freeing-bump allocator // being used in the client for the runtime memory allocations, could lead to possible - // memory grow up to `MaxCodeLen*4` for some contracts in extreme cases, wich should be - // taken into account as well. + // memory allocations for contract code grow up to `x4` times in some extreme cases, + // wich gives us total multiplier of `18*4` for `MaxCodeLen`. // - // That being said, for every contract executed in runtime, at least `MaxCodeLen*22` + // That being said, for every contract executed in runtime, at least `MaxCodeLen*18*4` // memory should be avaiable. Note that maximum allowed heap memory and stack size per // each contract (stack frame) should also be counted. // @@ -406,7 +406,7 @@ pub mod pallet { // // This gives us the following formula: // - // `(MaxCodeLen * 22 + STACK_MAX_SIZE + heap_max_size) * stack_height < + // `(MaxCodeLen * 18 * 4 + STACK_MAX_SIZE + heap_max_size) * stack_height < // MAX_RUNTIME_MEM/2` // // Hence the upper limit for the `MaxCodeLen` can be defined as follows: @@ -415,7 +415,7 @@ pub mod pallet { .saturating_div(stack_height) .saturating_sub(heap_max_size) .saturating_sub(STACK_MAX_SIZE) - .saturating_div(22); + .saturating_div(18 * 4); assert!( T::MaxCodeLen::get() < code_len_limit, From 9297ffb3ceb2f99fc6234a3a747e853e7042d923 Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Fri, 13 Jan 2023 12:46:37 +0200 Subject: [PATCH 08/13] +doc to Config trait associated types --- frame/contracts/src/lib.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index 023c123c5f753..837154f27c852 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -273,6 +273,9 @@ pub mod pallet { /// The allowed depth is `CallStack::size() + 1`. /// Therefore a size of `0` means that a contract cannot use call or instantiate. /// In other words only the origin called "root contract" is allowed to execute then. + /// + /// This setting along with [`MaxCodeLen`](#associatedtype.MaxCodeLen) directly affects + /// memory usage of your runtime. type CallStack: smallvec::Array>; /// The maximum number of contracts that can be pending for deletion. @@ -324,6 +327,10 @@ pub mod pallet { /// The maximum length of a contract code in bytes. This limit applies to the instrumented /// version of the code. Therefore `instantiate_with_code` can fail even when supplying /// a wasm binary below this maximum size. + /// + /// The value should be chosen carefully taking into the account the overall memory limit + /// your runtime has, as well as the [maximum allowed callstack + /// depth](#associatedtype.CallStack). Look into the `integrity_test()` for some insights. #[pallet::constant] type MaxCodeLen: Get; From 5b4aecfb4fb097071969513f31dbbb4fb27552f8 Mon Sep 17 00:00:00 2001 From: Sasha Gryaznov Date: Mon, 16 Jan 2023 13:20:54 +0200 Subject: [PATCH 09/13] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Alexander Theißen --- frame/contracts/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index 837154f27c852..8e03253729a09 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -388,8 +388,8 @@ pub mod pallet { // Value stack size: 1Mb per contract, default defined in wasmi const STACK_MAX_SIZE: u32 = 1024 * 1024; // Heap limit is normally 16 mempages of 64kb each = 1Mb per contract - let heap_max_size = T::Schedule::get().limits.max_memory_size(); - let stack_height = T::CallStack::size() as u32; + let max_heap_size = T::Schedule::get().limits.max_memory_size(); + let max_call_depth = T::CallStack::size() as u32; // Check that given configured `MaxCodeLen`, runtime heap memory limit can't be broken. // // In worst case, the decoded wasm contract code would be `x16` times larger than the @@ -419,8 +419,8 @@ pub mod pallet { // Hence the upper limit for the `MaxCodeLen` can be defined as follows: let code_len_limit = MAX_RUNTIME_MEM .saturating_div(2) - .saturating_div(stack_height) - .saturating_sub(heap_max_size) + .saturating_div(max_call_depth) + .saturating_sub(max_heap_size) .saturating_sub(STACK_MAX_SIZE) .saturating_div(18 * 4); From d010554c92bb0dedcd831506a9c99b16578b5573 Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Mon, 16 Jan 2023 13:39:37 +0200 Subject: [PATCH 10/13] more lil fixes from code review feedback --- frame/contracts/src/lib.rs | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index 8e03253729a09..84a2817a09935 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -276,7 +276,7 @@ pub mod pallet { /// /// This setting along with [`MaxCodeLen`](#associatedtype.MaxCodeLen) directly affects /// memory usage of your runtime. - type CallStack: smallvec::Array>; + type CallStack: Array>; /// The maximum number of contracts that can be pending for deletion. /// @@ -402,10 +402,10 @@ pub mod pallet { // Finally, the inefficiencies of the freeing-bump allocator // being used in the client for the runtime memory allocations, could lead to possible // memory allocations for contract code grow up to `x4` times in some extreme cases, - // wich gives us total multiplier of `18*4` for `MaxCodeLen`. + // which gives us total multiplier of `18*4` for `MaxCodeLen`. // // That being said, for every contract executed in runtime, at least `MaxCodeLen*18*4` - // memory should be avaiable. Note that maximum allowed heap memory and stack size per + // memory should be available. Note that maximum allowed heap memory and stack size per // each contract (stack frame) should also be counted. // // Finally, we allow 50% of the runtime memory to be utilized by the contracts call @@ -428,10 +428,24 @@ pub mod pallet { T::MaxCodeLen::get() < code_len_limit, "Given `CallStack` height {:?}, `MaxCodeLen` should be set less than {:?} \ (current value is {:?}), to avoid possible runtime oom issues.", - stack_height, + max_call_depth, code_len_limit, T::MaxCodeLen::get(), ); + + println!( + "size of err_msg is {}", + "Debug message too big (size={}) for debug buffer (bound={})".bytes().len() + ); + + // Debug buffer should at least be large enough to accomodate a simple error message + const MIN_DEBUG_BUF_SIZE: u32 = 256; + assert!( + T::MaxDebugBufferLen::get() > MIN_DEBUG_BUF_SIZE, + "Debug buffer should have minimum size of {} (current setting is {})", + MIN_DEBUG_BUF_SIZE, + T::MaxDebugBufferLen::get(), + ) } } From 9e387356b273d1efea30a18897c97a90560f7781 Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Mon, 16 Jan 2023 17:24:24 +0200 Subject: [PATCH 11/13] lowered max call depth to satisfy mem limits --- frame/contracts/src/lib.rs | 15 +++++---------- frame/contracts/src/schedule.rs | 4 ---- frame/contracts/src/tests.rs | 4 ++-- 3 files changed, 7 insertions(+), 16 deletions(-) diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index 84a2817a09935..99b11e06b9187 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -382,11 +382,11 @@ pub mod pallet { } fn integrity_test() { - // Total runtime memory is expected to have 1Gb upper limit - const MAX_RUNTIME_MEM: u32 = 1024 * 1024 * 1024; + // Total runtime memory is expected to have 128Mb upper limit + const MAX_RUNTIME_MEM: u32 = 1024 * 1024 * 128; // Memory limits for a single contract: // Value stack size: 1Mb per contract, default defined in wasmi - const STACK_MAX_SIZE: u32 = 1024 * 1024; + const MAX_STACK_SIZE: u32 = 1024 * 1024; // Heap limit is normally 16 mempages of 64kb each = 1Mb per contract let max_heap_size = T::Schedule::get().limits.max_memory_size(); let max_call_depth = T::CallStack::size() as u32; @@ -413,7 +413,7 @@ pub mod pallet { // // This gives us the following formula: // - // `(MaxCodeLen * 18 * 4 + STACK_MAX_SIZE + heap_max_size) * stack_height < + // `(MaxCodeLen * 18 * 4 + MAX_STACK_SIZE + max_heap_size) * max_call_depth < // MAX_RUNTIME_MEM/2` // // Hence the upper limit for the `MaxCodeLen` can be defined as follows: @@ -421,7 +421,7 @@ pub mod pallet { .saturating_div(2) .saturating_div(max_call_depth) .saturating_sub(max_heap_size) - .saturating_sub(STACK_MAX_SIZE) + .saturating_sub(MAX_STACK_SIZE) .saturating_div(18 * 4); assert!( @@ -433,11 +433,6 @@ pub mod pallet { T::MaxCodeLen::get(), ); - println!( - "size of err_msg is {}", - "Debug message too big (size={}) for debug buffer (bound={})".bytes().len() - ); - // Debug buffer should at least be large enough to accomodate a simple error message const MIN_DEBUG_BUF_SIZE: u32 = 256; assert!( diff --git a/frame/contracts/src/schedule.rs b/frame/contracts/src/schedule.rs index 912e58b048ff4..0d180e1bf5cef 100644 --- a/frame/contracts/src/schedule.rs +++ b/frame/contracts/src/schedule.rs @@ -134,9 +134,6 @@ pub struct Limits { /// The maximum length of a subject in bytes used for PRNG generation. pub subject_len: u32, - /// The maximum nesting level of the call stack. - pub call_depth: u32, - /// The maximum size of a storage value and event payload in bytes. pub payload_len: u32, } @@ -526,7 +523,6 @@ impl Default for Limits { table_size: 4096, br_table_size: 256, subject_len: 32, - call_depth: 32, payload_len: 16 * 1024, } } diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index 7a0736d1ed61a..9c7494f527e2a 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -394,7 +394,7 @@ impl Config for Test { type RuntimeEvent = RuntimeEvent; type RuntimeCall = RuntimeCall; type CallFilter = TestFilter; - type CallStack = [Frame; 31]; + type CallStack = [Frame; 6]; type WeightPrice = Self; type WeightInfo = (); type ChainExtension = @@ -405,7 +405,7 @@ impl Config for Test { type DepositPerByte = DepositPerByte; type DepositPerItem = DepositPerItem; type AddressGenerator = DefaultAddressGenerator; - type MaxCodeLen = ConstU32<{ 128 * 1024 }>; + type MaxCodeLen = ConstU32<{ 123 * 1024 }>; type MaxStorageKeyLen = ConstU32<128>; type UnsafeUnstableInterface = UnstableInterface; type MaxDebugBufferLen = ConstU32<{ 2 * 1024 * 1024 }>; From 10ad19afa9b149f21240fc20e2bb967c52ac9b42 Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Mon, 16 Jan 2023 21:42:18 +0200 Subject: [PATCH 12/13] fix node runtime pallet params to satisfy integrity check --- bin/node/runtime/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index a9e93a16f0713..39626830b1307 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1206,7 +1206,7 @@ impl pallet_contracts::Config for Runtime { type CallFilter = Nothing; type DepositPerItem = DepositPerItem; type DepositPerByte = DepositPerByte; - type CallStack = [pallet_contracts::Frame; 31]; + type CallStack = [pallet_contracts::Frame; 6]; type WeightPrice = pallet_transaction_payment::Pallet; type WeightInfo = pallet_contracts::weights::SubstrateWeight; type ChainExtension = (); @@ -1214,7 +1214,7 @@ impl pallet_contracts::Config for Runtime { type DeletionWeightLimit = DeletionWeightLimit; type Schedule = Schedule; type AddressGenerator = pallet_contracts::DefaultAddressGenerator; - type MaxCodeLen = ConstU32<{ 128 * 1024 }>; + type MaxCodeLen = ConstU32<{ 123 * 1024 }>; type MaxStorageKeyLen = ConstU32<128>; type UnsafeUnstableInterface = ConstBool; type MaxDebugBufferLen = ConstU32<{ 2 * 1024 * 1024 }>; From 8d3eace503c3956fbeda321686fc46faa62cebc6 Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Mon, 16 Jan 2023 22:46:16 +0200 Subject: [PATCH 13/13] fix max call depth value calc --- bin/node/runtime/src/lib.rs | 2 +- frame/contracts/src/lib.rs | 5 ++++- frame/contracts/src/tests.rs | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 39626830b1307..96345456fbfc7 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1206,7 +1206,7 @@ impl pallet_contracts::Config for Runtime { type CallFilter = Nothing; type DepositPerItem = DepositPerItem; type DepositPerByte = DepositPerByte; - type CallStack = [pallet_contracts::Frame; 6]; + type CallStack = [pallet_contracts::Frame; 5]; type WeightPrice = pallet_transaction_payment::Pallet; type WeightInfo = pallet_contracts::weights::SubstrateWeight; type ChainExtension = (); diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index 99b11e06b9187..870bd2ab28581 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -389,7 +389,10 @@ pub mod pallet { const MAX_STACK_SIZE: u32 = 1024 * 1024; // Heap limit is normally 16 mempages of 64kb each = 1Mb per contract let max_heap_size = T::Schedule::get().limits.max_memory_size(); - let max_call_depth = T::CallStack::size() as u32; + // Max call depth is CallStack::size() + 1 + let max_call_depth = u32::try_from(T::CallStack::size().saturating_add(1)) + .expect("CallStack size is too big"); + // Check that given configured `MaxCodeLen`, runtime heap memory limit can't be broken. // // In worst case, the decoded wasm contract code would be `x16` times larger than the diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index 9c7494f527e2a..698e47b026967 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -394,7 +394,7 @@ impl Config for Test { type RuntimeEvent = RuntimeEvent; type RuntimeCall = RuntimeCall; type CallFilter = TestFilter; - type CallStack = [Frame; 6]; + type CallStack = [Frame; 5]; type WeightPrice = Self; type WeightInfo = (); type ChainExtension =