/* * AquaVM Workflow Engine * * Copyright (C) 2024 Fluence DAO * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation version 3 of the * License. * * This program 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ use air::PreparationError; use air_interpreter_interface::CallResultsFormat; use air_interpreter_interface::CallResultsRepr; use air_interpreter_interface::RunParameters; use air_interpreter_interface::MAX_AIR_SIZE; use air_interpreter_interface::MAX_CALL_RESULT_SIZE; use air_interpreter_interface::MAX_PARTICLE_SIZE; use air_interpreter_sede::FromSerialized; use air_test_utils::prelude::*; use serde::Deserialize; use serde::Serialize; #[tokio::test] async fn invalid_data_without_versions() { use air_interpreter_sede::Format; use air_interpreter_sede::Representation; #[derive(Serialize, Deserialize)] struct InvalidDataStruct { pub trace: Vec, } let vm_peer_id = "some_peer_id"; let mut vm = create_avm(unit_call_service(), vm_peer_id).await; let script = r#"(null)"#; let invalid_data = InvalidDataStruct { trace: vec![1, 2, 3] }; let invalid_data = InterpreterDataEnvelopeRepr.get_format().to_vec(&invalid_data).unwrap(); let result = call_vm!(vm, <_>::default(), script, "", invalid_data.clone()); let expected_serde_error = InterpreterDataEnvelope::try_from_slice(&invalid_data).unwrap_err(); let expected_error = PreparationError::EnvelopeDeFailed { error: expected_serde_error, }; assert!(check_error(&result, expected_error)); } #[tokio::test] async fn invalid_data_with_versions() { use air_interpreter_sede::Format; use air_interpreter_sede::Representation; #[derive(Serialize, Deserialize)] struct InvalidDataStruct { pub trace: Vec, #[serde(flatten)] pub versions: Versions, } let vm_peer_id = "some_peer_id"; let mut vm = create_avm(unit_call_service(), vm_peer_id).await; let script = r#"(null)"#; let versions = Versions::new(semver::Version::new(1, 1, 1)); let invalid_data = InvalidDataStruct { trace: vec![1, 2, 3], versions: versions.clone(), }; let invalid_data = InterpreterDataEnvelopeRepr.get_format().to_vec(&invalid_data).unwrap(); let result = call_vm!(vm, <_>::default(), script, "", invalid_data.clone()); let expected_serde_error = InterpreterDataEnvelope::try_from_slice(&invalid_data).unwrap_err(); let expected_error = PreparationError::EnvelopeDeFailedWithVersions { error: expected_serde_error, versions, }; assert!(check_error(&result, expected_error)); } #[tokio::test] async fn invalid_callresults() { use air_interpreter_sede::Format; let air = r#"(null)"#.to_string(); let client_peer_id = "some_peer_id".to_string(); let prev_data = InterpreterDataEnvelope::new(semver::Version::new(1, 1, 1)); let prev_data: Vec = prev_data.serialize().unwrap(); let data = Vec::::new(); let vec = Vec::::new(); let wrong_call_results = CallResultsFormat::default().to_vec(&vec).unwrap(); let keypair = fluence_keypair::KeyPair::generate_ed25519(); let air_size_limit = MAX_AIR_SIZE; let particle_size_limit = MAX_PARTICLE_SIZE; let call_result_size_limit = MAX_CALL_RESULT_SIZE; let hard_limit_enable = false; let run_parameters = RunParameters::new( client_peer_id.clone(), client_peer_id.clone(), 0, 0, keypair.key_format().into(), keypair.secret().unwrap(), "".to_owned(), air_size_limit, particle_size_limit, call_result_size_limit, hard_limit_enable, ); let result = air::execute_air(air, prev_data, data, run_parameters, wrong_call_results.clone().into()); let result = RawAVMOutcome::from_interpreter_outcome(result).unwrap(); let expected_serde_error = CallResultsRepr.deserialize(&wrong_call_results).unwrap_err(); let expected_error = PreparationError::CallResultsDeFailed { error: expected_serde_error, }; assert!(check_error(&result, expected_error)); } #[test] fn air_size_hard_limit() { let script = "a".repeat((MAX_AIR_SIZE + 1) as usize); let peer_id = "some_peer_id".to_owned(); let air_size_limit = MAX_AIR_SIZE; let particle_size_limit = MAX_PARTICLE_SIZE; let call_result_size_limit = MAX_CALL_RESULT_SIZE; let hard_limit_enable = true; let run_parameters = RunParameters::new( peer_id.clone(), peer_id, 0, 0, <_>::default(), <_>::default(), "".to_owned(), air_size_limit, particle_size_limit, call_result_size_limit, hard_limit_enable, ); let result = air::execute_air(script, vec![], vec![], run_parameters, <_>::default()); let result = RawAVMOutcome::from_interpreter_outcome(result).unwrap(); let expected_error = PreparationError::air_size_limit((MAX_AIR_SIZE + 1) as usize, MAX_AIR_SIZE); assert!(check_error(&result, expected_error)); } #[test] fn particle_size_hard_limit() { let script = "(null)".to_owned(); let cur_data = vec![0; (MAX_PARTICLE_SIZE + 1) as usize]; let peer_id = "some_peer_id".to_owned(); let air_size_limit = MAX_AIR_SIZE; let particle_size_limit = MAX_PARTICLE_SIZE; let call_result_size_limit = MAX_CALL_RESULT_SIZE; let hard_limit_enable = true; let run_parameters = RunParameters::new( peer_id.clone(), peer_id, 0, 0, <_>::default(), <_>::default(), "".to_owned(), air_size_limit, particle_size_limit, call_result_size_limit, hard_limit_enable, ); let result = air::execute_air(script, vec![], cur_data, run_parameters, <_>::default()); let result = RawAVMOutcome::from_interpreter_outcome(result).unwrap(); let expected_error = PreparationError::particle_size_limit((MAX_PARTICLE_SIZE + 1) as usize, MAX_PARTICLE_SIZE); assert!(check_error(&result, expected_error)); } #[test] fn call_result_size_hard_limit() { use maplit::hashmap; use air::ToErrorCode; use air_interpreter_interface::MAX_CALL_RESULT_SIZE; use air_interpreter_sede::ToSerialized; let script = "(null)".to_owned(); let result_1 = "a".repeat((MAX_CALL_RESULT_SIZE / 2 + 1) as usize); let result_2 = "b".repeat((MAX_CALL_RESULT_SIZE + 1) as usize); let call_results: CallResults = hashmap! {0 => CallServiceResult::ok(result_1.into()), 1 => CallServiceResult::ok(result_2.into())}; let raw_call_results = into_raw_result(call_results); let raw_call_results = CallResultsRepr.serialize(&raw_call_results).unwrap(); let peer_id = "some_peer_id".to_owned(); let air_size_limit = MAX_AIR_SIZE; let particle_size_limit = MAX_PARTICLE_SIZE; let call_result_size_limit = MAX_CALL_RESULT_SIZE; let hard_limit_enable = true; let run_parameters = RunParameters::new( peer_id.clone(), peer_id, 0, 0, <_>::default(), <_>::default(), "".to_owned(), air_size_limit, particle_size_limit, call_result_size_limit, hard_limit_enable, ); let result = air::execute_air(script, vec![], vec![], run_parameters, raw_call_results); let result = RawAVMOutcome::from_interpreter_outcome(result).unwrap(); let expected_error = PreparationError::call_result_size_limit(MAX_CALL_RESULT_SIZE); assert_eq!(result.ret_code, expected_error.to_error_code()); }