unc_vm_runner/
prepare.rs

1//! Module that takes care of loading, checking and preprocessing of a
2//! wasm module before execution.
3
4use crate::logic::errors::PrepareError;
5use unc_parameters::vm::{Config, VMKind};
6
7mod prepare_v0;
8mod prepare_v1;
9mod prepare_v2;
10
11/// Loads the given module given in `original_code`, performs some checks on it and
12/// does some preprocessing.
13///
14/// The checks are:
15///
16/// - module doesn't define an internal memory instance,
17/// - imported memory (if any) doesn't reserve more memory than permitted by the `config`,
18/// - all imported functions from the external environment matches defined by `env` module,
19/// - functions number does not exceed limit specified in Config,
20///
21/// The preprocessing includes injecting code for gas metering and metering the height of stack.
22pub fn prepare_contract(
23    original_code: &[u8],
24    config: &Config,
25    kind: VMKind,
26) -> Result<Vec<u8>, PrepareError> {
27    let prepare = config.limit_config.contract_prepare_version;
28    // UncVM => ContractPrepareVersion::V2
29    assert!(
30        (kind != VMKind::UncVm) || (prepare == crate::logic::ContractPrepareVersion::V2),
31        "UncVM only works with contract prepare version V2",
32    );
33    let features = crate::features::WasmFeatures::from(prepare);
34    match prepare {
35        crate::logic::ContractPrepareVersion::V0 => {
36            // NB: v1 here is not a bug, we are reusing the code.
37            prepare_v1::validate_contract(original_code, features, config)?;
38            prepare_v0::prepare_contract(original_code, config)
39        }
40        crate::logic::ContractPrepareVersion::V1 => {
41            prepare_v1::validate_contract(original_code, features, config)?;
42            prepare_v1::prepare_contract(original_code, config)
43        }
44        crate::logic::ContractPrepareVersion::V2 => {
45            prepare_v2::prepare_contract(original_code, features, config, kind)
46        }
47    }
48}
49
50#[cfg(test)]
51mod tests {
52    use super::*;
53    use crate::tests::{test_vm_config, with_vm_variants};
54    use assert_matches::assert_matches;
55
56    fn parse_and_prepare_wat(
57        config: &Config,
58        vm_kind: VMKind,
59        wat: &str,
60    ) -> Result<Vec<u8>, PrepareError> {
61        let wasm = wat::parse_str(wat).unwrap();
62        prepare_contract(wasm.as_ref(), &config, vm_kind)
63    }
64
65    #[test]
66    fn internal_memory_declaration() {
67        let config = test_vm_config();
68        with_vm_variants(&config, |kind| {
69            let r = parse_and_prepare_wat(&config, kind, r#"(module (memory 1 1))"#);
70            assert_matches!(r, Ok(_));
71        })
72    }
73
74    #[test]
75    fn memory_imports() {
76        let config = test_vm_config();
77
78        // This test assumes that maximum page number is configured to a certain number.
79        assert_eq!(config.limit_config.max_memory_pages, 2048);
80
81        with_vm_variants(&config, |kind| {
82            let r = parse_and_prepare_wat(
83                &config,
84                kind,
85                r#"(module (import "env" "memory" (memory 1 1)))"#,
86            );
87            assert_matches!(r, Err(PrepareError::Memory));
88
89            // No memory import
90            let r = parse_and_prepare_wat(&config, kind, r#"(module)"#);
91            assert_matches!(r, Ok(_));
92
93            // initial exceed maximum
94            let r = parse_and_prepare_wat(
95                &config,
96                kind,
97                r#"(module (import "env" "memory" (memory 17 1)))"#,
98            );
99            assert_matches!(r, Err(PrepareError::Deserialization));
100
101            // no maximum
102            let r = parse_and_prepare_wat(
103                &config,
104                kind,
105                r#"(module (import "env" "memory" (memory 1)))"#,
106            );
107            assert_matches!(r, Err(PrepareError::Memory));
108
109            // requested maximum exceed configured maximum
110            let r = parse_and_prepare_wat(
111                &config,
112                kind,
113                r#"(module (import "env" "memory" (memory 1 33)))"#,
114            );
115            assert_matches!(r, Err(PrepareError::Memory));
116        })
117    }
118
119    #[test]
120    fn multiple_valid_memory_are_disabled() {
121        let config = test_vm_config();
122        with_vm_variants(&config, |kind| {
123            // Our preparation and sanitization pass assumes a single memory, so we should fail when
124            // there are multiple specified.
125            let r = parse_and_prepare_wat(
126                &config,
127                kind,
128                r#"(module
129                    (import "env" "memory" (memory 1 2048))
130                    (import "env" "memory" (memory 1 2048))
131                )"#,
132            );
133            assert_matches!(r, Err(_));
134            let r = parse_and_prepare_wat(
135                &config,
136                kind,
137                r#"(module
138                    (import "env" "memory" (memory 1 2048))
139                    (memory 1)
140                )"#,
141            );
142            assert_matches!(r, Err(_));
143        })
144    }
145
146    #[test]
147    fn imports() {
148        let config = test_vm_config();
149        with_vm_variants(&config, |kind| {
150            // nothing can be imported from non-"env" module for now.
151            let r = parse_and_prepare_wat(
152                &config,
153                kind,
154                r#"(module (import "another_module" "memory" (memory 1 1)))"#,
155            );
156            assert_matches!(r, Err(PrepareError::Instantiate));
157
158            let r = parse_and_prepare_wat(
159                &config,
160                kind,
161                r#"(module (import "env" "gas" (func (param i32))))"#,
162            );
163            assert_matches!(r, Ok(_));
164
165            // TODO: Address tests once we check proper function signatures.
166            /*
167            // wrong signature
168            let r = parse_and_prepare_wat(r#"(module (import "env" "gas" (func (param i64))))"#);
169            assert_matches!(r, Err(Error::Instantiate));
170
171            // unknown function name
172            let r = parse_and_prepare_wat(r#"(module (import "env" "unknown_func" (func)))"#);
173            assert_matches!(r, Err(Error::Instantiate));
174            */
175        })
176    }
177}