From 6fdc47f33bcea8edf09d90a62245588c2ba7c14a Mon Sep 17 00:00:00 2001 From: Rog3rSm1th Date: Mon, 6 Jan 2025 15:11:22 +0100 Subject: [PATCH 1/5] Bump dependencies --- Cargo.toml | 4 ++-- lib/Cargo.toml | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f85a067..e804d1d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,7 +41,7 @@ clap = { version = "4.0.0-rc.1", features = [ "derive" ] } serde = "1.0.209" serde_json = "1.0.116" tokio = "1.37.0" -cairo-lang-sierra = "~2.8.4" -cairo-lang-starknet-classes = "~2.8.4" +cairo-lang-sierra = "~2.9.2" +cairo-lang-starknet-classes = "~2.9.2" sierra-analyzer-lib = { path = "./lib" } diff --git a/lib/Cargo.toml b/lib/Cargo.toml index f81b715..69cbd6d 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -6,9 +6,9 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -cairo-lang-sierra = "~2.8.4" -cairo-lang-starknet-classes = "~2.8.4" -cairo-lang-starknet = "~2.8.4" +cairo-lang-sierra = "~2.9.2" +cairo-lang-starknet-classes = "~2.9.2" +cairo-lang-starknet = "~2.9.2" colored = "2.1.0" graphviz-rust = "0.9.0" hex = "0.4.3" @@ -24,4 +24,4 @@ z3 = "0.12.1" [dev-dependencies] serde_json = "1.0.116" -cairo-lang-starknet-classes = "~2.8.4" +cairo-lang-starknet-classes = "~2.9.2" From 10f5a8f0df75dc820f8c1dc25d234eb36e9f0c88 Mon Sep 17 00:00:00 2001 From: Rog3rSm1th Date: Mon, 6 Jan 2025 15:51:11 +0100 Subject: [PATCH 2/5] Update providers (mainnet/sepolia) & Update documentation --- README.md | 4 ++-- lib/src/decompiler/function.rs | 2 +- lib/src/provider.rs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ea2eacc..fee40ac 100644 --- a/README.md +++ b/README.md @@ -58,10 +58,10 @@ Contracts can be fetched directly from Starknet (Mainnet & Sepolia) by specifyin ``` # Fetch & decompile a contract from starknet mainnet -cargo run -- --remote 0x07c43d18d37d66d7855dab8f21ebf9d554dd213c6307aacecaf2d595a53b3bbb +cargo run -- --remote 0x035ae0fe6ca00fcc8020a6c64503f38bfaf3481ae9a6c8b7daec2f899df735fa # Fetch & decompile a contract from Sepolia network -cargo run -- --network sepolia --remote 0x068377a89d64c0b16dc97c66933777bf4e9b050652c4fde2c59c8c4d755a163b +cargo run -- --remote 0x01437be408319cdb7524b3e3c52c0e9d80070d8cb85f363d42a7c3c2df5b66b2 --network sepolia -d ``` ### Print the contract's Control-Flow Graph diff --git a/lib/src/decompiler/function.rs b/lib/src/decompiler/function.rs index 3c82663..634057e 100644 --- a/lib/src/decompiler/function.rs +++ b/lib/src/decompiler/function.rs @@ -565,7 +565,7 @@ impl<'a> Function<'a> { .expect("Library function not found in the registry"); if let CoreConcreteLibfunc::FunctionCall(f_called) = lib_func { - let function_name = f_called.function.id.debug_name.as_ref().unwrap(); + let function_name = parse_element_name!(f_called.function.id); for function in functions { let current_function_name = diff --git a/lib/src/provider.rs b/lib/src/provider.rs index 45b615d..8df3d14 100644 --- a/lib/src/provider.rs +++ b/lib/src/provider.rs @@ -9,11 +9,11 @@ pub struct NetworkConfig; impl NetworkConfig { /// URL for the mainnet API pub const MAINNET_API_URL: &'static str = - "https://starknet-mainnet.public.blastapi.io/rpc/v0_6"; + "https://starknet-mainnet.public.blastapi.io/rpc/v0_7"; /// URL for the Sepolia API pub const SEPOLIA_API_URL: &'static str = - "https://starknet-sepolia.public.blastapi.io/rpc/v0_6"; + "https://starknet-sepolia.public.blastapi.io/rpc/v0_7"; } /// Struct representing an RPC client From 3c0403dee222ffcbd9aa72613101eb29835416d5 Mon Sep 17 00:00:00 2001 From: Rog3rSm1th Date: Tue, 7 Jan 2025 01:03:24 +0100 Subject: [PATCH 3/5] Add `--scarb` command-line flag --- bin/sierra-decompiler/src/main.rs | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/bin/sierra-decompiler/src/main.rs b/bin/sierra-decompiler/src/main.rs index d95369d..5fb37e2 100644 --- a/bin/sierra-decompiler/src/main.rs +++ b/bin/sierra-decompiler/src/main.rs @@ -67,15 +67,19 @@ struct Args { /// Network type (Mainnet & Sepolia are supported) #[clap(long, default_value = "mainnet")] network: String, + + /// Run sierra-analyzer in a repo that uses Scarb + #[clap(long)] + scarb: bool, } #[tokio::main] async fn main() { let args = Args::parse(); - // Ensure either remote or Sierra file is provided - if args.remote.is_empty() && args.sierra_file.is_none() { - eprintln!("Error: Either remote or Sierra file must be provided"); + // Ensure either remote, Sierra file, or scarb is provided + if args.remote.is_empty() && args.sierra_file.is_none() && !args.scarb { + eprintln!("Error: Either remote, Sierra file, or --scarb flag must be provided"); return; } @@ -120,9 +124,11 @@ async fn main() { } } -/// Load the Sierra program from either a remote source or a local file +/// Load the Sierra program from either a remote source, a local file, or scarb async fn load_program(args: &Args) -> Result { - if !args.remote.is_empty() { + if args.scarb { + load_scarb_program(args).await + } else if !args.remote.is_empty() { load_remote_program(args).await } else { load_local_program(args) @@ -196,10 +202,19 @@ fn load_local_program(args: &Args) -> Result { Ok(program) } +/// Load the Sierra program in the /target directory +async fn load_scarb_program(args: &Args) -> Result { + // TODO: Implement the logic to load the sierra-program from /target/dev/ directory + todo!() +} + /// Get the file stem based on the remote address or the Sierra file fn get_file_stem(args: &Args) -> String { if !args.remote.is_empty() { args.remote.clone() + } else if args.scarb { + // TODO : modify with the program name + "sierra_program".to_string() } else { args.sierra_file .as_ref() From d2503527ac7c4992c25a53a9cdf2c96c03c4643d Mon Sep 17 00:00:00 2001 From: Rog3rSm1th Date: Wed, 8 Jan 2025 15:27:04 +0100 Subject: [PATCH 4/5] Implement `load_scarb_program` function --- bin/sierra-decompiler/src/main.rs | 79 ++++++++++++++++++++++++++++--- 1 file changed, 73 insertions(+), 6 deletions(-) diff --git a/bin/sierra-decompiler/src/main.rs b/bin/sierra-decompiler/src/main.rs index 5fb37e2..1704ed6 100644 --- a/bin/sierra-decompiler/src/main.rs +++ b/bin/sierra-decompiler/src/main.rs @@ -1,7 +1,8 @@ use std::fs; use std::fs::File; use std::io::Read; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; +use std::process::exit; use clap::Parser; use serde_json; @@ -127,7 +128,7 @@ async fn main() { /// Load the Sierra program from either a remote source, a local file, or scarb async fn load_program(args: &Args) -> Result { if args.scarb { - load_scarb_program(args).await + load_scarb_program().await } else if !args.remote.is_empty() { load_remote_program(args).await } else { @@ -202,10 +203,76 @@ fn load_local_program(args: &Args) -> Result { Ok(program) } -/// Load the Sierra program in the /target directory -async fn load_scarb_program(args: &Args) -> Result { - // TODO: Implement the logic to load the sierra-program from /target/dev/ directory - todo!() +/// Load the Sierra program from the /target directory +async fn load_scarb_program() -> Result { + let target_dir = Path::new("./target/dev/"); + + // Read the directory contents + let entries = + fs::read_dir(target_dir).map_err(|e| format!("Failed to read directory: {}", e))?; + + // Find the file that ends with "contract_class.json" + let contract_class_file = entries + .filter_map(|entry| { + let entry = entry.ok()?; + let path = entry.path(); + if path.is_file() + && path + .file_name() + .and_then(|name| name.to_str()) + .map_or(false, |name| name.ends_with("contract_class.json")) + { + Some(path) + } else { + None + } + }) + .next(); + + // Check if the file was found + let contract_class_file = if let Some(file) = contract_class_file { + file + } else { + eprintln!("You need to run scarb build before running the sierra-analyzer"); + exit(1); + }; + + // Open the file + let mut file = + File::open(&contract_class_file).map_err(|e| format!("Failed to open file: {}", e))?; + + // Read the file content into a string + let mut content = String::new(); + file.read_to_string(&mut content) + .map_err(|e| format!("Failed to read file: {}", e))?; + + // Deserialize the JSON content into a ContractClass + let contract_class: Result = serde_json::from_str(&content); + + let program_string = match contract_class { + Ok(ref prog) => { + // Extract the Sierra program from the ContractClass + match prog.extract_sierra_program() { + Ok(prog_sierra) => prog_sierra.to_string(), + Err(e) => { + eprintln!("Error extracting Sierra program: {}", e); + content.clone() + } + } + } + Err(ref _e) => content.clone(), + }; + + // Initialize a new SierraProgram with the deserialized Sierra program content + let mut program = SierraProgram::new(program_string); + + // Set the program ABI if deserialization was successful + if let Ok(ref contract_class) = contract_class { + let abi = contract_class.abi.clone(); + program.set_abi(abi.unwrap()); + } + + Ok(program) } /// Get the file stem based on the remote address or the Sierra file From fea7769db8f67dd52bdd1971ad34d0a41896a0d7 Mon Sep 17 00:00:00 2001 From: Rog3rSm1th Date: Wed, 8 Jan 2025 17:19:03 +0100 Subject: [PATCH 5/5] Update documentation --- README.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/README.md b/README.md index fee40ac..39c694e 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ Sierra Analyzer is a security toolkit designed for analyzing Sierra files. It in - [Use the symbolic execution to generate unit tests](#use-the-symbolic-execution-to-generate-unit-tests) - [Improve the decompiler output using LLMs](#print-the-contracts-callgraph) - [Use it as a library](#print-the-contracts-callgraph) +- [Use with a Scarb project](#use-it-with-a-scarb-project) ### Project structure @@ -141,6 +142,30 @@ The tests generator can also be used [with the library](https://github.com/Fuzzi It is also possible to use the `sierra-analyzer-lib` library to decompile serialised or unserialised Sierra files. +### Use it with a Scarb project + +First you need to build the project using Scarb : + +```sh +scarb build +``` + +Then you can run the sierra-decompiler using the `--scarb` flag : + +```sh +// Run the decompiler +sierra-decompiler --scarb + +// Run the analyzer +sierra-decompiler --scarb -a + +// Generate the control-flow graph +sierra-decompiler --scarb --cfg + +// Generate the callgraph +sierra-decompiler --scarb --callgraph +``` + ### Features - [x] Decompiler