1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of OpenEthereum.

// OpenEthereum is free software: you can redistribute it and/or modify
// 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.

// OpenEthereum is distributed in the hope that it will be useful,
// 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
// along with OpenEthereum.  If not, see <http://www.gnu.org/licenses/>.

//! ActionParams parser for wasm

use parity_wasm::{
    elements::{self, Deserialize},
    peek_size,
};
use vm;
use wasm_utils::{self, rules};

fn gas_rules(wasm_costs: &vm::WasmCosts) -> rules::Set {
    rules::Set::new(wasm_costs.regular, {
        let mut vals = ::std::collections::BTreeMap::new();
        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),
        );
        vals
    })
    .with_grow_cost(wasm_costs.grow_mem)
    .with_forbidden_floats()
}

/// 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.
pub fn payload<'a>(
    params: &'a vm::ActionParams,
    wasm_costs: &vm::WasmCosts,
) -> 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),
    };

    let deserialized_module = elements::Module::deserialize(&mut cursor)
        .map_err(|err| vm::Error::Wasm(format!("Error deserializing contract code ({:?})", err)))?;

    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))
            .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")))?;

    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))
}