diff --git a/Cargo.lock b/Cargo.lock index 3b28ca0d846..e65b02fe056 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12,8 +12,8 @@ dependencies = [ "flate2", "serde", "serde_json", - "strum", - "strum_macros", + "strum 0.24.1", + "strum_macros 0.24.3", "thiserror", ] @@ -341,6 +341,12 @@ dependencies = [ "rand", ] +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + [[package]] name = "arrayvec" version = "0.7.4" @@ -951,7 +957,7 @@ version = "0.91.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98b022ed2a5913a38839dfbafe6cf135342661293b08049843362df4301261dc" dependencies = [ - "arrayvec", + "arrayvec 0.7.4", "bumpalo", "cranelift-bforest", "cranelift-codegen-meta", @@ -1107,6 +1113,32 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossterm" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a84cda67535339806297f1b331d6dd6320470d2a0fe65381e79ee9e156dd3d13" +dependencies = [ + "bitflags 1.3.2", + "crossterm_winapi", + "libc", + "mio", + "parking_lot", + "serde", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + [[package]] name = "crypto-bigint" version = "0.4.9" @@ -1447,6 +1479,17 @@ dependencies = [ "instant", ] +[[package]] +name = "fd-lock" +version = "3.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5" +dependencies = [ + "cfg-if", + "rustix 0.38.4", + "windows-sys 0.48.0", +] + [[package]] name = "ff" version = "0.12.1" @@ -2222,6 +2265,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", + "log", "wasi", "windows-sys 0.48.0", ] @@ -2246,6 +2290,7 @@ dependencies = [ "noirc_errors", "noirc_frontend", "noirc_printable_type", + "reedline-repl-rs", "rustc_version", "serde", "thiserror", @@ -2284,6 +2329,7 @@ dependencies = [ "predicates 2.1.5", "prettytable-rs", "rayon", + "reedline-repl-rs", "rustc_version", "serde", "serde_json", @@ -2377,8 +2423,8 @@ dependencies = [ "num-traits", "serde", "serde_json", - "strum", - "strum_macros", + "strum 0.24.1", + "strum_macros 0.24.3", "thiserror", "toml", ] @@ -2460,8 +2506,8 @@ dependencies = [ "serde_json", "small-ord-set", "smol_str", - "strum", - "strum_macros", + "strum 0.24.1", + "strum_macros 0.24.3", "thiserror", ] @@ -2483,6 +2529,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" +[[package]] +name = "nu-ansi-term" +version = "0.49.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c073d3c1930d0751774acf49e66653acecb416c3a54c6ec095a9b11caddb5a68" +dependencies = [ + "windows-sys 0.48.0", +] + [[package]] name = "num-bigint" version = "0.4.3" @@ -2500,7 +2555,7 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" dependencies = [ - "arrayvec", + "arrayvec 0.7.4", "itoa", ] @@ -2949,6 +3004,41 @@ dependencies = [ "thiserror", ] +[[package]] +name = "reedline" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2fde955d11817fdcb79d703932fb6b473192cb36b6a92ba21f7f4ac0513374e" +dependencies = [ + "chrono", + "crossterm", + "fd-lock", + "itertools", + "nu-ansi-term", + "serde", + "strip-ansi-escapes", + "strum 0.25.0", + "strum_macros 0.25.2", + "thiserror", + "unicode-segmentation", + "unicode-width", +] + +[[package]] +name = "reedline-repl-rs" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dda59601d3b0c3ed9de96d342ca40f95cca2409e8497249139a6c723b211ee33" +dependencies = [ + "clap", + "crossterm", + "nu-ansi-term", + "reedline", + "regex", + "winapi-util", + "yansi", +] + [[package]] name = "regalloc2" version = "0.5.1" @@ -3514,6 +3604,36 @@ dependencies = [ "dirs", ] +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + [[package]] name = "signature" version = "1.6.4" @@ -3623,6 +3743,15 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9091b6114800a5f2141aee1d1b9d6ca3592ac062dc5decb3764ec5895a47b4eb" +[[package]] +name = "strip-ansi-escapes" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "011cbb39cf7c1f62871aea3cc46e5817b0937b49e9447370c93cacbe93a766d8" +dependencies = [ + "vte", +] + [[package]] name = "strsim" version = "0.10.0" @@ -3635,6 +3764,12 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" + [[package]] name = "strum_macros" version = "0.24.3" @@ -3648,6 +3783,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "strum_macros" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8d03b598d3d0fff69bf533ee3ef19b8eeb342729596df84bcc7e1f96ec4059" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.26", +] + [[package]] name = "subtle" version = "2.5.0" @@ -4063,6 +4211,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + [[package]] name = "unicode-width" version = "0.1.10" @@ -4117,6 +4271,27 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "vte" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cbce692ab4ca2f1f3047fcf732430249c0e971bfdd2b234cf2c47ad93af5983" +dependencies = [ + "arrayvec 0.5.2", + "utf8parse", + "vte_generate_state_changes", +] + +[[package]] +name = "vte_generate_state_changes" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff" +dependencies = [ + "proc-macro2", + "quote", +] + [[package]] name = "wait-timeout" version = "0.2.0" @@ -4662,6 +4837,12 @@ dependencies = [ "libc", ] +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" + [[package]] name = "zeroize" version = "1.6.0" diff --git a/tooling/nargo/Cargo.toml b/tooling/nargo/Cargo.toml index c038ab6b1e4..6a347314f47 100644 --- a/tooling/nargo/Cargo.toml +++ b/tooling/nargo/Cargo.toml @@ -24,3 +24,4 @@ serde.workspace = true thiserror.workspace = true base64.workspace = true codespan-reporting.workspace = true +reedline-repl-rs = "1.0.7" \ No newline at end of file diff --git a/tooling/nargo/src/ops/debug.rs b/tooling/nargo/src/ops/debug.rs index fae53f9916d..8d5c533b73d 100644 --- a/tooling/nargo/src/ops/debug.rs +++ b/tooling/nargo/src/ops/debug.rs @@ -11,6 +11,12 @@ use super::foreign_calls::ForeignCallExecutor; use std::io::{self, Write}; +use reedline_repl_rs::clap::{ + ArgMatches as ReplArgMatches, + Command as ReplCommand, +}; +use reedline_repl_rs::Repl; + enum SolveResult { Done, Ok, @@ -22,6 +28,31 @@ enum Command { Stop, } +struct ReplContext<'backend, B: BlackBoxFunctionSolver> { + acvm: ACVM<'backend, B>, + debug_artifact: DebugArtifact, + foreign_call_executor: ForeignCallExecutor, + circuit: Circuit, + show_output: bool, +} + +impl From for NargoError { + fn from(_e: reedline_repl_rs::Error) -> Self { + NargoError::CompilationError + } +} + + +// impl fmt::Display for NargoError { +// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +// match self { +// NargoError::CompilationError => write!(f, "Compilation Error"), +// NargoError::ExecutionError(e) => write!(f, "Execution Error: {}", e), +// NargoError::ForeignCallError(e) => write!(f, "Foreign call Error: {}", e), +// } +// } +// } + pub fn debug_circuit( blackbox_solver: &B, circuit: Circuit, @@ -29,39 +60,39 @@ pub fn debug_circuit( initial_witness: WitnessMap, show_output: bool, ) -> Result { - let mut acvm = ACVM::new(blackbox_solver, circuit.opcodes, initial_witness); + let opcodes = circuit.opcodes.clone(); + let acvm = ACVM::new(blackbox_solver, opcodes, initial_witness); let mut foreign_call_executor = ForeignCallExecutor::default(); - 'outer: loop { - show_current_vm_status(&acvm, &debug_artifact); - let command = match read_command() { - Ok(cmd) => cmd, - Err(err) => { - eprintln!("Error reading command: {}", err); - return Err(NargoError::ExecutionError(ExecutionError::Halted)) - } - }; - match command { - Command::Stop => return Err(NargoError::ExecutionError(ExecutionError::Halted)), - Command::Step => { - match step_opcode(&mut acvm, &circuit.assert_messages, show_output, &mut foreign_call_executor)? { - SolveResult::Done => break, - SolveResult::Ok => {}, - } - } - Command::Continue => { - println!("(Continuing execution...)"); - loop { - match step_opcode(&mut acvm, &circuit.assert_messages, show_output, &mut foreign_call_executor)? { - SolveResult::Done => break 'outer, - SolveResult::Ok => {}, - } - } - }, - } - } + let repl_context = ReplContext { + acvm, + debug_artifact, + foreign_call_executor, + circuit, + show_output, + }; - let solved_witness = acvm.finalize(); + let mut repl = Repl::new(repl_context) + .with_name("debug") + .with_version(env!["CARGO_PKG_VERSION"]) + .with_command( + ReplCommand::new("s") + .about("step to the next opcode"), + step_command, + ) + .with_command( + ReplCommand::new("c") + .about("continue execution until the end of the program"), + continue_command, + ) + .with_command( + ReplCommand::new("q") + .about("quit the debugger"), + quit_command, + ); + repl.run().unwrap(); + + let solved_witness = repl_context.acvm.finalize(); Ok(solved_witness) } @@ -157,3 +188,28 @@ fn read_command() -> Result { } } } + +fn step_command(_args: ReplArgMatches, context: &mut ReplContext) -> Result, NargoError> { + show_current_vm_status(&mut context.acvm, &mut context.debug_artifact); + match step_opcode(&mut context.acvm, &mut context.circuit.assert_messages, context.show_output, &mut context.foreign_call_executor)? { + SolveResult::Done => Ok(Some("Done".to_string())), + SolveResult::Ok => Ok(Some("Ok".to_string())), + } +} + +fn continue_command(_args: ReplArgMatches, context: &mut ReplContext) -> Result, NargoError> { + show_current_vm_status(&mut context.acvm, &mut context.debug_artifact); + println!("(Continuing execution...)"); + loop { + match step_opcode(&mut context.acvm, &context.circuit.assert_messages, context.show_output, &mut context.foreign_call_executor)? { + SolveResult::Done => break, + SolveResult::Ok => {}, + } + } + Ok(Some("Ok".to_string())) +} + +fn quit_command(_args: ReplArgMatches, context: &mut ReplContext) -> Result, NargoError> { + show_current_vm_status(&mut context.acvm, &mut context.debug_artifact); + Err(NargoError::ExecutionError(ExecutionError::Halted)) +} diff --git a/tooling/nargo_cli/Cargo.toml b/tooling/nargo_cli/Cargo.toml index 0ae6073f0a1..bd9f14b035c 100644 --- a/tooling/nargo_cli/Cargo.toml +++ b/tooling/nargo_cli/Cargo.toml @@ -36,6 +36,7 @@ serde.workspace = true serde_json.workspace = true prettytable-rs = "0.10" rayon = "1.7.0" +reedline-repl-rs = "1.0.7" thiserror.workspace = true tower.workspace = true async-lsp = { version = "0.0.5", default-features = false, features = [ diff --git a/tooling/nargo_cli/src/cli/debug_cmd.rs b/tooling/nargo_cli/src/cli/debug_cmd.rs index 024037d06b6..c58525ebbb7 100644 --- a/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -26,10 +26,14 @@ pub(crate) struct DebugCommand { #[clap(long, short, default_value = PROVER_INPUT_FILE)] prover_name: String, - /// The name of the package to execute + /// The name of the package to debug #[clap(long)] package: Option, + /// Detail all packages in the workspace + #[clap(long, conflicts_with = "package")] + workspace: bool, + #[clap(flatten)] compile_options: CompileOptions, } @@ -45,6 +49,7 @@ pub(crate) fn run( let target_dir = &workspace.target_directory_path(); let (np_language, opcode_support) = backend.get_backend_info()?; + for package in &workspace { let compiled_program = compile_bin_package( &workspace, diff --git a/tooling/nargo_cli/src/cli/mod.rs b/tooling/nargo_cli/src/cli/mod.rs index 657973a92d0..03214dec9c3 100644 --- a/tooling/nargo_cli/src/cli/mod.rs +++ b/tooling/nargo_cli/src/cli/mod.rs @@ -13,6 +13,7 @@ mod backend_cmd; mod check_cmd; mod codegen_verifier_cmd; mod compile_cmd; +mod debug_cmd; mod execute_cmd; mod info_cmd; mod init_cmd; @@ -94,6 +95,7 @@ pub(crate) fn start_cli() -> eyre::Result<()> { NargoCommand::Init(args) => init_cmd::run(&backend, args, config), NargoCommand::Check(args) => check_cmd::run(&backend, args, config), NargoCommand::Compile(args) => compile_cmd::run(&backend, args, config), + NargoCommand::Debug(args) => debug_cmd::run(&backend, args, config), NargoCommand::Execute(args) => execute_cmd::run(&backend, args, config), NargoCommand::Debug(args) => debug_cmd::run(&backend, args, config), NargoCommand::Prove(args) => prove_cmd::run(&backend, args, config),