radix-engine 1.3.1

Reference implementation of Radix Engine, from the Radix DLT project.
Documentation
use crate::internal_prelude::*;
use crate::vm::wasm::*;
use crate::vm::ScryptoVmVersion;
use radix_engine_interface::blueprints::package::BlueprintDefinitionInit;

pub struct ScryptoV1WasmValidator {
    pub max_memory_size_in_pages: u32,
    pub max_initial_table_size: u32,
    pub max_number_of_br_table_targets: u32,
    pub max_number_of_functions: u32,
    pub max_number_of_function_params: u32,
    pub max_number_of_function_locals: u32,
    pub max_number_of_globals: u32,
    pub instrumenter_config: WasmValidatorConfigV1,
    pub version: ScryptoVmVersion,
}

impl ScryptoV1WasmValidator {
    pub fn new(version: ScryptoVmVersion) -> Self {
        if version > ScryptoVmVersion::latest() {
            panic!("Invalid minor version: {:?}", version);
        }

        Self {
            max_memory_size_in_pages: MAX_MEMORY_SIZE_IN_PAGES,
            max_initial_table_size: MAX_INITIAL_TABLE_SIZE,
            max_number_of_br_table_targets: MAX_NUMBER_OF_BR_TABLE_TARGETS,
            max_number_of_functions: MAX_NUMBER_OF_FUNCTIONS,
            max_number_of_function_params: MAX_NUMBER_OF_FUNCTION_PARAMS,
            max_number_of_function_locals: MAX_NUMBER_OF_FUNCTION_LOCALS,
            max_number_of_globals: MAX_NUMBER_OF_GLOBALS,
            instrumenter_config: WasmValidatorConfigV1::new(),
            version,
        }
    }
}

impl ScryptoV1WasmValidator {
    pub fn validate<'a, I: Iterator<Item = &'a BlueprintDefinitionInit>>(
        &self,
        code: &[u8],
        blueprints: I,
    ) -> Result<(Vec<u8>, Vec<String>), PrepareError> {
        WasmModule::init(code)?
            .enforce_no_start_function()?
            .enforce_import_constraints(self.version)?
            .enforce_export_names()?
            .enforce_memory_limit_and_inject_max(self.max_memory_size_in_pages)?
            .enforce_table_limit(self.max_initial_table_size)?
            .enforce_br_table_limit(self.max_number_of_br_table_targets)?
            .enforce_function_limit(
                self.max_number_of_functions,
                self.max_number_of_function_params,
                self.max_number_of_function_locals,
            )?
            .enforce_global_limit(self.max_number_of_globals)?
            .enforce_export_constraints(blueprints)?
            .inject_instruction_metering(&self.instrumenter_config)?
            .inject_stack_metering(self.instrumenter_config.max_stack_size())?
            .ensure_instantiatable()?
            .ensure_compilable()?
            .to_bytes()
    }
}

#[cfg(test)]
mod tests {
    use radix_engine_interface::blueprints::package::PackageDefinition;

    use super::ScryptoV1WasmValidator;
    use super::ScryptoVmVersion;

    #[test]
    fn test_validate() {
        let code = wat::parse_str(
            r#"
        (module

            ;; Simple function that always returns `()`
            (func $Test_f (param $0 i64) (result i64)
              ;; Grow memory
              (drop
                (memory.grow (i32.const 1000000))
              )

              ;; Encode () in SBOR at address 0x0
              (i32.const 0)
              (i32.const 92)  ;; prefix
              (i32.store8)
              (i32.const 1)
              (i32.const 33)  ;; tuple value kind
              (i32.store8)
              (i32.const 2)
              (i32.const 0)  ;; tuple length
              (i32.store8)

              ;; Return slice (ptr = 0, len = 3)
              (i64.const 3)
            )

            (memory $0 1)
            (export "memory" (memory $0))
            (export "Test_f" (func $Test_f))
        )"#,
        )
        .unwrap();

        let instrumented_code = wasmprinter::print_bytes(
            ScryptoV1WasmValidator::new(ScryptoVmVersion::latest())
                .validate(
                    &code,
                    PackageDefinition::new_single_function_test_definition("Test", "f")
                        .blueprints
                        .values(),
                )
                .unwrap()
                .0,
        )
        .unwrap();

        assert_eq!(
            instrumented_code,
            r#"(module
  (type (;0;) (func (param i64) (result i64)))
  (type (;1;) (func (param i64)))
  (import "env" "gas" (func (;0;) (type 1)))
  (memory (;0;) 1 64)
  (global (;0;) (mut i32) i32.const 0)
  (export "memory" (memory 0))
  (export "Test_f" (func 2))
  (func (;1;) (type 0) (param i64) (result i64)
    i64.const 14788284
    call 0
    i32.const 1000000
    memory.grow
    drop
    i32.const 0
    i32.const 92
    i32.store8
    i32.const 1
    i32.const 33
    i32.store8
    i32.const 2
    i32.const 0
    i32.store8
    i64.const 3
  )
  (func (;2;) (type 0) (param i64) (result i64)
    local.get 0
    global.get 0
    i32.const 4
    i32.add
    global.set 0
    global.get 0
    i32.const 1024
    i32.gt_u
    if ;; label = @1
      unreachable
    end
    call 1
    global.get 0
    i32.const 4
    i32.sub
    global.set 0
  )
)
"#
        )
    }
}