1
- use std:: { collections :: BTreeMap , path:: PathBuf } ;
1
+ use std:: path:: PathBuf ;
2
2
3
- use acir:: { FieldElement , circuit:: Program , native_types:: WitnessStack } ;
4
3
use bn254_blackbox_solver:: Bn254BlackBoxSolver ;
5
4
use clap:: Args ;
6
- use color_eyre:: eyre:: { self , bail} ;
7
5
8
6
use crate :: {
9
7
Artifact ,
10
8
errors:: CliError ,
11
- fs :: { inputs :: read_inputs_from_file , witness :: save_witness_to_dir } ,
9
+ execution :: { self , ExecutionResults } ,
12
10
} ;
13
- use nargo:: { NargoError , PrintOutput , foreign_calls:: DefaultForeignCallBuilder } ;
14
- use noirc_abi:: { Abi , input_parser:: InputValue } ;
15
- use noirc_artifacts:: debug:: DebugArtifact ;
11
+ use nargo:: { PrintOutput , foreign_calls:: DefaultForeignCallBuilder } ;
12
+ use noirc_driver:: CompiledProgram ;
16
13
17
14
use super :: parse_and_normalize_path;
18
15
@@ -21,106 +18,84 @@ use super::parse_and_normalize_path;
21
18
pub struct ExecuteCommand {
22
19
/// Path to the JSON build artifact (either a program or a contract).
23
20
#[ clap( long, short, value_parser = parse_and_normalize_path) ]
24
- artifact_path : PathBuf ,
21
+ pub artifact_path : PathBuf ,
25
22
26
23
/// Path to the Prover.toml file which contains the inputs and the
27
24
/// optional return value in ABI format.
28
25
#[ clap( long, short, value_parser = parse_and_normalize_path) ]
29
- prover_file : PathBuf ,
26
+ pub prover_file : PathBuf ,
30
27
31
28
/// Path to the directory where the output witness should be saved.
32
29
/// If empty then the results are discarded.
33
30
#[ clap( long, short, value_parser = parse_and_normalize_path) ]
34
- output_dir : Option < PathBuf > ,
31
+ pub output_dir : Option < PathBuf > ,
35
32
36
33
/// Write the execution witness to named file
37
34
///
38
35
/// Defaults to the name of the circuit being executed.
39
36
#[ clap( long, short) ]
40
- witness_name : Option < String > ,
37
+ pub witness_name : Option < String > ,
41
38
42
39
/// Name of the function to execute, if the artifact is a contract.
43
40
#[ clap( long) ]
44
- contract_fn : Option < String > ,
41
+ pub contract_fn : Option < String > ,
45
42
46
43
/// JSON RPC url to solve oracle calls.
47
44
#[ clap( long) ]
48
- oracle_resolver : Option < String > ,
45
+ pub oracle_resolver : Option < String > ,
49
46
50
47
/// Use pedantic ACVM solving, i.e. double-check some black-box function assumptions when solving.
51
48
#[ clap( long, default_value_t = false ) ]
52
- pedantic_solving : bool ,
49
+ pub pedantic_solving : bool ,
53
50
}
54
51
55
- pub fn run ( args : ExecuteCommand ) -> eyre :: Result < ( ) > {
52
+ pub fn run ( args : ExecuteCommand ) -> Result < ( ) , CliError > {
56
53
let artifact = Artifact :: read_from_file ( & args. artifact_path ) ?;
54
+ let artifact_name = args. artifact_path . file_stem ( ) . and_then ( |s| s. to_str ( ) ) . unwrap_or_default ( ) ;
57
55
58
- let circuit = match artifact {
59
- Artifact :: Program ( program) => Circuit {
60
- name : None ,
61
- abi : program. abi ,
62
- bytecode : program. bytecode ,
63
- debug_symbols : program. debug_symbols ,
64
- file_map : program. file_map ,
65
- } ,
56
+ let ( circuit, circuit_name) : ( CompiledProgram , String ) = match artifact {
57
+ Artifact :: Program ( program) => ( program. into ( ) , artifact_name. to_string ( ) ) ,
66
58
Artifact :: Contract ( contract) => {
67
- let names =
68
- contract. functions . iter ( ) . map ( |f| f. name . clone ( ) ) . collect :: < Vec < _ > > ( ) . join ( "," ) ;
59
+ let names = || contract. functions . iter ( ) . map ( |f| f. name . clone ( ) ) . collect :: < Vec < _ > > ( ) ;
69
60
70
61
let Some ( ref name) = args. contract_fn else {
71
- bail ! ( "--contract-fn missing; options: [{ names}]" ) ;
62
+ return Err ( CliError :: MissingContractFn { names : names ( ) } ) ;
72
63
} ;
73
- let Some ( function ) = contract. functions . into_iter ( ) . find ( |f| f . name == * name) else {
74
- bail ! ( "unknown --contract-fn '{ name}'; options: [{ names}]" ) ;
64
+ let Some ( program ) = contract. function_as_compiled_program ( name) else {
65
+ return Err ( CliError :: UnknownContractFn { name : name . clone ( ) , names : names ( ) } ) ;
75
66
} ;
76
67
77
- Circuit {
78
- name : Some ( name. clone ( ) ) ,
79
- abi : function. abi ,
80
- bytecode : function. bytecode ,
81
- debug_symbols : function. debug_symbols ,
82
- file_map : contract. file_map ,
83
- }
68
+ ( program, format ! ( "{artifact_name}::{name}" ) )
84
69
}
85
70
} ;
86
71
87
72
match execute ( & circuit, & args) {
88
- Ok ( solved) => {
89
- save_witness ( circuit, args, solved) ?;
90
- }
91
- Err ( CliError :: CircuitExecutionError ( err) ) => {
92
- show_diagnostic ( circuit, err) ;
73
+ Ok ( results) => {
74
+ execution:: save_and_check_witness (
75
+ & circuit,
76
+ results,
77
+ & circuit_name,
78
+ args. output_dir . as_deref ( ) ,
79
+ args. witness_name . as_deref ( ) ,
80
+ ) ?;
93
81
}
94
82
Err ( e) => {
95
- bail ! ( "failed to execute the circuit: {e}" ) ;
83
+ if let CliError :: CircuitExecutionError ( ref err) = e {
84
+ execution:: show_diagnostic ( & circuit, err) ;
85
+ }
86
+ // Still returning the error to facilitate command forwarding, to indicate that the command failed.
87
+ return Err ( e) ;
96
88
}
97
89
}
98
90
Ok ( ( ) )
99
91
}
100
92
101
- /// Parameters necessary to execute a circuit, display execution failures, etc.
102
- struct Circuit {
103
- name : Option < String > ,
104
- abi : Abi ,
105
- bytecode : Program < FieldElement > ,
106
- debug_symbols : noirc_errors:: debug_info:: ProgramDebugInfo ,
107
- file_map : BTreeMap < fm:: FileId , noirc_driver:: DebugFile > ,
108
- }
109
-
110
- struct SolvedWitnesses {
111
- expected_return : Option < InputValue > ,
112
- actual_return : Option < InputValue > ,
113
- witness_stack : WitnessStack < FieldElement > ,
114
- }
115
-
116
93
/// Execute a circuit and return the output witnesses.
117
- fn execute ( circuit : & Circuit , args : & ExecuteCommand ) -> Result < SolvedWitnesses , CliError > {
118
- let ( input_map, expected_return) = read_inputs_from_file ( & args. prover_file , & circuit. abi ) ?;
119
-
120
- let initial_witness = circuit. abi . encode ( & input_map, None ) ?;
121
-
94
+ fn execute ( circuit : & CompiledProgram , args : & ExecuteCommand ) -> Result < ExecutionResults , CliError > {
122
95
// TODO: Build a custom foreign call executor that reads from the Oracle transcript,
123
- // and use it as a base for the default executor; see `DefaultForeignCallBuilder::build_with_base`
96
+ // and use it as a base for the default executor. Using it as the innermost rather
97
+ // than top layer so that any extra `print` added for debugging is handled by the
98
+ // default, rather than trying to match it to the transcript.
124
99
let mut foreign_call_executor = DefaultForeignCallBuilder {
125
100
output : PrintOutput :: Stdout ,
126
101
enable_mocks : false ,
@@ -130,80 +105,7 @@ fn execute(circuit: &Circuit, args: &ExecuteCommand) -> Result<SolvedWitnesses,
130
105
}
131
106
. build ( ) ;
132
107
133
- let witness_stack = nargo:: ops:: execute_program (
134
- & circuit. bytecode ,
135
- initial_witness,
136
- & Bn254BlackBoxSolver ( args. pedantic_solving ) ,
137
- & mut foreign_call_executor,
138
- ) ?;
139
-
140
- let main_witness =
141
- & witness_stack. peek ( ) . expect ( "Should have at least one witness on the stack" ) . witness ;
142
-
143
- let ( _, actual_return) = circuit. abi . decode ( main_witness) ?;
108
+ let blackbox_solver = Bn254BlackBoxSolver ( args. pedantic_solving ) ;
144
109
145
- Ok ( SolvedWitnesses { expected_return, actual_return, witness_stack } )
146
- }
147
-
148
- /// Print an error stack trace, if possible.
149
- fn show_diagnostic ( circuit : Circuit , err : NargoError < FieldElement > ) {
150
- if let Some ( diagnostic) = nargo:: errors:: try_to_diagnose_runtime_error (
151
- & err,
152
- & circuit. abi ,
153
- & circuit. debug_symbols . debug_infos ,
154
- ) {
155
- let debug_artifact = DebugArtifact {
156
- debug_symbols : circuit. debug_symbols . debug_infos ,
157
- file_map : circuit. file_map ,
158
- } ;
159
- diagnostic. report ( & debug_artifact, false ) ;
160
- }
161
- }
162
-
163
- /// Print information about the witness and compare to expectations,
164
- /// returning errors if something isn't right.
165
- fn save_witness (
166
- circuit : Circuit ,
167
- args : ExecuteCommand ,
168
- solved : SolvedWitnesses ,
169
- ) -> eyre:: Result < ( ) > {
170
- let artifact = args. artifact_path . file_stem ( ) . and_then ( |s| s. to_str ( ) ) . unwrap_or_default ( ) ;
171
- let name = circuit
172
- . name
173
- . as_ref ( )
174
- . map ( |name| format ! ( "{artifact}.{name}" ) )
175
- . unwrap_or_else ( || artifact. to_string ( ) ) ;
176
-
177
- println ! ( "[{}] Circuit witness successfully solved" , name) ;
178
-
179
- if let Some ( ref witness_dir) = args. output_dir {
180
- let witness_path = save_witness_to_dir (
181
- solved. witness_stack ,
182
- & args. witness_name . unwrap_or_else ( || name. clone ( ) ) ,
183
- witness_dir,
184
- ) ?;
185
- println ! ( "[{}] Witness saved to {}" , name, witness_path. display( ) ) ;
186
- }
187
-
188
- // Check that the circuit returned a non-empty result if the ABI expects a return value.
189
- if let Some ( ref expected) = circuit. abi . return_type {
190
- if solved. actual_return . is_none ( ) {
191
- bail ! ( "Missing return witness; expected a value of type {expected:?}" ) ;
192
- }
193
- }
194
-
195
- // Check that if the prover file contained a `return` entry then that's what we got.
196
- if let Some ( expected) = solved. expected_return {
197
- match solved. actual_return {
198
- None => {
199
- bail ! ( "Missing return witness;\n expected:\n {expected:?}" ) ;
200
- }
201
- Some ( actual) if actual != expected => {
202
- bail ! ( "Unexpected return witness;\n expected:\n {expected:?}\n got:\n {actual:?}" ) ;
203
- }
204
- _ => { }
205
- }
206
- }
207
-
208
- Ok ( ( ) )
110
+ execution:: execute ( circuit, & blackbox_solver, & mut foreign_call_executor, & args. prover_file )
209
111
}
0 commit comments