Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -359,3 +359,18 @@ jobs:
- name: Run tests
working-directory: ./debug_utils/sierra-emu
run: make test

build-sierra2casm-dbg:
name: Build sierra2casm-dbg
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- name: Retreive cached dependecies
uses: Swatinem/rust-cache@v2
- name: Build
working-directory: ./debug_utils/sierra2casm-dbg
run: cargo build --all-features --verbose
- name: Run tests
working-directory: ./debug_utils/sierra2casm-dbg
run: cargo test
13 changes: 13 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -177,4 +177,4 @@ name = "libfuncs"
harness = false

[workspace]
members = ["debug_utils/sierra-emu"]
members = ["debug_utils/sierra-emu", "debug_utils/sierra2casm-dbg"]
19 changes: 19 additions & 0 deletions debug_utils/sierra2casm-dbg/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
### Rust ###
# Generated by Cargo
# will have compiled files and executables
debug/
target/

# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock

# These are backup files generated by rustfmt
**/*.rs.bk

# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb


corelib/
run/
15 changes: 15 additions & 0 deletions debug_utils/sierra2casm-dbg/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "sierra2casm-dbg"
version = "0.1.0"
edition = "2021"

[dependencies]
bincode = { version = "2.0.0-rc.3", default-features = false }
clap = { version = "4.5.27", features = ["derive"] }
cairo-lang-casm = { version = "=2.12.0-dev.1", default-features = false, features = [
"serde",
] }
cairo-lang-utils = { version = "=2.12.0-dev.1", default-features = false }
cairo-vm = { version = "2.0.1", default-features = false }
serde_json = "1.0.138"
starknet-types-core = { version = "0.1.7", default-features = false }
37 changes: 37 additions & 0 deletions debug_utils/sierra2casm-dbg/examples/basic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use bincode::de::read::SliceReader;
use sierra2casm_dbg::{decode_instruction, GraphMappings, Memory, Trace, ValueId};
use std::{collections::HashMap, fs};

fn main() {
let memory = Memory::decode(SliceReader::new(&fs::read("memory-2.bin").unwrap()));
let trace = Trace::decode(SliceReader::new(&fs::read("trace-2.bin").unwrap()));

let mappings = GraphMappings::new(&memory, &trace, &HashMap::new());

// let value_idx = memory
// .iter()
// .copied()
// .enumerate()
// .filter_map(|(idx, val)| {
// (val == Some(Felt::from_str("9980669810").unwrap())).then_some(idx)
// })
// .collect::<Vec<_>>();

let value_idx = ValueId(93139);

println!("Memory offset: {value_idx:?}");
for id in &mappings[value_idx] {
println!(
"{id:?} => {} [{:?}]",
decode_instruction(&memory, trace[id.0].pc),
trace[id.0]
);
}

println!("[93139] = {}", memory[93139].unwrap());
println!("[93140] = {}", memory[93140].unwrap());
println!("[93142] = {}", memory[93142].unwrap());

// dbg!(memory[25386].unwrap().to_string());
// dbg!(value_idx);
}
18 changes: 18 additions & 0 deletions debug_utils/sierra2casm-dbg/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
pub use self::{
mappings::GraphMappings, memory::Memory, program::decode_instruction,
search::run_search_algorithm, trace::Trace,
};

mod mappings;
mod memory;
mod program;
pub mod search;
mod trace;

#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[repr(transparent)]
pub struct StepId(pub usize);

#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[repr(transparent)]
pub struct ValueId(pub usize);
162 changes: 162 additions & 0 deletions debug_utils/sierra2casm-dbg/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
use bincode::de::read::SliceReader;
use cairo_vm::serde::deserialize_program::HintParams;
use clap::Parser;
use serde_json::Value;
use sierra2casm_dbg::{
run_search_algorithm,
search::{DfsQueue, NodeId},
GraphMappings, Memory, Trace,
};
use starknet_types_core::felt::Felt;
use std::{collections::HashMap, fs, path::PathBuf, str::FromStr};

#[derive(Debug, Parser)]
struct CmdArgs {
#[clap(long)]
memory_path: PathBuf,
#[clap(long)]
trace_path: PathBuf,
#[clap(long)]
program_path: Option<PathBuf>,

#[clap(short, long, value_parser = parse_felt252)]
source_value: Felt,
#[clap(short, long, value_parser = parse_felt252)]
target_value: Felt,
}

fn parse_felt252(input: &str) -> Result<Felt, String> {
Felt::from_str(input).map_err(|e| e.to_string())
}

fn main() {
let args = CmdArgs::parse();

//
// Load data from disk.
//
println!("Loading memory and trace.");
let memory = Memory::decode(SliceReader::new(&fs::read(args.memory_path).unwrap()));
let trace = Trace::decode(SliceReader::new(&fs::read(args.trace_path).unwrap()));
println!(" {:?}", trace.first().unwrap());
println!(" {:?}", trace.last().unwrap());

let hints = match args.program_path {
None => HashMap::default(),
Some(program_path) => {
println!("Loading hints from provided program JSON.");
let mut program_json: Value =
serde_json::from_slice(&fs::read(program_path).unwrap()).unwrap();
let hints = program_json
.as_object_mut()
.unwrap()
.remove("hints")
.unwrap();

let hints: HashMap<usize, Vec<HintParams>> = serde_json::from_value(hints).unwrap();
hints
.into_iter()
.map(|(key, value)| {
//
(
key,
value
.into_iter()
.map(|hint_params| serde_json::from_str(&hint_params.code).unwrap())
.collect(),
)
})
.collect()
}
};

//
// Generate graph mappings.
//
println!("Generating graph mappings.");
let mappings = GraphMappings::new(&memory, &trace, &hints);

//
// Find initial and final values.
//
println!("Finding initial and final values within the data.");
let source_value = mappings
.value2step()
.keys()
.copied()
.filter(|x| memory[x.0] == Some(args.source_value))
.min()
.expect("Source value not found within accessed memory.");
let target_value = mappings
.value2step()
.keys()
.copied()
.filter(|x| memory[x.0] == Some(args.target_value))
.max()
.expect("Target value not found within accessed memory.");
println!(" Source value found at {}.", source_value.0);
println!(" Target value found at {}.", target_value.0);

println!();

//
// Find a path between the source and target nodes.
//
// Queue containers:
// - BfsQueue: Will find the shortest path using the BFS algorithm.
// - DfsQueue: Will find the left-most path using the DFS algorithm.
//
println!("Starting search algorithm.");
let mut iter =
run_search_algorithm::<DfsQueue<_>>(&memory, &mappings, source_value, target_value);
println!();
println!();

let mut num_solutions = 0;
while let Some(path) = iter.next() {
num_solutions += 1;

println!("Found solution at step {}.", iter.queue().current_step());
// println!("Connecting path (spans {} steps):", path.len() >> 1);
// for id in path {
// match id {
// NodeId::Step(offset) => {
// if let Some(hints) = hints.get(&offset.0) {
// for hint in hints {
// println!("; {}", hint.representing_string());
// }
// }
// println!("{}", decode_instruction(&memory, trace[offset.0].pc));
// println!(" {:?}", trace[offset.0]);
// }
// NodeId::Value(offset) => {
// println!(" [{}] = {}", offset.0, memory[offset.0].unwrap());
// println!();
// }
// }
// }
// println!();

let mut prev_value: Option<Felt> = None;
for id in path.into_iter().filter_map(|x| match x {
NodeId::Step(_) => None,
NodeId::Value(id) => Some(id),
}) {
let curr_value = memory[id.0].unwrap();
match prev_value {
Some(prev_value) if curr_value != prev_value => println!(
" [{}] = {curr_value} (Δ{})",
id.0,
curr_value.to_bigint() - prev_value.to_bigint()
),
None => println!(" [{}] = {curr_value}", id.0),
_ => {}
}
prev_value = Some(curr_value);
}
println!();
println!();
}

println!("Done! Found {num_solutions} solutions.");
}
Loading
Loading