From 5e6863d4d59684d4f2b509ece6e597831e648f05 Mon Sep 17 00:00:00 2001 From: Mike Voronov Date: Tue, 21 Feb 2023 20:28:56 +0300 Subject: [PATCH] feat(avm)!: improve anomaly detection (#479) --- Cargo.lock | 84 +++++++++++----------------- avm/interface/Cargo.toml | 1 - avm/server/src/avm.rs | 13 ++++- crates/air-lib/air-parser/Cargo.toml | 2 +- crates/data-store/Cargo.toml | 1 + crates/data-store/src/lib.rs | 29 ++++++++-- 6 files changed, 70 insertions(+), 60 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6a7f52c7..6f9fe528 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -65,7 +65,7 @@ version = "0.1.1" dependencies = [ "air-beautifier", "anyhow", - "clap 4.1.4", + "clap 4.1.6", ] [[package]] @@ -233,7 +233,7 @@ dependencies = [ "anyhow", "avm-data-store", "avm-interface", - "clap 4.1.4", + "clap 4.1.6", "itertools", "serde", "serde_json", @@ -333,6 +333,7 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" name = "avm-data-store" version = "0.4.1" dependencies = [ + "avm-interface", "serde", "serde_bytes", "serde_json", @@ -344,7 +345,6 @@ version = "0.28.1" dependencies = [ "air-interpreter-interface", "air-utils", - "avm-data-store", "log", "maplit", "polyplets", @@ -452,18 +452,6 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfa8873f51c92e232f9bac4065cddef41b714152812bfc5f7672ba16d6ef8cd9" -[[package]] -name = "bstr" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" -dependencies = [ - "lazy_static", - "memchr", - "regex-automata", - "serde", -] - [[package]] name = "bumpalo" version = "3.12.0" @@ -596,9 +584,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.1.4" +version = "4.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76" +checksum = "ec0b0588d44d4d63a87dbd75c136c166bbfd9a86a31cb89e09906521c7d3f5e3" dependencies = [ "bitflags", "clap_derive", @@ -911,13 +899,12 @@ dependencies = [ [[package]] name = "csv" -version = "1.1.6" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" +checksum = "af91f40b7355f82b0a891f50e70399475945bb0b0da4f1700ce60761c9d3e359" dependencies = [ - "bstr", "csv-core", - "itoa 0.4.8", + "itoa", "ryu", "serde", ] @@ -943,9 +930,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.89" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc831ee6a32dd495436e317595e639a587aa9907bef96fe6e6abc290ab6204e9" +checksum = "86d3488e7665a7a483b57e25bdd90d0aeb2bc7608c8d0346acf2ad3f1caf1d62" dependencies = [ "cc", "cxxbridge-flags", @@ -955,9 +942,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.89" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94331d54f1b1a8895cd81049f7eaaaef9d05a7dcb4d1fd08bf3ff0806246789d" +checksum = "48fcaf066a053a41a81dfb14d57d99738b767febb8b735c3016e469fac5da690" dependencies = [ "cc", "codespan-reporting", @@ -970,15 +957,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.89" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48dcd35ba14ca9b40d6e4b4b39961f23d835dbb8eed74565ded361d93e1feb8a" +checksum = "a2ef98b8b717a829ca5603af80e1f9e2e48013ab227b68ef37872ef84ee479bf" [[package]] name = "cxxbridge-macro" -version = "1.0.89" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bbeb29798b407ccd82a3324ade1a7286e0d29851475990b612670f6f5124d2" +checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892" dependencies = [ "proc-macro2", "quote", @@ -1183,9 +1170,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "fluence-app-service" -version = "0.23.0" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3264bb11b79b9e3e4e5b722d986efd461e4606477ba2e454e61ddf79bc1d3bf1" +checksum = "942caba094d1c219ad3656d51c827470d12dac834b4325843e9f61b12bf0d0dd" dependencies = [ "log", "maplit", @@ -1521,12 +1508,6 @@ dependencies = [ "either", ] -[[package]] -name = "itoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" - [[package]] name = "itoa" version = "1.0.5" @@ -1824,9 +1805,9 @@ dependencies = [ [[package]] name = "marine-runtime" -version = "0.24.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3726e9dccce778d0b848ebc9adb312f33f338128bb182aa5ed8429f3f7aac1" +checksum = "545262bf5b68d2b4f4b9eed2557f09421cc4bb62a10e71c307140f27b6411b9c" dependencies = [ "bytesize", "it-json-serde", @@ -2053,9 +2034,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.17.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "oorandom" @@ -2577,7 +2558,7 @@ version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" dependencies = [ - "itoa 1.0.5", + "itoa", "ryu", "serde", ] @@ -2595,7 +2576,7 @@ dependencies = [ "serde", "serde_json", "serde_with_macros", - "time 0.3.17", + "time 0.3.19", ] [[package]] @@ -2781,10 +2762,11 @@ dependencies = [ [[package]] name = "thread_local" -version = "1.1.4" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ + "cfg-if 1.0.0", "once_cell", ] @@ -2801,11 +2783,11 @@ dependencies = [ [[package]] name = "time" -version = "0.3.17" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" +checksum = "53250a3b3fed8ff8fd988587d8925d26a83ac3845d9e03b220b37f34c2b8d6c2" dependencies = [ - "itoa 1.0.5", + "itoa", "serde", "time-core", "time-macros", @@ -2819,9 +2801,9 @@ checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" [[package]] name = "time-macros" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" +checksum = "a460aeb8de6dcb0f381e1ee05f1cd56fcf5a5f6eb8187ff3d8f0b11078d38b7c" dependencies = [ "time-core", ] @@ -2911,7 +2893,7 @@ dependencies = [ "sharded-slab", "smallvec", "thread_local", - "time 0.3.17", + "time 0.3.19", "tracing", "tracing-core", "tracing-serde", diff --git a/avm/interface/Cargo.toml b/avm/interface/Cargo.toml index 9ae46431..d1d82a0b 100644 --- a/avm/interface/Cargo.toml +++ b/avm/interface/Cargo.toml @@ -17,7 +17,6 @@ path = "src/lib.rs" [dependencies] air-interpreter-interface = { version = "0.12.1", path = "../../crates/air-lib/interpreter-interface", default-features = false } air-utils = { version = "0.1.0", path = "../../crates/air-lib/utils" } -avm-data-store = { version = "0.4.1", path = "../../crates/data-store" } polyplets = { version = "0.3.2", path = "../../crates/air-lib/polyplets" } thiserror = "1.0.38" diff --git a/avm/server/src/avm.rs b/avm/server/src/avm.rs index 28520c71..ec05ed44 100644 --- a/avm/server/src/avm.rs +++ b/avm/server/src/avm.rs @@ -102,16 +102,20 @@ impl AVM { particle_parameters.timestamp, particle_parameters.ttl, particle_parameters.current_peer_id.clone(), - call_results, + call_results.clone(), ) .map_err(AVMError::RunnerError)?; let execution_time = execution_start_time.elapsed(); let memory_delta = self.memory_stats().memory_size - memory_size_before; - if self.data_store.detect_anomaly(execution_time, memory_delta) { + if self + .data_store + .detect_anomaly(execution_time, memory_delta, &outcome) + { self.save_anomaly_data( &air, ¤t_data, + &call_results, &particle_parameters, &outcome, execution_time, @@ -139,11 +143,12 @@ impl AVM { self.runner.memory_stats() } - #[allow(clippy::result_large_err)] + #[allow(clippy::result_large_err, clippy::too_many_arguments)] fn save_anomaly_data( &mut self, air_script: &str, current_data: &[u8], + call_result: &CallResults, particle_parameters: &ParticleParameters<'_>, avm_outcome: &RawAVMOutcome, execution_time: Duration, @@ -152,6 +157,7 @@ impl AVM { let prev_data = self .data_store .read_data(&particle_parameters.particle_id)?; + let call_results = serde_json::to_vec(call_result).map_err(AVMError::AnomalyDataSeError)?; let ser_particle = serde_json::to_vec(particle_parameters).map_err(AVMError::AnomalyDataSeError)?; let ser_avm_outcome = @@ -162,6 +168,7 @@ impl AVM { &ser_particle, &prev_data, current_data, + &call_results, &ser_avm_outcome, execution_time, memory_delta, diff --git a/crates/air-lib/air-parser/Cargo.toml b/crates/air-lib/air-parser/Cargo.toml index 00ab4ee3..7241bebf 100644 --- a/crates/air-lib/air-parser/Cargo.toml +++ b/crates/air-lib/air-parser/Cargo.toml @@ -14,7 +14,7 @@ lalrpop = "0.19.8" [dependencies] air-lambda-ast = { version = "0.1.0", path = "../lambda/ast" } -air-lambda-parser = { vesrion = "0.1.0", path = "../lambda/parser" } +air-lambda-parser = { version = "0.1.0", path = "../lambda/parser" } lalrpop-util = "0.19.8" regex = "1.7.1" diff --git a/crates/data-store/Cargo.toml b/crates/data-store/Cargo.toml index c739d365..98d59d34 100644 --- a/crates/data-store/Cargo.toml +++ b/crates/data-store/Cargo.toml @@ -15,6 +15,7 @@ name = "avm_data_store" path = "src/lib.rs" [dependencies] +avm-interface = { version = "0.28.1", path = "../../avm/interface"} serde = { version = "1.0.152", features = ["derive"] } serde_bytes = "0.11.9" diff --git a/crates/data-store/src/lib.rs b/crates/data-store/src/lib.rs index 97a3d054..0487ec12 100644 --- a/crates/data-store/src/lib.rs +++ b/crates/data-store/src/lib.rs @@ -26,6 +26,8 @@ unreachable_patterns )] +use avm_interface::raw_outcome::RawAVMOutcome; + use serde::Deserialize; use serde::Serialize; use std::borrow::Cow; @@ -48,10 +50,16 @@ pub trait DataStore { /// Returns true if an anomaly happened and it's necessary to save execution data /// for debugging purposes. - /// execution_time - is time taken by the interpreter to execute provided script - /// memory_delta - is a count of bytes on which an interpreter heap has been extended + /// execution_time - time taken by the interpreter to execute provided script + /// memory_delta - count of bytes on which an interpreter heap has been extended /// during execution of a particle - fn detect_anomaly(&self, execution_time: Duration, memory_delta: usize) -> bool; + /// outcome - a result of AquaVM invocation + fn detect_anomaly( + &self, + execution_time: Duration, + memory_delta: usize, + outcome: &RawAVMOutcome, + ) -> bool; fn collect_anomaly_data( &mut self, @@ -71,17 +79,21 @@ pub struct AnomalyData<'data> { #[serde(borrow, with = "serde_bytes")] pub current_data: Cow<'data, [u8]>, #[serde(borrow, with = "serde_bytes")] - pub avm_outcome: Cow<'data, [u8]>, // it's byte because of the restriction on trait objects methods + pub call_results: Cow<'data, [u8]>, + #[serde(borrow, with = "serde_bytes")] + pub avm_outcome: Cow<'data, [u8]>, pub execution_time: Duration, pub memory_delta: usize, } impl<'data> AnomalyData<'data> { + #[allow(clippy::too_many_arguments)] pub fn new( air_script: &'data str, particle: &'data [u8], prev_data: &'data [u8], current_data: &'data [u8], + call_results: &'data [u8], avm_outcome: &'data [u8], execution_time: Duration, memory_delta: usize, @@ -91,6 +103,7 @@ impl<'data> AnomalyData<'data> { particle: particle.into(), prev_data: prev_data.into(), current_data: current_data.into(), + call_results: call_results.into(), avm_outcome: avm_outcome.into(), execution_time, memory_delta, @@ -107,6 +120,7 @@ mod tests { particle: &[u8], prev_data: &[u8], current_data: &[u8], + call_results: &[u8], avm_outcome: &[u8], ) -> String { format!( @@ -115,6 +129,7 @@ mod tests { r#""particle":{particle},"#, r#""prev_data":{prev_data},"#, r#""current_data":{current_data},"#, + r#""call_results":{call_results},"#, r#""avm_outcome":{avm_outcome},"#, r#""execution_time":{{"secs":42,"nanos":0}},"#, r#""memory_delta":123"#, @@ -124,6 +139,7 @@ mod tests { particle = serde_json::to_string(particle).unwrap(), prev_data = serde_json::to_string(prev_data).unwrap(), current_data = serde_json::to_string(current_data).unwrap(), + call_results = serde_json::to_string(call_results).unwrap(), avm_outcome = serde_json::to_string(avm_outcome).unwrap(), ) } @@ -135,6 +151,7 @@ mod tests { br#"{"trace":[]}"#, // not real data br#"{"trace":[1,2,3]}"#, // not real data b"{}", // not real data + b"{}", Duration::from_secs(42), 123, ); @@ -145,6 +162,7 @@ mod tests { &anomaly.particle, &anomaly.prev_data, &anomaly.current_data, + &anomaly.call_results, &anomaly.avm_outcome, ); assert_eq!(json_data, expected); @@ -156,11 +174,13 @@ mod tests { let current_data = br#"{"data":"current"}"#; let prev_data = br#"{"data":"prev"}"#; let avm_outcome = br#"{"avm":[1,2,3]}"#; + let call_results = br#"{"call_results": "excellent result"}"#; let json_data = anomaly_json( "(null)", &particle[..], &prev_data[..], ¤t_data[..], + &call_results[..], &avm_outcome[..], ); @@ -174,6 +194,7 @@ mod tests { &particle[..], &prev_data[..], ¤t_data[..], + &call_results[..], &avm_outcome[..], Duration::from_secs(42), 123,