leo_lang/cli/commands/
debug.rs

1// Copyright (C) 2019-2025 Provable Inc.
2// This file is part of the Leo library.
3
4// The Leo library is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// The Leo library is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
16
17use leo_package::Package;
18
19use snarkvm::prelude::TestnetV0;
20
21use std::path::PathBuf;
22
23use super::*;
24
25/// Debugs an Aleo program through the interpreter.
26#[derive(Parser, Debug)]
27pub struct LeoDebug {
28    #[arg(long, help = "Use these source files instead of finding source files through the project structure. Program submodules aren't supported here.", num_args = 1..)]
29    pub(crate) paths: Vec<String>,
30    #[arg(long, help = "The block height, accessible via block.height.", default_value = "0")]
31    pub(crate) block_height: u32,
32    #[arg(long, action, help = "Use the text user interface.")]
33    pub(crate) tui: bool,
34    #[clap(flatten)]
35    pub(crate) compiler_options: BuildOptions,
36    #[clap(flatten)]
37    pub(crate) env_override: EnvOptions,
38}
39
40impl Command for LeoDebug {
41    type Input = Option<Package>;
42    type Output = ();
43
44    fn log_span(&self) -> Span {
45        tracing::span!(tracing::Level::INFO, "Leo")
46    }
47
48    fn prelude(&self, context: Context) -> Result<Self::Input> {
49        if self.paths.is_empty() {
50            let package = LeoBuild { options: self.compiler_options.clone(), env_override: self.env_override.clone() }
51                .execute(context)?;
52            Ok(Some(package))
53        } else {
54            Ok(None)
55        }
56    }
57
58    fn apply(self, _: Context, input: Self::Input) -> Result<Self::Output> {
59        handle_debug(&self, input)
60    }
61}
62
63fn handle_debug(command: &LeoDebug, package: Option<Package>) -> Result<()> {
64    // Get the network.
65    let network_name = get_network(&command.env_override.network)?;
66
67    if command.paths.is_empty() {
68        let package = package.unwrap();
69
70        // Get the private key.
71        let private_key = get_private_key(&None)?;
72        let address = Address::<TestnetV0>::try_from(&private_key)?;
73
74        // Get the paths of all local Leo dependencies.
75        let local_dependency_paths = collect_leo_paths(&package);
76        let aleo_paths = collect_aleo_paths(&package);
77
78        // No need to keep this around while the interpreter runs.
79        std::mem::drop(package);
80
81        leo_interpreter::interpret(
82            &local_dependency_paths,
83            &aleo_paths,
84            address.into(),
85            command.block_height,
86            command.tui,
87            network_name,
88        )
89    } else {
90        // Program that have submodules aren't supported in this mode.
91        let private_key: PrivateKey<TestnetV0> = PrivateKey::from_str(leo_package::TEST_PRIVATE_KEY)?;
92        let address = Address::<TestnetV0>::try_from(&private_key)?;
93
94        let leo_paths: Vec<(PathBuf, Vec<PathBuf>)> = command
95            .paths
96            .iter()
97            .filter(|path_str| path_str.ends_with(".leo"))
98            .map(|path_str| (path_str.into(), vec![]))
99            .collect();
100        let aleo_paths: Vec<PathBuf> = command
101            .paths
102            .iter()
103            .filter(|path_str| !path_str.ends_with(".leo"))
104            .map(|path_str| path_str.into())
105            .collect();
106
107        leo_interpreter::interpret(
108            &leo_paths,
109            &aleo_paths,
110            address.into(),
111            command.block_height,
112            command.tui,
113            network_name,
114        )
115    }
116}