1use std::path::Path;
2
3use acir::{AcirField, FieldElement, native_types::WitnessStack};
4use acvm::BlackBoxFunctionSolver;
5use nargo::{NargoError, foreign_calls::ForeignCallExecutor};
6use noirc_abi::{AbiType, Sign, input_parser::InputValue};
7use noirc_artifacts::debug::DebugArtifact;
8use noirc_driver::CompiledProgram;
9use noirc_printable_type::format_field_string;
10
11use crate::{
12 errors::CliError,
13 fs::{inputs::read_inputs_from_file, witness::save_witness_to_dir},
14};
15
16#[derive(Clone, Debug)]
18pub struct ExecutionResults {
19 pub witness_stack: WitnessStack<FieldElement>,
20 pub return_values: ReturnValues,
21}
22
23#[derive(Clone, Debug)]
25pub struct ReturnValues {
26 pub expected_return: Option<InputValue>,
28 pub actual_return: Option<InputValue>,
30}
31
32pub fn execute<B, E>(
34 circuit: &CompiledProgram,
35 blackbox_solver: &B,
36 foreign_call_executor: &mut E,
37 prover_file: &Path,
38) -> Result<ExecutionResults, CliError>
39where
40 B: BlackBoxFunctionSolver<FieldElement>,
41 E: ForeignCallExecutor<FieldElement>,
42{
43 let (input_map, expected_return) = read_inputs_from_file(prover_file, &circuit.abi)?;
44
45 let initial_witness = circuit.abi.encode(&input_map, None)?;
46
47 let witness_stack = nargo::ops::execute_program(
48 &circuit.program,
49 initial_witness,
50 blackbox_solver,
51 foreign_call_executor,
52 )?;
53
54 let main_witness =
55 &witness_stack.peek().expect("Should have at least one witness on the stack").witness;
56
57 let (_, actual_return) = circuit.abi.decode(main_witness)?;
58
59 Ok(ExecutionResults {
60 witness_stack,
61 return_values: ReturnValues { actual_return, expected_return },
62 })
63}
64
65pub fn show_diagnostic(circuit: &CompiledProgram, err: &NargoError<FieldElement>) {
67 if let Some(diagnostic) =
68 nargo::errors::try_to_diagnose_runtime_error(err, &circuit.abi, &circuit.debug)
69 {
70 let debug_artifact = DebugArtifact {
71 debug_symbols: circuit.debug.clone(),
72 file_map: circuit.file_map.clone(),
73 };
74
75 diagnostic.report(&debug_artifact, false);
76 }
77}
78
79pub fn save_and_check_witness(
82 circuit: &CompiledProgram,
83 results: ExecutionResults,
84 circuit_name: &str,
85 witness_dir: Option<&Path>,
86 witness_name: Option<&str>,
87) -> Result<(), CliError> {
88 noirc_errors::println_to_stdout!("[{circuit_name}] Circuit witness successfully solved");
89 if let Some(witness_dir) = witness_dir {
91 save_witness(&results.witness_stack, circuit_name, witness_dir, witness_name)?;
92 }
93 if let Some(ref return_value) = results.return_values.actual_return {
94 let abi_type = &circuit.abi.return_type.as_ref().unwrap().abi_type;
95 let output_string = input_value_to_string(return_value, abi_type);
96 noirc_errors::println_to_stdout!("[{circuit_name}] Circuit output: {output_string}");
97 }
98 check_witness(circuit, results.return_values)
99}
100
101pub fn save_witness(
103 witness_stack: &WitnessStack<FieldElement>,
104 circuit_name: &str,
105 witness_dir: &Path,
106 witness_name: Option<&str>,
107) -> Result<(), CliError> {
108 let witness_name = witness_name.unwrap_or(circuit_name);
109 let mut witness_path = save_witness_to_dir(witness_stack, witness_name, witness_dir)?;
110
111 if let Ok(current_dir) = std::env::current_dir() {
113 if let Ok(name_without_prefix) = witness_path.strip_prefix(current_dir) {
114 witness_path = name_without_prefix.to_path_buf();
115 }
116 }
117
118 noirc_errors::println_to_stdout!(
119 "[{}] Witness saved to {}",
120 circuit_name,
121 witness_path.display()
122 );
123 Ok(())
124}
125
126pub fn check_witness(
128 circuit: &CompiledProgram,
129 return_values: ReturnValues,
130) -> Result<(), CliError> {
131 if let Some(ref expected) = circuit.abi.return_type {
133 if return_values.actual_return.is_none() {
134 return Err(CliError::MissingReturn { expected: expected.clone() });
135 }
136 }
137
138 if let Some(expected) = return_values.expected_return {
140 match return_values.actual_return {
141 None => {
142 return Err(CliError::UnexpectedReturn { expected, actual: None });
143 }
144 Some(actual) => {
145 if actual != expected {
146 return Err(CliError::UnexpectedReturn { expected, actual: Some(actual) });
147 }
148 }
149 }
150 }
151
152 Ok(())
153}
154
155pub fn input_value_to_string(input_value: &InputValue, abi_type: &AbiType) -> String {
156 let mut string = String::new();
157 append_input_value_to_string(input_value, abi_type, &mut string);
158 string
159}
160
161fn append_input_value_to_string(input_value: &InputValue, abi_type: &AbiType, string: &mut String) {
162 match (abi_type, input_value) {
163 (AbiType::Field, InputValue::Field(field_element)) => {
164 string.push_str(&format_field_string(*field_element));
165 }
166 (AbiType::Array { length: _, typ }, InputValue::Vec(input_values)) => {
167 string.push('[');
168 for (index, input_value) in input_values.iter().enumerate() {
169 if index != 0 {
170 string.push_str(", ");
171 }
172 append_input_value_to_string(input_value, typ, string);
173 }
174 string.push(']');
175 }
176 (AbiType::Integer { sign, width: bit_size }, InputValue::Field(f)) => match sign {
177 Sign::Unsigned => {
178 string.push_str(&f.to_string());
179 }
180 Sign::Signed => {
181 let bit_size = *bit_size;
182 let max =
183 if bit_size == 128 { i128::MAX as u128 } else { (1 << (bit_size - 1)) - 1 };
184 if f.num_bits() > 128 || f.to_u128() > max {
185 string.push('-');
186 let f = FieldElement::from(2u32).pow(&bit_size.into()) - *f;
187 string.push_str(&f.to_string());
188 } else {
189 string.push_str(&f.to_string());
190 }
191 }
192 },
193 (AbiType::Boolean, InputValue::Field(field_element)) => {
194 if field_element.is_zero() {
195 string.push_str("false");
196 } else {
197 string.push_str("true");
198 }
199 }
200 (AbiType::Struct { path, fields: field_types }, InputValue::Struct(field_values)) => {
201 string.push_str(path);
202 string.push_str(" { ");
203 for (index, (field_name, field_value)) in field_values.iter().enumerate() {
204 if index != 0 {
205 string.push_str(", ");
206 }
207 string.push_str(field_name);
208 string.push_str(": ");
209 let typ = &field_types.iter().find(|(name, _)| name == field_name).unwrap().1;
210 append_input_value_to_string(field_value, typ, string);
211 }
212 string.push_str(" }");
213 }
214 (AbiType::Tuple { fields }, InputValue::Vec(input_values)) => {
215 assert_eq!(fields.len(), input_values.len());
216
217 string.push('(');
218 for (index, (input_value, field_type)) in input_values.iter().zip(fields).enumerate() {
219 if index != 0 {
220 string.push_str(", ");
221 }
222 append_input_value_to_string(input_value, field_type, string);
223 }
224 if input_values.len() == 1 {
225 string.push(',');
226 }
227 string.push(')');
228 }
229 (AbiType::String { .. }, InputValue::String(value)) => {
230 string.push_str(&format!("{value:?}"));
231 }
232 (_, _) => {
233 panic!("Unexpected InputValue-AbiType combination: {input_value:?} - {abi_type:?}");
234 }
235 }
236}