diff --git a/aquamarine-vm/src/aquamarine_stepper_vm.rs b/aquamarine-vm/src/aquamarine_stepper_vm.rs index e87115e8..bb1c2bb8 100644 --- a/aquamarine-vm/src/aquamarine_stepper_vm.rs +++ b/aquamarine-vm/src/aquamarine_stepper_vm.rs @@ -28,8 +28,8 @@ use fluence_faas::IValue; use std::convert::TryInto; use std::path::PathBuf; use std::path::Path; +use crate::errors::AquamarineVMError::InvalidAquamarinePath; -const AQUAMARINE_WASM_FILE_NAME: &str = "aquamarine"; const CALL_SERVICE_NAME: &str = "call_service"; const CURRENT_PEER_ID_ENV_NAME: &str = "CURRENT_PEER_ID"; @@ -38,6 +38,8 @@ unsafe impl Send for AquamarineVM {} pub struct AquamarineVM { faas: FluenceFaaS, particle_data_store: PathBuf, + /// file name of the AIR interpreter .wasm + wasm_filename: String, } impl AquamarineVM { @@ -45,8 +47,11 @@ impl AquamarineVM { pub fn new(config: AquamarineVMConfig) -> Result { use AquamarineVMError::InvalidDataStorePath; - let faas_config = Self::make_faas_config( - config.aquamarine_wasm_path, + let (wasm_dir, wasm_filename) = split_dirname(config.aquamarine_wasm_path)?; + + let faas_config = make_faas_config( + wasm_dir, + &wasm_filename, config.call_service, config.current_peer_id, config.logging_mask, @@ -60,6 +65,7 @@ impl AquamarineVM { Ok(Self { faas, particle_data_store, + wasm_filename, }) } @@ -82,131 +88,153 @@ impl AquamarineVM { IValue::String(data.into()), ]; - let result = self.faas.call_with_ivalues( - AQUAMARINE_WASM_FILE_NAME, - "invoke", - &args, - <_>::default(), - )?; + let result = + self.faas + .call_with_ivalues(&self.wasm_filename, "invoke", &args, <_>::default())?; - let raw_outcome = Self::make_raw_outcome(result)?; + let raw_outcome = make_raw_outcome(result)?; std::fs::write(&prev_data_path, &raw_outcome.data) .map_err(|e| PersistDataError(e, prev_data_path))?; raw_outcome.try_into() } +} - fn make_faas_config( - aquamarine_wasm_path: PathBuf, - call_service: HostImportDescriptor, - current_peer_id: String, - logging_mask: i64, - ) -> FaaSConfig { - use maplit::hashmap; +/// Splits given path into its directory and file stem +/// +/// # Example +/// For path `/path/to/aquamarine.wasm` result will be `Ok(PathBuf(/path/to), "aquamarine")` +fn split_dirname(path: PathBuf) -> Result<(PathBuf, String)> { + let metadata = path.metadata().map_err(|err| InvalidAquamarinePath { + invalid_path: path.clone(), + reason: "failed to get file's metadata (doesn't exist or invalid permissions)", + io_error: Some(err), + })?; - let make_faas_module_config = |call_service: HostImportDescriptor| { - use fluence_faas::FaaSModuleConfig; - - let host_imports = hashmap! { - String::from(CALL_SERVICE_NAME) => call_service - }; - - FaaSModuleConfig { - mem_pages_count: None, - logger_enabled: true, - host_imports, - wasi: None, - logging_mask, - } - }; - - let mut aquamarine_module_config = make_faas_module_config(call_service); - - let envs = hashmap! { - CURRENT_PEER_ID_ENV_NAME.as_bytes().to_vec() => current_peer_id.into_bytes(), - }; - aquamarine_module_config.extend_wasi_envs(envs); - - let mut aquamarine_wasm_dir = aquamarine_wasm_path; - // faas config requires a path to the directory with Wasm modules - aquamarine_wasm_dir.pop(); - - FaaSConfig { - modules_dir: Some(aquamarine_wasm_dir), - modules_config: vec![( - String::from(AQUAMARINE_WASM_FILE_NAME), - aquamarine_module_config, - )], - default_modules_config: None, - } + if !metadata.is_file() { + return Err(InvalidAquamarinePath { + invalid_path: path, + reason: "is not a file", + io_error: None, + }); } - fn make_raw_outcome(mut result: Vec) -> Result { - use AquamarineVMError::AquamarineResultError as ResultError; + let file_stem = path + .file_stem() + .expect("checked to be a file, file name must be defined"); + let file_stem = file_stem.to_string_lossy().into_owned(); - match result.remove(0) { - IValue::Record(record_values) => { - let mut record_values = record_values.into_vec(); - if record_values.len() != 3 { - return Err(ResultError(format!( - "expected StepperOutcome struct with 3 fields, got {:?}", - record_values - ))); - } + let mut path = path; + // drop file name from path + path.pop(); - let ret_code = match record_values.remove(0) { - IValue::S32(ret_code) => ret_code, - v => { - return Err(ResultError(format!( - "expected i32 for ret_code, got {:?}", - v - ))) - } - }; + Ok((path, file_stem)) +} - let data = match record_values.remove(0) { - IValue::String(str) => str, - v => { - return Err(ResultError(format!( - "expected string for data, got {:?}", - v - ))) - } - }; +fn make_faas_config( + aquamarine_wasm_dir: PathBuf, + aquamarine_wasm_file: &str, + call_service: HostImportDescriptor, + current_peer_id: String, + logging_mask: i64, +) -> FaaSConfig { + use maplit::hashmap; - let next_peer_pks = match record_values.remove(0) { - IValue::Array(ar_values) => { - let array = ar_values - .into_iter() - .map(|v| match v { - IValue::String(str) => Ok(str), - v => Err(ResultError(format!( - "expected string for next_peer_pks, got {:?}", - v - ))), - }) - .collect::>>()?; + let make_faas_module_config = |call_service: HostImportDescriptor| { + use fluence_faas::FaaSModuleConfig; - Ok(array) - } - v => Err(ResultError(format!( - "expected array for next_peer_pks, got {:?}", - v - ))), - }?; + let host_imports = hashmap! { + String::from(CALL_SERVICE_NAME) => call_service + }; - Ok(RawStepperOutcome { - ret_code, - data, - next_peer_pks, - }) - } - v => { + FaaSModuleConfig { + mem_pages_count: None, + logger_enabled: true, + host_imports, + wasi: None, + logging_mask, + } + }; + + let mut aquamarine_module_config = make_faas_module_config(call_service); + + let envs = hashmap! { + CURRENT_PEER_ID_ENV_NAME.as_bytes().to_vec() => current_peer_id.into_bytes(), + }; + aquamarine_module_config.extend_wasi_envs(envs); + + FaaSConfig { + modules_dir: Some(aquamarine_wasm_dir), + modules_config: vec![(String::from(aquamarine_wasm_file), aquamarine_module_config)], + default_modules_config: None, + } +} + +fn make_raw_outcome(mut result: Vec) -> Result { + use AquamarineVMError::AquamarineResultError as ResultError; + + match result.remove(0) { + IValue::Record(record_values) => { + let mut record_values = record_values.into_vec(); + if record_values.len() != 3 { return Err(ResultError(format!( - "expected record for StepperOutcome, got {:?}", - v - ))) + "expected StepperOutcome struct with 3 fields, got {:?}", + record_values + ))); } + + let ret_code = match record_values.remove(0) { + IValue::S32(ret_code) => ret_code, + v => { + return Err(ResultError(format!( + "expected i32 for ret_code, got {:?}", + v + ))) + } + }; + + let data = match record_values.remove(0) { + IValue::String(str) => str, + v => { + return Err(ResultError(format!( + "expected string for data, got {:?}", + v + ))) + } + }; + + let next_peer_pks = match record_values.remove(0) { + IValue::Array(ar_values) => { + let array = ar_values + .into_iter() + .map(|v| match v { + IValue::String(str) => Ok(str), + v => Err(ResultError(format!( + "expected string for next_peer_pks, got {:?}", + v + ))), + }) + .collect::>>()?; + + Ok(array) + } + v => Err(ResultError(format!( + "expected array for next_peer_pks, got {:?}", + v + ))), + }?; + + Ok(RawStepperOutcome { + ret_code, + data, + next_peer_pks, + }) + } + v => { + return Err(ResultError(format!( + "expected record for StepperOutcome, got {:?}", + v + ))) } } } @@ -228,14 +256,11 @@ impl AquamarineVM { IValue::String(data.into()), ]; - let result = self.faas.call_with_ivalues( - AQUAMARINE_WASM_FILE_NAME, - "invoke", - &args, - <_>::default(), - )?; + let result = + self.faas + .call_with_ivalues(&self.wasm_filename, "invoke", &args, <_>::default())?; - let raw_outcome = Self::make_raw_outcome(result)?; + let raw_outcome = make_raw_outcome(result)?; raw_outcome.try_into() } } diff --git a/aquamarine-vm/src/errors.rs b/aquamarine-vm/src/errors.rs index 4f451c07..ce904997 100644 --- a/aquamarine-vm/src/errors.rs +++ b/aquamarine-vm/src/errors.rs @@ -37,6 +37,13 @@ pub enum AquamarineVMError { /// Errors related to particle_data_store path from supplied config. InvalidDataStorePath(IOError, PathBuf), + + /// Specified path to AIR interpreter .wasm file was invalid + InvalidAquamarinePath { + invalid_path: PathBuf, + io_error: Option, + reason: &'static str, + }, } impl Error for AquamarineVMError {} @@ -57,6 +64,18 @@ impl std::fmt::Display for AquamarineVMError { "an error occurred while creating data storage {:?} by {:?} path", err, path ), + + AquamarineVMError::InvalidAquamarinePath { + invalid_path, + io_error, + reason, + } => { + write!( + f, + "path to AIR interpreter .wasm ({:?}) is invalid: {}; IO Error: {:?}", + invalid_path, reason, io_error + ) + } } } }