From bfa036caf4d2b0f23473675a948c7c96f59b3832 Mon Sep 17 00:00:00 2001 From: Salvatore Ingala <6681844+bigspider@users.noreply.github.com> Date: Fri, 11 Oct 2024 10:35:01 +0200 Subject: [PATCH] Added 'panic' command to test app; improved error handling --- apps/test/README.md | 1 + apps/test/app/src/commands.rs | 2 + apps/test/app/src/main.rs | 4 ++ apps/test/client/src/client.rs | 90 ++++++++++++++++++++------------ apps/test/client/src/commands.rs | 2 + apps/test/client/src/main.rs | 24 +++++++-- 6 files changed, 88 insertions(+), 35 deletions(-) diff --git a/apps/test/README.md b/apps/test/README.md index 602d7b6..cb11bc1 100644 --- a/apps/test/README.md +++ b/apps/test/README.md @@ -55,4 +55,5 @@ Once the client is running, these are the available commands: - `b58enc ` - Computes the base58 encoding of the given buffer (the output is in hex as well). - `addnumbers ` - Computes the sum of the numbers between `1` and `n`. - `nprimes ` - Counts the number of primes up to `n` using the Sieve of Erathostenes. +- `panic ` - Cause the V-App to panic. Everything written after 'panic' is the panic message. - An empty command will exit the V-App. diff --git a/apps/test/app/src/commands.rs b/apps/test/app/src/commands.rs index 64df67f..1556c62 100644 --- a/apps/test/app/src/commands.rs +++ b/apps/test/app/src/commands.rs @@ -8,6 +8,7 @@ pub enum Command { Base58Encode, Sha256, CountPrimes, + Panic = 0xff, } impl TryFrom for Command { @@ -20,6 +21,7 @@ impl TryFrom for Command { 0x02 => Ok(Command::Base58Encode), 0x03 => Ok(Command::Sha256), 0x04 => Ok(Command::CountPrimes), + 0xff => Ok(Command::Panic), _ => Err(()), } } diff --git a/apps/test/app/src/main.rs b/apps/test/app/src/main.rs index c45c86d..4675c71 100644 --- a/apps/test/app/src/main.rs +++ b/apps/test/app/src/main.rs @@ -93,6 +93,10 @@ pub fn main(_: isize, _: *const *const u8) -> isize { Command::Base58Encode => handle_base58_encode(&msg[1..]), Command::Sha256 => handle_sha256(&msg[1..]), Command::CountPrimes => handle_count_primes(&msg[1..]), + Command::Panic => { + let panic_msg = core::str::from_utf8(&msg[1..]).unwrap(); + panic!("{}", panic_msg); + } }; sdk::xsend(&response); diff --git a/apps/test/client/src/client.rs b/apps/test/client/src/client.rs index b07d679..49e175c 100644 --- a/apps/test/client/src/client.rs +++ b/apps/test/client/src/client.rs @@ -1,6 +1,42 @@ use crate::commands::Command; use sdk::vanadium_client::{VAppClient, VAppExecutionError}; +#[derive(Debug)] +pub enum TestClientError { + VAppExecutionError(VAppExecutionError), + GenericError(&'static str), +} + +impl From for TestClientError { + fn from(e: VAppExecutionError) -> Self { + Self::VAppExecutionError(e) + } +} + +impl From<&'static str> for TestClientError { + fn from(e: &'static str) -> Self { + Self::GenericError(e) + } +} + +impl std::fmt::Display for TestClientError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + TestClientError::VAppExecutionError(e) => write!(f, "VAppExecutionError: {}", e), + TestClientError::GenericError(e) => write!(f, "GenericError: {}", e), + } + } +} + +impl std::error::Error for TestClientError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + TestClientError::VAppExecutionError(e) => Some(e), + TestClientError::GenericError(_) => None, + } + } +} + pub struct TestClient { app_client: Box, } @@ -10,76 +46,66 @@ impl TestClient { Self { app_client } } - pub async fn reverse(&mut self, data: &[u8]) -> Result, &'static str> { + pub async fn reverse(&mut self, data: &[u8]) -> Result, TestClientError> { let mut msg: Vec = Vec::new(); msg.extend_from_slice(&[Command::Reverse as u8]); msg.extend_from_slice(data); - Ok(self - .app_client - .send_message(msg) - .await - .map_err(|_| "Failed")?) + Ok(self.app_client.send_message(msg).await?) } - pub async fn add_numbers(&mut self, n: u32) -> Result { + pub async fn add_numbers(&mut self, n: u32) -> Result { let mut msg: Vec = Vec::new(); msg.extend_from_slice(&[Command::AddNumbers as u8]); msg.extend_from_slice(&n.to_be_bytes()); - let result_raw = self - .app_client - .send_message(msg) - .await - .map_err(|_| "Failed")?; + let result_raw = self.app_client.send_message(msg).await?; if result_raw.len() != 8 { - return Err("Invalid response length"); + return Err("Invalid response length".into()); } Ok(u64::from_be_bytes(result_raw.try_into().unwrap())) } - pub async fn sha256(&mut self, data: &[u8]) -> Result, &'static str> { + pub async fn sha256(&mut self, data: &[u8]) -> Result, TestClientError> { let mut msg: Vec = Vec::new(); msg.extend_from_slice(&[Command::Sha256 as u8]); msg.extend_from_slice(data); - Ok(self - .app_client - .send_message(msg) - .await - .map_err(|_| "Failed")?) + Ok(self.app_client.send_message(msg).await?) } - pub async fn b58enc(&mut self, data: &[u8]) -> Result, &'static str> { + pub async fn b58enc(&mut self, data: &[u8]) -> Result, TestClientError> { let mut msg: Vec = Vec::new(); msg.extend_from_slice(&[Command::Base58Encode as u8]); msg.extend_from_slice(data); - Ok(self - .app_client - .send_message(msg) - .await - .map_err(|_| "Failed")?) + Ok(self.app_client.send_message(msg).await?) } - pub async fn nprimes(&mut self, n: u32) -> Result { + pub async fn nprimes(&mut self, n: u32) -> Result { let mut msg: Vec = Vec::new(); msg.extend_from_slice(&[Command::CountPrimes as u8]); msg.extend_from_slice(&n.to_be_bytes()); - let result_raw = self - .app_client - .send_message(msg) - .await - .map_err(|_| "Failed")?; + let result_raw = self.app_client.send_message(msg).await?; if result_raw.len() != 4 { - return Err("Invalid response length"); + return Err("Invalid response length".into()); } Ok(u32::from_be_bytes(result_raw.try_into().unwrap())) } + pub async fn panic(&mut self, panic_msg: &str) -> Result<(), TestClientError> { + let mut msg: Vec = Vec::new(); + msg.extend_from_slice(&[Command::Panic as u8]); + msg.extend_from_slice(panic_msg.as_bytes()); + + self.app_client.send_message(msg).await?; + + Err("The app should have panicked!".into()) + } + pub async fn exit(&mut self) -> Result { match self.app_client.send_message(Vec::new()).await { Ok(_) => { diff --git a/apps/test/client/src/commands.rs b/apps/test/client/src/commands.rs index 8030d36..991c8cf 100644 --- a/apps/test/client/src/commands.rs +++ b/apps/test/client/src/commands.rs @@ -8,6 +8,7 @@ pub enum Command { Base58Encode, Sha256, CountPrimes, + Panic = 0xff, } impl TryFrom for Command { @@ -20,6 +21,7 @@ impl TryFrom for Command { 0x02 => Ok(Command::Base58Encode), 0x03 => Ok(Command::Sha256), 0x04 => Ok(Command::CountPrimes), + 0xff => Ok(Command::Panic), _ => Err(()), } } diff --git a/apps/test/client/src/main.rs b/apps/test/client/src/main.rs index 080fb1e..912d4ba 100644 --- a/apps/test/client/src/main.rs +++ b/apps/test/client/src/main.rs @@ -34,6 +34,7 @@ enum CliCommand { Sha256(Vec), B58Enc(Vec), NPrimes(u32), + Panic(String), Exit, } @@ -82,6 +83,14 @@ fn parse_command(line: &str) -> Result { _ => unreachable!(), } } + "panic" => { + // find where the word "panic" ends and the message starts + let msg = line + .find("panic") + .map(|i| line[i + 5..].trim()) + .unwrap_or(""); + Ok(CliCommand::Panic(msg.to_string())) + } _ => Err(format!("Unknown command: '{}'", command)), } } else { @@ -90,7 +99,7 @@ fn parse_command(line: &str) -> Result { } #[tokio::main(flavor = "current_thread")] -async fn main() -> Result<(), Box> { +async fn main() -> Result<(), Box> { let args = Args::parse(); let default_app_path = if args.native { @@ -102,7 +111,11 @@ async fn main() -> Result<(), Box> { let app_path_str = args.app.unwrap_or(default_app_path.to_string()); let mut test_client = if args.native { - TestClient::new(Box::new(NativeAppClient::new(&app_path_str).await?)) + TestClient::new(Box::new( + NativeAppClient::new(&app_path_str) + .await + .map_err(|_| "Failed to create client")?, + )) } else { let transport_raw: Arc< dyn Transport> + Send + Sync, @@ -123,7 +136,9 @@ async fn main() -> Result<(), Box> { let transport = TransportWrapper::new(transport_raw.clone()); TestClient::new(Box::new( - VanadiumAppClient::new(&app_path_str, Arc::new(transport)).await?, + VanadiumAppClient::new(&app_path_str, Arc::new(transport)) + .await + .map_err(|_| "Failed to create client")?, )) }; @@ -153,6 +168,9 @@ async fn main() -> Result<(), Box> { CliCommand::NPrimes(n) => { println!("{}", test_client.nprimes(n).await?); } + CliCommand::Panic(msg) => { + test_client.panic(&msg).await?; + } CliCommand::Exit => { let status = test_client.exit().await?; if status != 0 {