leo_disassembler/
lib.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_ast::{Composite, FunctionStub, Identifier, Mapping, ProgramId, Stub};
18use leo_errors::UtilError;
19use leo_span::Symbol;
20
21use snarkvm::{
22    prelude::{Itertools, Network},
23    synthesizer::program::{CommandTrait, InstructionTrait, Program, ProgramCore},
24};
25
26use std::{fmt, str::FromStr};
27
28pub fn disassemble<N: Network, Instruction: InstructionTrait<N>, Command: CommandTrait<N>>(
29    program: ProgramCore<N, Instruction, Command>,
30) -> Stub {
31    let program_id = ProgramId::from(program.id());
32    Stub {
33        imports: program.imports().into_iter().map(|(id, _)| ProgramId::from(id)).collect(),
34        stub_id: program_id,
35        consts: Vec::new(),
36        structs: [
37            program
38                .structs()
39                .iter()
40                .map(|(id, s)| (Identifier::from(id).name, Composite::from_snarkvm(s)))
41                .collect_vec(),
42            program
43                .records()
44                .iter()
45                .map(|(id, s)| (Identifier::from(id).name, Composite::from_external_record(s, program_id.name.name)))
46                .collect_vec(),
47        ]
48        .concat(),
49        mappings: program
50            .mappings()
51            .into_iter()
52            .map(|(id, m)| (Identifier::from(id).name, Mapping::from_snarkvm(m)))
53            .collect(),
54        functions: [
55            program
56                .closures()
57                .iter()
58                .map(|(id, closure)| {
59                    (Identifier::from(id).name, FunctionStub::from_closure(closure, program_id.name.name))
60                })
61                .collect_vec(),
62            program
63                .functions()
64                .iter()
65                .map(|(id, function)| {
66                    (Identifier::from(id).name, FunctionStub::from_function_core(function, program_id.name.name))
67                })
68                .collect_vec(),
69            program
70                .functions()
71                .iter()
72                .filter_map(|(id, function)| match function.finalize_logic() {
73                    Some(_f) => {
74                        let key_name = Symbol::intern(&format!(
75                            "finalize/{}",
76                            Symbol::intern(&Identifier::from(id).name.to_string())
77                        ));
78                        Some((key_name, FunctionStub::from_finalize(function, key_name, program_id.name.name)))
79                    }
80                    None => None,
81                })
82                .collect_vec(),
83        ]
84        .concat(),
85        span: Default::default(),
86    }
87}
88
89pub fn disassemble_from_str<N: Network>(name: impl fmt::Display, program: &str) -> Result<Stub, UtilError> {
90    match Program::<N>::from_str(program) {
91        Ok(p) => Ok(disassemble(p)),
92        Err(_) => Err(UtilError::snarkvm_parsing_error(name)),
93    }
94}
95
96#[cfg(test)]
97mod tests {
98    use super::*;
99    use leo_span::create_session_if_not_set_then;
100    use snarkvm::synthesizer::program::Program;
101    use std::fs;
102
103    type CurrentNetwork = snarkvm::prelude::MainnetV0;
104
105    #[test]
106    #[ignore]
107    fn credits_test() {
108        create_session_if_not_set_then(|_| {
109            let program = Program::<CurrentNetwork>::credits();
110            match program {
111                Ok(p) => {
112                    let disassembled = disassemble(p);
113                    println!("{}", disassembled);
114                }
115                Err(e) => {
116                    println!("{}", e);
117                }
118            }
119        });
120    }
121    #[test]
122    #[ignore]
123    fn array_test() {
124        create_session_if_not_set_then(|_| {
125            let program_from_file =
126                fs::read_to_string("../tmp/.aleo/registry/mainnet/zk_bitwise_stack_v0_0_2.aleo").unwrap();
127            let _program =
128                disassemble_from_str::<CurrentNetwork>("zk_bitwise_stack_v0_0_2", &program_from_file).unwrap();
129        });
130    }
131}