#[cfg(target_arch = "wasm32")]
extern crate self as snarkvm;
#[cfg(target_arch = "wasm32")]
mod snarkvm_wasm;
#[cfg(target_arch = "wasm32")]
#[doc(hidden)]
pub use snarkvm_wasm::{prelude, synthesizer};
mod errors;
use leo_ast::{AleoProgram, Composite, FunctionStub, Identifier, Mapping, NetworkName, ProgramId};
use leo_errors::LeoError;
use leo_span::Symbol;
use snarkvm::{
prelude::{Itertools, Network},
synthesizer::program::{Program, ProgramCore},
};
use std::{fmt, str::FromStr};
pub fn disassemble<N: Network>(program: ProgramCore<N>) -> AleoProgram {
let program_id = ProgramId::from(program.id());
AleoProgram {
imports: program.imports().into_iter().map(|(id, _)| ProgramId::from(id)).collect(),
stub_id: program_id,
consts: Vec::new(),
composites: [
program
.structs()
.iter()
.map(|(id, s)| (Identifier::from(id).name, Composite::from_snarkvm(s, program_id)))
.collect_vec(),
program
.records()
.iter()
.map(|(id, s)| (Identifier::from(id).name, Composite::from_external_record(s, program_id)))
.collect_vec(),
]
.concat(),
mappings: program
.mappings()
.into_iter()
.map(|(id, m)| (Identifier::from(id).name, Mapping::from_snarkvm(m, program_id)))
.collect(),
functions: [
program
.closures()
.iter()
.map(|(id, closure)| (Identifier::from(id).name, FunctionStub::from_closure(closure, program_id)))
.collect_vec(),
program
.functions()
.iter()
.map(|(id, function)| {
(Identifier::from(id).name, FunctionStub::from_function_core(function, program_id))
})
.collect_vec(),
program
.functions()
.iter()
.filter_map(|(id, function)| match function.finalize_logic() {
Some(_f) => {
let key_name = Symbol::intern(&format!(
"finalize/{}",
Symbol::intern(&Identifier::from(id).name.to_string())
));
Some((key_name, FunctionStub::from_finalize(function, key_name, program_id)))
}
None => None,
})
.collect_vec(),
program
.views()
.iter()
.map(|(id, view)| (Identifier::from(id).name, FunctionStub::from_view(view, program_id)))
.collect_vec(),
]
.concat(),
span: Default::default(),
}
}
pub fn disassemble_from_str_unchecked<N: Network>(
name: impl fmt::Display,
program: &str,
) -> Result<AleoProgram, LeoError> {
let p = Program::<N>::from_str(program).map_err(|_| crate::errors::snarkvm_parsing_error(name))?;
Ok(disassemble(p))
}
#[cfg(not(target_arch = "wasm32"))]
pub fn disassemble_from_str<N: Network>(
name: impl fmt::Display,
program: &str,
process: &mut snarkvm::prelude::Process<N>,
) -> Result<AleoProgram, LeoError> {
let p = Program::<N>::from_str(program).map_err(|_| crate::errors::snarkvm_parsing_error(&name))?;
validate_and_disassemble(name, p, process)
}
#[cfg(not(target_arch = "wasm32"))]
pub fn validate_and_disassemble<N: Network>(
name: impl fmt::Display,
program: Program<N>,
process: &mut snarkvm::prelude::Process<N>,
) -> Result<AleoProgram, LeoError> {
process.lock().add_program(&program).map_err(|e| crate::errors::snarkvm_validation_error(&name, e))?;
Ok(disassemble(program))
}
pub fn disassemble_from_str_for_network(
name: impl fmt::Display,
program: &str,
network: NetworkName,
) -> Result<AleoProgram, LeoError> {
#[cfg(not(target_arch = "wasm32"))]
{
match network {
NetworkName::MainnetV0 => {
let mut process = snarkvm::prelude::Process::<snarkvm::prelude::MainnetV0>::load()
.map_err(|e| crate::errors::snarkvm_validation_error(&name, e))?;
disassemble_from_str(name, program, &mut process)
}
NetworkName::TestnetV0 => {
let mut process = snarkvm::prelude::Process::<snarkvm::prelude::TestnetV0>::load()
.map_err(|e| crate::errors::snarkvm_validation_error(&name, e))?;
disassemble_from_str(name, program, &mut process)
}
NetworkName::CanaryV0 => {
let mut process = snarkvm::prelude::Process::<snarkvm::prelude::CanaryV0>::load()
.map_err(|e| crate::errors::snarkvm_validation_error(&name, e))?;
disassemble_from_str(name, program, &mut process)
}
}
}
#[cfg(target_arch = "wasm32")]
{
match network {
NetworkName::MainnetV0 => disassemble_from_str_unchecked::<snarkvm::prelude::MainnetV0>(name, program),
NetworkName::TestnetV0 => disassemble_from_str_unchecked::<snarkvm::prelude::TestnetV0>(name, program),
NetworkName::CanaryV0 => disassemble_from_str_unchecked::<snarkvm::prelude::CanaryV0>(name, program),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use leo_span::create_session_if_not_set_then;
use snarkvm::synthesizer::program::Program;
use std::fs;
type CurrentNetwork = snarkvm::prelude::MainnetV0;
#[test]
#[ignore]
fn credits_test() {
create_session_if_not_set_then(|_| {
let program = Program::<CurrentNetwork>::credits();
match program {
Ok(p) => {
let disassembled = disassemble(p);
println!("{disassembled}");
}
Err(e) => {
println!("{e}");
}
}
});
}
#[test]
#[ignore]
fn array_test() {
create_session_if_not_set_then(|_| {
let program_from_file =
fs::read_to_string("../tmp/.aleo/registry/mainnet/zk_bitwise_stack_v0_0_2.aleo").unwrap();
let _program =
disassemble_from_str_unchecked::<CurrentNetwork>("zk_bitwise_stack_v0_0_2", &program_from_file)
.unwrap();
});
}
#[test]
fn rejects_future_typed_register_input_without_panic() {
create_session_if_not_set_then(|_| {
let src = include_str!("tests/victim_future_input.aleo");
let mut process = snarkvm::prelude::Process::<CurrentNetwork>::load().unwrap();
let result = disassemble_from_str::<CurrentNetwork>("victim", src, &mut process);
assert!(result.is_err(), "expected disassembler to reject malformed bytecode, got Ok");
});
}
#[test]
fn snarkvm_rejects_view_name_colliding_with_function() {
create_session_if_not_set_then(|_| {
let src = "\
program collide.aleo;
function foo:
input r0 as u32.public;
output r0 as u32.public;
view foo:
input r0 as u32.public;
output r0 as u32.public;
";
let result = disassemble_from_str_unchecked::<CurrentNetwork>("collide", src);
assert!(
result.is_err(),
"expected snarkVM to reject a program with `function foo` and `view foo` sharing a name, got Ok"
);
});
}
}