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
pub mod wasm_costs;

use std::fmt::{self, Display, Formatter};

use parity_wasm::elements::{self, Module};
use pwasm_utils::{self, stack_height};

use crate::wasm_costs::WasmCosts;

//NOTE: size of Wasm memory page is 64 KiB
pub const MEM_PAGES: u32 = 64;

#[derive(Debug)]
pub enum PreprocessingError {
    Deserialize(String),
    OperationForbiddenByGasRules,
    StackLimiter,
}

impl From<elements::Error> for PreprocessingError {
    fn from(error: elements::Error) -> Self {
        PreprocessingError::Deserialize(error.to_string())
    }
}

impl Display for PreprocessingError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match self {
            PreprocessingError::Deserialize(error) => write!(f, "Deserialization error: {}", error),
            PreprocessingError::OperationForbiddenByGasRules => write!(f, "Encountered operation forbidden by gas rules. Consult instruction -> metering config map"),
            PreprocessingError::StackLimiter => write!(f, "Stack limiter error"),
        }
    }
}

pub struct Preprocessor {
    wasm_costs: WasmCosts,
    // Number of memory pages.
    mem_pages: u32,
}

impl Preprocessor {
    pub fn new(wasm_costs: WasmCosts) -> Self {
        Self {
            wasm_costs,
            mem_pages: MEM_PAGES,
        }
    }

    pub fn preprocess(&self, module_bytes: &[u8]) -> Result<Module, PreprocessingError> {
        let module = deserialize(module_bytes)?;
        let module = pwasm_utils::externalize_mem(module, None, self.mem_pages);
        let module = pwasm_utils::inject_gas_counter(module, &self.wasm_costs.to_set())
            .map_err(|_| PreprocessingError::OperationForbiddenByGasRules)?;
        let module = stack_height::inject_limiter(module, self.wasm_costs.max_stack_height)
            .map_err(|_| PreprocessingError::StackLimiter)?;
        Ok(module)
    }
}

// Returns a parity Module from bytes without making modifications or limits
pub fn deserialize(module_bytes: &[u8]) -> Result<Module, PreprocessingError> {
    parity_wasm::deserialize_buffer::<Module>(module_bytes).map_err(Into::into)
}