Skip to content
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

[pallet_contracts] Increase the weight of the deposit_event host function to limit the memory used by events. #4973

Merged
merged 13 commits into from
Aug 6, 2024
15 changes: 15 additions & 0 deletions prdoc/pr_4973.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0
# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json

title: "[pallet_contracts] Increase the weight of the deposit_event host function to limit the memory used by events."

doc:
- audience: Runtime User
description: |
This PR updates the weight of the deposit_event host function by adding
a fixed ref_time of 60,000 picoseconds per byte. Given a block time of 2 seconds
and this specified ref_time, the total allocation size is 32MB.

crates:
- name: pallet-contracts
bump: patch
60 changes: 58 additions & 2 deletions substrate/frame/contracts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ use crate::{
},
gas::GasMeter,
storage::{meter::Meter as StorageMeter, ContractInfo, DeletionQueueManager},
wasm::{CodeInfo, WasmBlob},
wasm::{CodeInfo, RuntimeCosts, WasmBlob},
};
use codec::{Codec, Decode, Encode, HasCompact, MaxEncodedLen};
use environmental::*;
Expand Down Expand Up @@ -656,7 +656,63 @@ pub mod pallet {
"Debug buffer should have minimum size of {} (current setting is {})",
MIN_DEBUG_BUF_SIZE,
T::MaxDebugBufferLen::get(),
)
);

// Validators have configured runtime memory for 512MB. They need to keep in it
// max_runtime_mem, PoV in multiple copies (1x encoded + 2x decoded), and storage
// including events. The assumption is that max_runtime_mem + storage/events can be
// a maximum of half of the validator runtime memory.
smiasojed marked this conversation as resolved.
Show resolved Hide resolved
let max_block_ref_time = T::BlockWeights::get()
.get(DispatchClass::Normal)
.max_total
.unwrap_or_else(|| T::BlockWeights::get().max_block)
.ref_time();
let max_payload_size = T::Schedule::get().limits.payload_len;
// Blake2_128Concat is being used.
let max_key_size = T::MaxStorageKeyLen::get().saturating_add(128);
athei marked this conversation as resolved.
Show resolved Hide resolved

// We can use storage to store items using the available block ref_time with the
// `set_storage` host function.
let max_storage_size: u32 = ((max_block_ref_time /
(<RuntimeCosts as gas::Token<T>>::weight(&RuntimeCosts::SetStorage {
new_bytes: max_payload_size,
old_bytes: 0,
})
.ref_time()))
.saturating_mul(max_payload_size.saturating_add(max_key_size) as u64))
.try_into()
.expect("Storage size too big");

let max_validator_runtime_mem: u32 = T::Schedule::get().limits.validator_runtime_memory;
let storage_size_limit =
(max_validator_runtime_mem / 2).saturating_sub(max_runtime_mem);

assert!(
max_storage_size < storage_size_limit,
"Maximal storage size {} exceeds the storage limit {}",
max_storage_size,
storage_size_limit
);

// We can use storage to store events using the available block ref_time with the
// `deposit_event` host function. The overhead of stored events, which is around 100B,
// is not taken into account to simplify calculations, as it does not change much.
let max_events_size: u32 = ((max_block_ref_time /
(<RuntimeCosts as gas::Token<T>>::weight(&RuntimeCosts::DepositEvent {
num_topic: 0,
len: max_payload_size,
})
.ref_time()))
.saturating_mul(max_payload_size as u64))
.try_into()
.expect("Events size too big");

assert!(
max_events_size < storage_size_limit,
"Maximal events size {} exceeds the events limit {}",
max_events_size,
storage_size_limit
);
}
}

Expand Down
5 changes: 5 additions & 0 deletions substrate/frame/contracts/src/schedule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ pub struct Limits {
/// The maximum node runtime memory. This is for integrity checks only and does not affect the
/// real setting.
pub runtime_memory: u32,

/// The maximum validator node runtime memory. This is for integrity checks only and does not
/// affect the real setting.
pub validator_runtime_memory: u32,
}

impl Limits {
Expand Down Expand Up @@ -121,6 +125,7 @@ impl Default for Limits {
subject_len: 32,
payload_len: 16 * 1024,
runtime_memory: 1024 * 1024 * 128,
validator_runtime_memory: 1024 * 1024 * 512,
}
}
}
Expand Down
5 changes: 4 additions & 1 deletion substrate/frame/contracts/src/wasm/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,10 @@ impl<T: Config> Token<T> for RuntimeCosts {
WeightToFee => T::WeightInfo::seal_weight_to_fee(),
Terminate(locked_dependencies) => T::WeightInfo::seal_terminate(locked_dependencies),
Random => T::WeightInfo::seal_random(),
DepositEvent { num_topic, len } => T::WeightInfo::seal_deposit_event(num_topic, len),
// Given a 2-second block time and hardcoding a `ref_time` of 60,000 picoseconds per
// byte, the max allocation size is 32MB per block.
DepositEvent { num_topic, len } => T::WeightInfo::seal_deposit_event(num_topic, len)
.saturating_add(Weight::from_parts(60_000u64.saturating_mul(len.into()), 0)),
athei marked this conversation as resolved.
Show resolved Hide resolved
DebugMessage(len) => T::WeightInfo::seal_debug_message(len),
SetStorage { new_bytes, old_bytes } =>
T::WeightInfo::seal_set_storage(new_bytes, old_bytes),
Expand Down
Loading