newton_cli/commands/
utils.rs1use eyre::Context;
2use newton_data_provider::{DataProviderConfig, WasmExecutor, WasmExecutorConfig};
3use newton_prover_core::config::{NewtonAvsConfig, NewtonAvsConfigBuilder};
4use std::path::PathBuf;
5
6use crate::config::NewtonCliConfig;
7
8pub async fn execute_wasm(
10 wasm_file: PathBuf,
11 input_json: String,
12 config: NewtonAvsConfig<NewtonCliConfig>,
13) -> eyre::Result<String> {
14 let wasm_bytes = std::fs::read(&wasm_file).with_context(|| format!("Failed to read WASM file: {:?}", wasm_file))?;
16
17 let data_provider_config = NewtonAvsConfigBuilder::new(config.chain_id)
19 .build::<DataProviderConfig>()
20 .map_err(|e| eyre::eyre!("Failed to load data-provider config: {e}"))?;
21
22 let executor_config = WasmExecutorConfig::from_data_provider_config(&data_provider_config);
24 let executor =
25 WasmExecutor::new(executor_config).map_err(|e| eyre::eyre!("Failed to create WASM executor: {e}"))?;
26
27 let result = executor
29 .execute_wasm_bytes(&wasm_bytes, &input_json)
30 .await
31 .map_err(|e| eyre::eyre!("WASM execution failed: {e}"))?;
32
33 match result {
34 Ok(output) => Ok(output),
35 Err(error) => {
36 eyre::bail!("WASM execution error: {}", error);
37 }
38 }
39}
40
41#[cfg(test)]
42mod tests {
43 use super::*;
44 use std::{fs, io::Write};
45 use tempfile::TempDir;
46
47 fn create_test_config(chain_id: u64) -> eyre::Result<NewtonAvsConfig<NewtonCliConfig>> {
50 NewtonAvsConfigBuilder::new(chain_id)
51 .build::<NewtonCliConfig>()
52 .map_err(|e| eyre::eyre!("Failed to build test config: {}", e))
53 }
54
55 fn create_invalid_wasm_file(dir: &TempDir) -> PathBuf {
57 let wasm_path = dir.path().join("test.wasm");
58 let mut file = fs::File::create(&wasm_path).unwrap();
59 file.write_all(b"invalid wasm content").unwrap();
61 wasm_path
62 }
63
64 fn create_nonexistent_path() -> PathBuf {
66 PathBuf::from("/nonexistent/path/to/file.wasm")
67 }
68
69 #[tokio::test]
70 async fn test_execute_wasm_file_not_found() {
71 let config = match create_test_config(11155111) {
72 Ok(c) => c,
73 Err(_) => {
74 eprintln!("Skipping test: config loading failed (this is expected in some test environments)");
75 return;
76 }
77 };
78 let nonexistent_file = create_nonexistent_path();
79 let input_json = r#"{}"#.to_string();
80
81 let result = execute_wasm(nonexistent_file, input_json, config).await;
82
83 assert!(result.is_err(), "Should fail when WASM file doesn't exist");
84 let error_msg = result.unwrap_err().to_string();
85 assert!(
86 error_msg.contains("Failed to read WASM file") || error_msg.contains("No such file"),
87 "Error message should mention file reading failure. Got: {}",
88 error_msg
89 );
90 }
91
92 #[tokio::test]
93 async fn test_execute_wasm_invalid_wasm_content() {
94 let config = match create_test_config(11155111) {
95 Ok(c) => c,
96 Err(_) => {
97 eprintln!("Skipping test: config loading failed (this is expected in some test environments)");
98 return;
99 }
100 };
101 let temp_dir = TempDir::new().unwrap();
102 let wasm_file = create_invalid_wasm_file(&temp_dir);
103 let input_json = r#"{}"#.to_string();
104
105 let result = execute_wasm(wasm_file, input_json, config).await;
106
107 assert!(result.is_err(), "Should fail with invalid WASM content");
109 let error_msg = result.unwrap_err().to_string();
110 assert!(
112 error_msg.contains("WASM")
113 || error_msg.contains("wasm")
114 || error_msg.contains("executor")
115 || error_msg.contains("compile"),
116 "Error message should mention WASM-related failure. Got: {}",
117 error_msg
118 );
119 }
120
121 #[tokio::test]
122 async fn test_execute_wasm_empty_input_json() {
123 let config = match create_test_config(11155111) {
124 Ok(c) => c,
125 Err(_) => {
126 eprintln!("Skipping test: config loading failed (this is expected in some test environments)");
127 return;
128 }
129 };
130 let temp_dir = TempDir::new().unwrap();
131 let wasm_file = create_invalid_wasm_file(&temp_dir);
132 let input_json = String::new();
133
134 let result = execute_wasm(wasm_file, input_json, config).await;
135
136 assert!(result.is_err(), "Should fail with invalid WASM even with empty input");
138 }
139
140 #[tokio::test]
141 async fn test_execute_wasm_valid_json_input() {
142 let config = match create_test_config(11155111) {
143 Ok(c) => c,
144 Err(_) => {
145 eprintln!("Skipping test: config loading failed (this is expected in some test environments)");
146 return;
147 }
148 };
149 let temp_dir = TempDir::new().unwrap();
150 let wasm_file = create_invalid_wasm_file(&temp_dir);
151 let input_json = r#"{"key": "value", "number": 42}"#.to_string();
152
153 let result = execute_wasm(wasm_file, input_json, config).await;
154
155 assert!(
157 result.is_err(),
158 "Should fail with invalid WASM even with valid JSON input"
159 );
160 }
161
162 #[tokio::test]
163 async fn test_execute_wasm_different_chain_ids() {
164 let temp_dir = TempDir::new().unwrap();
165 let wasm_file = create_invalid_wasm_file(&temp_dir);
166 let input_json = r#"{}"#.to_string();
167
168 for chain_id in [1, 11155111, 17000] {
171 let config_result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| create_test_config(chain_id)));
173
174 let config = match config_result {
175 Ok(Ok(c)) => c,
176 Ok(Err(e)) => {
177 eprintln!("Skipping chain_id {}: config loading failed: {}", chain_id, e);
178 continue;
179 }
180 Err(_) => {
181 eprintln!(
182 "Skipping chain_id {}: config loading panicked (missing deployment files)",
183 chain_id
184 );
185 continue;
186 }
187 };
188
189 let result = execute_wasm(wasm_file.clone(), input_json.clone(), config).await;
190
191 assert!(
193 result.is_err(),
194 "Should fail with invalid WASM for chain_id {}",
195 chain_id
196 );
197 }
198 }
199
200 #[tokio::test]
201 async fn test_execute_wasm_error_context_preserved() {
202 let config = match create_test_config(11155111) {
203 Ok(c) => c,
204 Err(_) => {
205 eprintln!("Skipping test: config loading failed (this is expected in some test environments)");
206 return;
207 }
208 };
209 let nonexistent_file = create_nonexistent_path();
210 let input_json = r#"{}"#.to_string();
211
212 let result = execute_wasm(nonexistent_file.clone(), input_json, config).await;
213
214 assert!(result.is_err(), "Should fail when file doesn't exist");
215 let error_msg = result.unwrap_err().to_string();
216 let file_str = nonexistent_file.to_string_lossy();
218 assert!(
219 error_msg.contains("Failed to read WASM file") || error_msg.contains(&*file_str),
220 "Error should include context about the file. Got: {}",
221 error_msg
222 );
223 }
224
225 }