diff --git a/acvm-repo/acvm/src/pwg/brillig.rs b/acvm-repo/acvm/src/pwg/brillig.rs index b0e5ec6dd48..0db38c776e2 100644 --- a/acvm-repo/acvm/src/pwg/brillig.rs +++ b/acvm-repo/acvm/src/pwg/brillig.rs @@ -112,6 +112,22 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { Ok(Self { vm, acir_index }) } + pub fn get_registers(&self) -> &Registers { + self.vm.get_registers() + } + + pub fn set_register(&mut self, register_index: usize, value: Value) { + self.vm.set_register(RegisterIndex(register_index), value); + } + + pub fn get_memory(&self) -> &[Value] { + self.vm.get_memory() + } + + pub fn write_memory_at(&mut self, ptr: usize, value: Value) { + self.vm.write_memory_at(ptr, value); + } + pub(super) fn solve(&mut self) -> Result { let status = self.vm.process_opcodes(); self.handle_vm_status(status) diff --git a/acvm-repo/acvm/src/pwg/mod.rs b/acvm-repo/acvm/src/pwg/mod.rs index 6016d0d1f3e..c1edf60161a 100644 --- a/acvm-repo/acvm/src/pwg/mod.rs +++ b/acvm-repo/acvm/src/pwg/mod.rs @@ -168,6 +168,14 @@ impl<'a, B: BlackBoxFunctionSolver> ACVM<'a, B> { &self.witness_map } + pub fn overwrite_witness( + &mut self, + witness: Witness, + value: FieldElement, + ) -> Option { + self.witness_map.insert(witness, value) + } + /// Returns a slice containing the opcodes of the circuit being executed. pub fn opcodes(&self) -> &[Opcode] { self.opcodes diff --git a/acvm-repo/brillig_vm/src/lib.rs b/acvm-repo/brillig_vm/src/lib.rs index e0d30f7b2e0..482b9b36d77 100644 --- a/acvm-repo/brillig_vm/src/lib.rs +++ b/acvm-repo/brillig_vm/src/lib.rs @@ -164,10 +164,18 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { &self.registers } + pub fn set_register(&mut self, register_index: RegisterIndex, value: Value) { + self.registers.set(register_index, value); + } + pub fn get_memory(&self) -> &Vec { self.memory.values() } + pub fn write_memory_at(&mut self, ptr: usize, value: Value) { + self.memory.write(ptr, value); + } + /// Process a single opcode and modify the program counter. pub fn process_opcode(&mut self) -> VMStatus { let opcode = &self.bytecode[self.program_counter]; diff --git a/tooling/debugger/src/context.rs b/tooling/debugger/src/context.rs index a97bc9e8b86..37c3b9d89d9 100644 --- a/tooling/debugger/src/context.rs +++ b/tooling/debugger/src/context.rs @@ -1,9 +1,10 @@ -use acvm::acir::circuit::{Opcode, OpcodeLocation}; +use acvm::acir::circuit::{Circuit, Opcode, OpcodeLocation}; +use acvm::acir::native_types::{Witness, WitnessMap}; +use acvm::brillig_vm::{brillig::Value, Registers}; use acvm::pwg::{ ACVMStatus, BrilligSolver, BrilligSolverStatus, ForeignCallWaitInfo, StepResult, ACVM, }; -use acvm::BlackBoxFunctionSolver; -use acvm::{acir::circuit::Circuit, acir::native_types::WitnessMap}; +use acvm::{BlackBoxFunctionSolver, FieldElement}; use nargo::artifacts::debug::DebugArtifact; use nargo::errors::{ExecutionError, Location}; @@ -50,6 +51,18 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { self.acvm.opcodes() } + pub(super) fn get_witness_map(&self) -> &WitnessMap { + self.acvm.witness_map() + } + + pub(super) fn overwrite_witness( + &mut self, + witness: Witness, + value: FieldElement, + ) -> Option { + self.acvm.overwrite_witness(witness, value) + } + pub(super) fn get_current_opcode_location(&self) -> Option { let ip = self.acvm.instruction_pointer(); if ip >= self.get_opcodes().len() { @@ -64,11 +77,11 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { } } - // Returns the callstack in source code locations for the currently - // executing opcode. This can be None if the execution finished (and - // get_current_opcode_location() returns None) or if the opcode is not - // mapped to a specific source location in the debug artifact (which can - // happen for certain opcodes inserted synthetically by the compiler) + /// Returns the callstack in source code locations for the currently + /// executing opcode. This can be `None` if the execution finished (and + /// `get_current_opcode_location()` returns `None`) or if the opcode is not + /// mapped to a specific source location in the debug artifact (which can + /// happen for certain opcodes inserted synthetically by the compiler) pub(super) fn get_current_source_location(&self) -> Option> { self.get_current_opcode_location() .as_ref() @@ -190,6 +203,32 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { } } + pub(super) fn is_executing_brillig(&self) -> bool { + let opcodes = self.get_opcodes(); + let acir_index = self.acvm.instruction_pointer(); + acir_index < opcodes.len() && matches!(opcodes[acir_index], Opcode::Brillig(..)) + } + + pub(super) fn get_brillig_registers(&self) -> Option<&Registers> { + self.brillig_solver.as_ref().map(|solver| solver.get_registers()) + } + + pub(super) fn set_brillig_register(&mut self, register_index: usize, value: FieldElement) { + if let Some(solver) = self.brillig_solver.as_mut() { + solver.set_register(register_index, value.into()); + } + } + + pub(super) fn get_brillig_memory(&self) -> Option<&[Value]> { + self.brillig_solver.as_ref().map(|solver| solver.get_memory()) + } + + pub(super) fn write_brillig_memory(&mut self, ptr: usize, value: FieldElement) { + if let Some(solver) = self.brillig_solver.as_mut() { + solver.write_memory_at(ptr, value.into()); + } + } + fn breakpoint_reached(&self) -> bool { if let Some(location) = self.get_current_opcode_location() { self.breakpoints.contains(&location) diff --git a/tooling/debugger/src/repl.rs b/tooling/debugger/src/repl.rs index 320d8edf63a..23abe83c38d 100644 --- a/tooling/debugger/src/repl.rs +++ b/tooling/debugger/src/repl.rs @@ -1,8 +1,8 @@ use crate::context::{DebugCommandResult, DebugContext}; -use acvm::acir::circuit::{Opcode, OpcodeLocation}; -use acvm::BlackBoxFunctionSolver; -use acvm::{acir::circuit::Circuit, acir::native_types::WitnessMap}; +use acvm::acir::circuit::{Circuit, Opcode, OpcodeLocation}; +use acvm::acir::native_types::{Witness, WitnessMap}; +use acvm::{BlackBoxFunctionSolver, FieldElement}; use nargo::artifacts::debug::DebugArtifact; use nargo::NargoError; @@ -283,6 +283,93 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { self.show_current_vm_status(); } + pub fn show_witness_map(&self) { + let witness_map = self.context.get_witness_map(); + // NOTE: we need to clone() here to get the iterator + for (witness, value) in witness_map.clone().into_iter() { + println!("_{} = {value}", witness.witness_index()); + } + } + + pub fn show_witness(&self, index: u32) { + if let Some(value) = self.context.get_witness_map().get_index(index) { + println!("_{} = {value}", index); + } + } + + pub fn update_witness(&mut self, index: u32, value: String) { + let Some(field_value) = FieldElement::try_from_str(&value) else { + println!("Invalid witness value: {value}"); + return; + }; + + let witness = Witness::from(index); + _ = self.context.overwrite_witness(witness, field_value); + println!("_{} = {value}", index); + } + + pub fn show_brillig_registers(&self) { + if !self.context.is_executing_brillig() { + println!("Not executing a Brillig block"); + return; + } + + let Some(registers) = self.context.get_brillig_registers() else { + // this can happen when just entering the Brillig block since ACVM + // would have not initialized the Brillig VM yet; in fact, the + // Brillig code may be skipped altogether + println!("Brillig VM registers not available"); + return; + }; + + for (index, value) in registers.inner.iter().enumerate() { + println!("{index} = {}", value.to_field()); + } + } + + pub fn set_brillig_register(&mut self, index: usize, value: String) { + let Some(field_value) = FieldElement::try_from_str(&value) else { + println!("Invalid value: {value}"); + return; + }; + if !self.context.is_executing_brillig() { + println!("Not executing a Brillig block"); + return; + } + self.context.set_brillig_register(index, field_value); + } + + pub fn show_brillig_memory(&self) { + if !self.context.is_executing_brillig() { + println!("Not executing a Brillig block"); + return; + } + + let Some(memory) = self.context.get_brillig_memory() else { + // this can happen when just entering the Brillig block since ACVM + // would have not initialized the Brillig VM yet; in fact, the + // Brillig code may be skipped altogether + println!("Brillig VM memory not available"); + return; + }; + + for (index, value) in memory.iter().enumerate() { + println!("{index} = {}", value.to_field()); + } + } + + pub fn write_brillig_memory(&mut self, index: usize, value: String) { + let Some(field_value) = FieldElement::try_from_str(&value) else { + println!("Invalid value: {value}"); + return; + }; + if !self.context.is_executing_brillig() { + println!("Not executing a Brillig block"); + return; + } + self.context.write_brillig_memory(index, field_value); + } + fn is_solved(&self) -> bool { self.context.is_solved() } @@ -393,6 +480,76 @@ pub fn run( } }, ) + .add( + "witness", + command! { + "show witness map", + () => || { + ref_context.borrow().show_witness_map(); + Ok(CommandStatus::Done) + } + }, + ) + .add( + "witness", + command! { + "display a single witness from the witness map", + (index: u32) => |index| { + ref_context.borrow().show_witness(index); + Ok(CommandStatus::Done) + } + }, + ) + .add( + "witness", + command! { + "update a witness with the given value", + (index: u32, value: String) => |index, value| { + ref_context.borrow_mut().update_witness(index, value); + Ok(CommandStatus::Done) + } + }, + ) + .add( + "registers", + command! { + "show Brillig registers (valid when executing a Brillig block)", + () => || { + ref_context.borrow().show_brillig_registers(); + Ok(CommandStatus::Done) + } + }, + ) + .add( + "regset", + command! { + "update a Brillig register with the given value", + (index: usize, value: String) => |index, value| { + ref_context.borrow_mut().set_brillig_register(index, value); + Ok(CommandStatus::Done) + } + }, + ) + .add( + "memory", + command! { + "show Brillig memory (valid when executing a Brillig block)", + () => || { + ref_context.borrow().show_brillig_memory(); + Ok(CommandStatus::Done) + } + }, + ) + .add( + "memset", + command! { + "update a Brillig memory cell with the given value", + (index: usize, value: String) => |index, value| { + ref_context.borrow_mut().write_brillig_memory(index, value); + Ok(CommandStatus::Done) + } + }, + ) .build() .expect("Failed to initialize debugger repl");