2020-09-22 14:53:52 +02:00
|
|
|
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
|
|
|
// This file is part of OpenEthereum.
|
2018-02-05 20:59:27 +01:00
|
|
|
|
2020-09-22 14:53:52 +02:00
|
|
|
// OpenEthereum is free software: you can redistribute it and/or modify
|
2018-02-05 20:59:27 +01:00
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
|
2020-09-22 14:53:52 +02:00
|
|
|
// OpenEthereum is distributed in the hope that it will be useful,
|
2018-02-05 20:59:27 +01:00
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
|
|
|
|
// You should have received a copy of the GNU General Public License
|
2020-09-22 14:53:52 +02:00
|
|
|
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
2018-02-05 20:59:27 +01:00
|
|
|
|
|
|
|
//! ActionParams parser for wasm
|
|
|
|
|
|
|
|
use parity_wasm::{
|
|
|
|
elements::{self, Deserialize},
|
2020-08-05 06:08:03 +02:00
|
|
|
peek_size,
|
|
|
|
};
|
2018-02-05 20:59:27 +01:00
|
|
|
use vm;
|
|
|
|
use wasm_utils::{self, rules};
|
|
|
|
|
2018-02-19 12:27:42 +01:00
|
|
|
fn gas_rules(wasm_costs: &vm::WasmCosts) -> rules::Set {
|
2018-03-12 12:37:32 +01:00
|
|
|
rules::Set::new(wasm_costs.regular, {
|
2018-07-03 17:31:08 +02:00
|
|
|
let mut vals = ::std::collections::BTreeMap::new();
|
2018-03-12 12:37:32 +01:00
|
|
|
vals.insert(
|
|
|
|
rules::InstructionType::Load,
|
|
|
|
rules::Metering::Fixed(wasm_costs.mem as u32),
|
|
|
|
);
|
|
|
|
vals.insert(
|
|
|
|
rules::InstructionType::Store,
|
|
|
|
rules::Metering::Fixed(wasm_costs.mem as u32),
|
|
|
|
);
|
|
|
|
vals.insert(
|
|
|
|
rules::InstructionType::Div,
|
|
|
|
rules::Metering::Fixed(wasm_costs.div as u32),
|
|
|
|
);
|
|
|
|
vals.insert(
|
|
|
|
rules::InstructionType::Mul,
|
|
|
|
rules::Metering::Fixed(wasm_costs.mul as u32),
|
2020-08-05 06:08:03 +02:00
|
|
|
);
|
2018-03-12 12:37:32 +01:00
|
|
|
vals
|
|
|
|
})
|
|
|
|
.with_grow_cost(wasm_costs.grow_mem)
|
|
|
|
.with_forbidden_floats()
|
2018-02-05 20:59:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Splits payload to code and data according to params.params_type, also
|
|
|
|
/// loads the module instance from payload and injects gas counter according
|
|
|
|
/// to schedule.
|
2018-02-19 12:27:42 +01:00
|
|
|
pub fn payload<'a>(
|
|
|
|
params: &'a vm::ActionParams,
|
|
|
|
wasm_costs: &vm::WasmCosts,
|
2018-02-05 20:59:27 +01:00
|
|
|
) -> Result<(elements::Module, &'a [u8]), vm::Error> {
|
|
|
|
let code = match params.code {
|
|
|
|
Some(ref code) => &code[..],
|
|
|
|
None => {
|
|
|
|
return Err(vm::Error::Wasm("Invalid wasm call".to_owned()));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let (mut cursor, data_position) = match params.params_type {
|
|
|
|
vm::ParamsType::Embedded => {
|
|
|
|
let module_size = peek_size(&*code);
|
|
|
|
(::std::io::Cursor::new(&code[..module_size]), module_size)
|
|
|
|
}
|
|
|
|
vm::ParamsType::Separate => (::std::io::Cursor::new(&code[..]), 0),
|
|
|
|
};
|
|
|
|
|
2018-02-12 11:19:43 +01:00
|
|
|
let deserialized_module = elements::Module::deserialize(&mut cursor)
|
2018-02-05 20:59:27 +01:00
|
|
|
.map_err(|err| vm::Error::Wasm(format!("Error deserializing contract code ({:?})", err)))?;
|
2018-02-12 11:19:43 +01:00
|
|
|
|
|
|
|
if deserialized_module
|
|
|
|
.memory_section()
|
|
|
|
.map_or(false, |ms| ms.entries().len() > 0)
|
|
|
|
{
|
|
|
|
// According to WebAssembly spec, internal memory is hidden from embedder and should not
|
|
|
|
// be interacted with. So we disable this kind of modules at decoding level.
|
|
|
|
return Err(vm::Error::Wasm(format!(
|
|
|
|
"Malformed wasm module: internal memory"
|
|
|
|
)));
|
|
|
|
}
|
|
|
|
|
|
|
|
let contract_module =
|
|
|
|
wasm_utils::inject_gas_counter(deserialized_module, &gas_rules(wasm_costs))
|
2018-03-12 12:37:32 +01:00
|
|
|
.map_err(|_| vm::Error::Wasm(format!("Wasm contract error: bytecode invalid")))?;
|
|
|
|
|
|
|
|
let contract_module =
|
|
|
|
wasm_utils::stack_height::inject_limiter(contract_module, wasm_costs.max_stack_height)
|
|
|
|
.map_err(|_| vm::Error::Wasm(format!("Wasm contract error: stack limiter failure")))?;
|
2018-02-05 20:59:27 +01:00
|
|
|
|
|
|
|
let data = match params.params_type {
|
|
|
|
vm::ParamsType::Embedded => {
|
|
|
|
if data_position < code.len() {
|
|
|
|
&code[data_position..]
|
|
|
|
} else {
|
|
|
|
&[]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
vm::ParamsType::Separate => match params.data {
|
|
|
|
Some(ref s) => &s[..],
|
|
|
|
None => &[],
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok((contract_module, data))
|
2018-06-04 10:19:50 +02:00
|
|
|
}
|