Skip to content

Commit

Permalink
feat(pallets,core): separately charge for wasm module sections instan…
Browse files Browse the repository at this point in the history
…tiation (#3937)
  • Loading branch information
ByteNacked authored Jul 8, 2024
1 parent cc0f995 commit 64ace22
Show file tree
Hide file tree
Showing 36 changed files with 1,282 additions and 176 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 19 additions & 4 deletions core-processor/src/configs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,23 @@ pub struct ExtCosts {
pub mem_grow_per_page: CostOf<WasmPagesAmount>,
}

/// Module instantiation costs.
#[derive(Debug, Default, Clone)]
pub struct InstantiationCosts {
/// WASM module code section instantiation per byte cost.
pub code_section_per_byte: CostOf<BytesAmount>,
/// WASM module data section instantiation per byte cost.
pub data_section_per_byte: CostOf<BytesAmount>,
/// WASM module global section instantiation per byte cost.
pub global_section_per_byte: CostOf<BytesAmount>,
/// WASM module table section instantiation per byte cost.
pub table_section_per_byte: CostOf<BytesAmount>,
/// WASM module element section instantiation per byte cost.
pub element_section_per_byte: CostOf<BytesAmount>,
/// WASM module type section instantiation per byte cost.
pub type_section_per_byte: CostOf<BytesAmount>,
}

/// Costs for message processing
#[derive(Clone, Debug, Default)]
pub struct ProcessCosts {
Expand All @@ -79,10 +96,8 @@ pub struct ProcessCosts {
pub instrumentation: CostOf<CallsAmount>,
/// Code instrumentation per byte cost.
pub instrumentation_per_byte: CostOf<BytesAmount>,
/// Static page cost.
pub static_page: CostOf<WasmPagesAmount>,
/// WASM module instantiation per byte cost.
pub module_instantiation_per_byte: CostOf<BytesAmount>,
/// Module instantiation costs.
pub instantiation_costs: InstantiationCosts,
}

/// Execution settings for handling messages.
Expand Down
16 changes: 4 additions & 12 deletions core-processor/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,11 @@ impl ContextChargedForCodeLength {
/// successfully charged for fetching the binary code from storage.
pub struct ContextChargedForCode {
pub(crate) data: ContextData,
pub(crate) code_len_bytes: u32,
}

impl From<(ContextChargedForCodeLength, u32)> for ContextChargedForCode {
fn from((context, code_len_bytes): (ContextChargedForCodeLength, u32)) -> Self {
Self {
data: context.data,
code_len_bytes,
}
impl From<ContextChargedForCodeLength> for ContextChargedForCode {
fn from(context: ContextChargedForCodeLength) -> Self {
Self { data: context.data }
}
}

Expand All @@ -70,15 +66,11 @@ impl From<(ContextChargedForCodeLength, u32)> for ContextChargedForCode {
/// successfully charged for reinstrumentation of the code.
pub struct ContextChargedForInstrumentation {
pub(crate) data: ContextData,
pub(crate) code_len_bytes: u32,
}

impl From<ContextChargedForCode> for ContextChargedForInstrumentation {
fn from(context: ContextChargedForCode) -> Self {
Self {
data: context.data,
code_len_bytes: context.code_len_bytes,
}
Self { data: context.data }
}
}

Expand Down
2 changes: 1 addition & 1 deletion core-processor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ pub use ext::{
pub use handler::handle_journal;
pub use precharge::{
precharge_for_code, precharge_for_code_length, precharge_for_instrumentation,
precharge_for_memory, precharge_for_program, SuccessfulDispatchResultKind,
precharge_for_module_instantiation, precharge_for_program, SuccessfulDispatchResultKind,
};
pub use processing::{
process, process_execution_error, process_non_executable, process_reinstrumentation_error,
Expand Down
124 changes: 58 additions & 66 deletions core-processor/src/precharge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ use crate::{
};
use alloc::vec::Vec;
use gear_core::{
code::{InstantiatedSectionSizes, SectionName},
costs::BytesAmount,
gas::{ChargeResult, GasAllowanceCounter, GasCounter},
ids::ProgramId,
message::{IncomingDispatch, MessageWaitedType},
pages::{numerated::tree::IntervalsTree, WasmPage, WasmPagesAmount},
};

/// Operation related to gas charging.
Expand All @@ -50,9 +50,9 @@ pub enum PreChargeGasOperation {
/// Handle program code.
#[display(fmt = "handle program code")]
ProgramCode,
/// Instantiate Wasm module.
#[display(fmt = "instantiate Wasm module")]
ModuleInstantiation,
/// Instantiate the type section of the Wasm module.
#[display(fmt = "instantiate {_0} of Wasm module")]
ModuleInstantiation(SectionName),
/// Instrument Wasm module.
#[display(fmt = "instrument Wasm module")]
ModuleInstrumentation,
Expand Down Expand Up @@ -124,13 +124,29 @@ impl<'a> GasPrecharger<'a> {
)
}

pub fn charge_gas_for_instantiation(
pub fn charge_gas_for_section_instantiation(
&mut self,
code_len: BytesAmount,
section_name: SectionName,
section_len: BytesAmount,
) -> Result<(), PrechargeError> {
let instantiation_costs = &self.costs.instantiation_costs;

let cost_per_byte = match section_name {
SectionName::Function => &instantiation_costs.code_section_per_byte,
SectionName::Data => &instantiation_costs.data_section_per_byte,
SectionName::Global => &instantiation_costs.global_section_per_byte,
SectionName::Table => &instantiation_costs.table_section_per_byte,
SectionName::Element => &instantiation_costs.element_section_per_byte,
SectionName::Type => &instantiation_costs.type_section_per_byte,
_ => {
// TODO: change this to a system error in future
unimplemented!("Wrong {section_name:?} for section instantiation")
}
};

self.charge_gas(
PreChargeGasOperation::ModuleInstantiation,
self.costs.module_instantiation_per_byte.cost_for(code_len),
PreChargeGasOperation::ModuleInstantiation(section_name),
cost_per_byte.cost_for(section_len),
)
}

Expand All @@ -145,24 +161,6 @@ impl<'a> GasPrecharger<'a> {
.cost_for_with_bytes(self.costs.instrumentation_per_byte, original_code_len_bytes),
)
}

/// Charge gas for pages and checks that there is enough gas for that.
/// Returns size of wasm memory buffer which must be created in execution environment.
pub fn charge_gas_for_pages(
&mut self,
allocations: &IntervalsTree<WasmPage>,
static_pages: WasmPagesAmount,
) -> Result<WasmPagesAmount, PrechargeError> {
// Charging gas for static pages.
let amount = self.costs.static_page.cost_for(static_pages);
self.charge_gas(PreChargeGasOperation::StaticPages, amount)?;

if let Some(page) = allocations.end() {
Ok(page.inc())
} else {
Ok(static_pages)
}
}
}

/// Possible variants of the `DispatchResult` if the latter contains value.
Expand Down Expand Up @@ -289,7 +287,7 @@ pub fn precharge_for_code(
);

match charger.charge_gas_for_program_code(code_len_bytes.into()) {
Ok(()) => Ok((context, code_len_bytes).into()),
Ok(()) => Ok(context.into()),
Err(PrechargeError::BlockGasExceeded) => Err(process_allowance_exceed(
context.data.dispatch,
context.data.destination_id,
Expand Down Expand Up @@ -343,9 +341,10 @@ pub fn precharge_for_instrumentation(
}

/// Charge a message for program memory and module instantiation beforehand.
pub fn precharge_for_memory(
pub fn precharge_for_module_instantiation(
block_config: &BlockConfig,
mut context: ContextChargedForInstrumentation,
section_sizes: &InstantiatedSectionSizes,
) -> PrechargeResult<ContextChargedForMemory> {
let ContextChargedForInstrumentation {
data:
Expand All @@ -355,17 +354,44 @@ pub fn precharge_for_memory(
actor_data,
..
},
code_len_bytes,
..
} = &mut context;

let mut f = || {
let mut charger =
GasPrecharger::new(gas_counter, gas_allowance_counter, &block_config.costs);

let memory_size =
charger.charge_gas_for_pages(&actor_data.allocations, actor_data.static_pages)?;
// Calculates size of wasm memory buffer which must be created in execution environment
let memory_size = if let Some(page) = actor_data.allocations.end() {
page.inc()
} else {
actor_data.static_pages
};

charger.charge_gas_for_instantiation((*code_len_bytes).into())?;
charger.charge_gas_for_section_instantiation(
SectionName::Function,
section_sizes.code_section.into(),
)?;
charger.charge_gas_for_section_instantiation(
SectionName::Data,
section_sizes.data_section.into(),
)?;
charger.charge_gas_for_section_instantiation(
SectionName::Global,
section_sizes.global_section.into(),
)?;
charger.charge_gas_for_section_instantiation(
SectionName::Table,
section_sizes.table_section.into(),
)?;
charger.charge_gas_for_section_instantiation(
SectionName::Element,
section_sizes.element_section.into(),
)?;
charger.charge_gas_for_section_instantiation(
SectionName::Type,
section_sizes.type_section.into(),
)?;

Ok(memory_size)
};
Expand Down Expand Up @@ -402,37 +428,3 @@ pub fn precharge_for_memory(
}
}
}

#[cfg(test)]
mod tests {
use super::*;

fn prepare_gas_counters() -> (GasCounter, GasAllowanceCounter) {
(
GasCounter::new(1_000_000),
GasAllowanceCounter::new(4_000_000),
)
}

#[test]
fn gas_for_static_pages() {
let (mut gas_counter, mut gas_allowance_counter) = prepare_gas_counters();
let costs = ProcessCosts {
static_page: 1.into(),
..Default::default()
};
let mut charger = GasPrecharger::new(&mut gas_counter, &mut gas_allowance_counter, &costs);
let static_pages = 4.into();
let allocations = Default::default();

let res = charger.charge_gas_for_pages(&allocations, static_pages);

// Result is static pages count
assert_eq!(res, Ok(static_pages));

// Charging for static pages initialization
let charge = costs.static_page.cost_for(static_pages);
assert_eq!(charger.counter.left(), 1_000_000 - charge);
assert_eq!(charger.allowance_counter.left(), 4_000_000 - charge);
}
}
34 changes: 31 additions & 3 deletions core/src/code/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,29 @@ pub use gear_wasm_instrument::{parity_wasm::SerializationError, InstrumentationE
pub use wasmparser::BinaryReaderError;

/// Section name in WASM module.
#[derive(Debug, derive_more::Display)]
#[derive(PartialEq, Eq, Debug, derive_more::Display)]
pub enum SectionName {
/// Type section.
#[display(fmt = "Type section")]
Type,
/// Import section.
#[display(fmt = "Import section")]
Import,
/// Function section.
/// Function (Code) section.
#[display(fmt = "Function section")]
Function,
/// Data section.
#[display(fmt = "Data section")]
Data,
/// Global section.
#[display(fmt = "Global section")]
Global,
/// Table section.
#[display(fmt = "Table section")]
Table,
/// Element section.
#[display(fmt = "Element section")]
Element,
/// Export section.
#[display(fmt = "Export section")]
Export,
Expand Down Expand Up @@ -77,7 +89,7 @@ pub enum StackEndError {
OutOfStatic(u32, u64),
}

/// Stack end error in WASM module.
/// Data section error in WASM module.
#[derive(Debug, derive_more::Display)]
pub enum DataSectionError {
/// Unsupported initialization of data segment.
Expand All @@ -102,6 +114,19 @@ pub enum DataSectionError {
},
}

/// Table section error in WASM module.
#[derive(Debug, derive_more::Display)]
pub enum TableSectionError {
/// Number of table exceeds the limit.
#[display(fmt = "Number of table limit exceeded: limit={limit}, actual={actual}")]
TableNumberLimit {
/// Limit on the number of tables.
limit: u32,
/// Actual number of tables.
actual: u32,
},
}

/// Export error in WASM module.
#[derive(Debug, derive_more::Display)]
pub enum ExportError {
Expand Down Expand Up @@ -182,6 +207,9 @@ pub enum CodeError {
/// The provided code contains data section error.
#[display(fmt = "Data section error: {_0}")]
DataSection(DataSectionError),
/// The provided code contains table section error.
#[display(fmt = "Table section error: {_0}")]
TableSection(TableSectionError),
/// The provided code contains export error.
#[display(fmt = "Export error: {_0}")]
Export(ExportError),
Expand Down
Loading

0 comments on commit 64ace22

Please sign in to comment.