unc_vm_runner/prepare/
prepare_v0.rs1use crate::logic::errors::PrepareError;
4use crate::logic::Config;
5use parity_wasm_41::builder;
6use parity_wasm_41::elements::{self, External, MemorySection, Type};
7use pwasm_utils_12 as pwasm_utils;
8
9pub(crate) fn prepare_contract(
10 original_code: &[u8],
11 config: &Config,
12) -> Result<Vec<u8>, PrepareError> {
13 ContractModule::init(original_code, config)?
14 .standardize_mem()
15 .ensure_no_internal_memory()?
16 .inject_gas_metering()?
17 .inject_stack_height_metering()?
18 .scan_imports()?
19 .into_wasm_code()
20}
21
22struct ContractModule<'a> {
23 module: elements::Module,
24 config: &'a Config,
25}
26
27impl<'a> ContractModule<'a> {
28 fn init(original_code: &[u8], config: &'a Config) -> Result<Self, PrepareError> {
29 let module = elements::deserialize_buffer(original_code).map_err(|e| {
30 tracing::debug!(err=?e, "parity_wasm_41 failed decoding a contract");
31 PrepareError::Deserialization
32 })?;
33 Ok(ContractModule { module, config })
34 }
35
36 fn standardize_mem(self) -> Self {
37 let Self { mut module, config } = self;
38
39 let mut tmp = MemorySection::default();
40
41 module.memory_section_mut().unwrap_or(&mut tmp).entries_mut().pop();
42
43 let entry = elements::MemoryType::new(
44 config.limit_config.initial_memory_pages,
45 Some(config.limit_config.max_memory_pages),
46 );
47
48 let mut builder = builder::from_module(module);
49 builder.push_import(elements::ImportEntry::new(
50 "env".to_string(),
51 "memory".to_string(),
52 elements::External::Memory(entry),
53 ));
54
55 Self { module: builder.build(), config }
56 }
57
58 fn ensure_no_internal_memory(self) -> Result<Self, PrepareError> {
64 if self.module.memory_section().map_or(false, |ms| !ms.entries().is_empty()) {
65 Err(PrepareError::InternalMemoryDeclared)
66 } else {
67 Ok(self)
68 }
69 }
70
71 fn inject_gas_metering(self) -> Result<Self, PrepareError> {
72 let Self { module, config } = self;
73 if config.regular_op_cost == 0 {
75 return Ok(Self { module, config });
76 }
77 let gas_rules = pwasm_utils::rules::Set::new(1, Default::default())
78 .with_grow_cost(config.grow_mem_cost);
79 let module = pwasm_utils::inject_gas_counter(module, &gas_rules)
80 .map_err(|_| PrepareError::GasInstrumentation)?;
81 Ok(Self { module, config })
82 }
83
84 fn inject_stack_height_metering(self) -> Result<Self, PrepareError> {
85 let Self { module, config } = self;
86 let module =
87 pwasm_utils::stack_height::inject_limiter(module, config.limit_config.max_stack_height)
88 .map_err(|_| PrepareError::StackHeightInstrumentation)?;
89 Ok(Self { module, config })
90 }
91
92 fn scan_imports(self) -> Result<Self, PrepareError> {
100 let Self { module, config } = self;
101
102 let types = module.type_section().map(elements::TypeSection::types).unwrap_or(&[]);
103 let import_entries =
104 module.import_section().map(elements::ImportSection::entries).unwrap_or(&[]);
105
106 let mut imported_mem_type = None;
107
108 for import in import_entries {
109 if import.module() != "env" {
110 return Err(PrepareError::Instantiate);
113 }
114
115 let type_idx = match *import.external() {
116 External::Function(ref type_idx) => type_idx,
117 External::Memory(ref memory_type) => {
118 imported_mem_type = Some(memory_type);
119 continue;
120 }
121 _ => continue,
122 };
123
124 let Type::Function(ref _func_ty) =
125 types.get(*type_idx as usize).ok_or(PrepareError::Instantiate)?;
126
127 }
139 if let Some(memory_type) = imported_mem_type {
140 let limits = memory_type.limits();
142 if limits.initial() != config.limit_config.initial_memory_pages
143 || limits.maximum() != Some(config.limit_config.max_memory_pages)
144 {
145 return Err(PrepareError::Memory);
146 }
147 } else {
148 return Err(PrepareError::Memory);
149 };
150 Ok(Self { module, config })
151 }
152
153 fn into_wasm_code(self) -> Result<Vec<u8>, PrepareError> {
154 elements::serialize(self.module).map_err(|_| PrepareError::Serialization)
155 }
156}