Skip to content

Commit 83638af

Browse files
committed
Add sierra2casm-dbg debug utility (#1196)
* Add sierra2casm-dbg * Sync cairo version between sierra2casm-dbg and root * Bump Cairo VM version in sierra2casm-dbg * Fix compilation and linter errors * Bump Cairo in sierra2casm-dbg * Remove unused serde dep in sierra2casm-dbg * Remove Rust toolchain in sierra2casm-dbg * Add sierra2casm-dbg CI job (#1201)
1 parent 531071c commit 83638af

File tree

13 files changed

+1313
-1
lines changed

13 files changed

+1313
-1
lines changed

.github/workflows/ci.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,3 +363,18 @@ jobs:
363363
working-directory: ./debug_utils/sierra-emu
364364
run: cargo test test_corelib -- --nocapture
365365
continue-on-error: true # ignore result for now. When sierra-emu is fully implemented this should be removed
366+
367+
build-sierra2casm-dbg:
368+
name: Build sierra2casm-dbg
369+
runs-on: ubuntu-latest
370+
371+
steps:
372+
- uses: actions/checkout@v4
373+
- name: Retreive cached dependecies
374+
uses: Swatinem/rust-cache@v2
375+
- name: Build
376+
working-directory: ./debug_utils/sierra2casm-dbg
377+
run: cargo build --all-features --verbose
378+
- name: Run tests
379+
working-directory: ./debug_utils/sierra2casm-dbg
380+
run: cargo test

Cargo.lock

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,4 +177,4 @@ name = "libfuncs"
177177
harness = false
178178

179179
[workspace]
180-
members = ["debug_utils/sierra-emu"]
180+
members = ["debug_utils/sierra-emu", "debug_utils/sierra2casm-dbg"]
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
### Rust ###
2+
# Generated by Cargo
3+
# will have compiled files and executables
4+
debug/
5+
target/
6+
7+
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
8+
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
9+
Cargo.lock
10+
11+
# These are backup files generated by rustfmt
12+
**/*.rs.bk
13+
14+
# MSVC Windows builds of rustc generate these, which store debugging information
15+
*.pdb
16+
17+
18+
corelib/
19+
run/
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[package]
2+
name = "sierra2casm-dbg"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
bincode = { version = "2.0.0-rc.3", default-features = false }
8+
clap = { version = "4.5.27", features = ["derive"] }
9+
cairo-lang-casm = { version = "=2.12.0-dev.1", default-features = false, features = [
10+
"serde",
11+
] }
12+
cairo-lang-utils = { version = "=2.12.0-dev.1", default-features = false }
13+
cairo-vm = { version = "2.0.1", default-features = false }
14+
serde_json = "1.0.138"
15+
starknet-types-core = { version = "0.1.7", default-features = false }
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
use bincode::de::read::SliceReader;
2+
use sierra2casm_dbg::{decode_instruction, GraphMappings, Memory, Trace, ValueId};
3+
use std::{collections::HashMap, fs};
4+
5+
fn main() {
6+
let memory = Memory::decode(SliceReader::new(&fs::read("memory-2.bin").unwrap()));
7+
let trace = Trace::decode(SliceReader::new(&fs::read("trace-2.bin").unwrap()));
8+
9+
let mappings = GraphMappings::new(&memory, &trace, &HashMap::new());
10+
11+
// let value_idx = memory
12+
// .iter()
13+
// .copied()
14+
// .enumerate()
15+
// .filter_map(|(idx, val)| {
16+
// (val == Some(Felt::from_str("9980669810").unwrap())).then_some(idx)
17+
// })
18+
// .collect::<Vec<_>>();
19+
20+
let value_idx = ValueId(93139);
21+
22+
println!("Memory offset: {value_idx:?}");
23+
for id in &mappings[value_idx] {
24+
println!(
25+
"{id:?} => {} [{:?}]",
26+
decode_instruction(&memory, trace[id.0].pc),
27+
trace[id.0]
28+
);
29+
}
30+
31+
println!("[93139] = {}", memory[93139].unwrap());
32+
println!("[93140] = {}", memory[93140].unwrap());
33+
println!("[93142] = {}", memory[93142].unwrap());
34+
35+
// dbg!(memory[25386].unwrap().to_string());
36+
// dbg!(value_idx);
37+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
pub use self::{
2+
mappings::GraphMappings, memory::Memory, program::decode_instruction,
3+
search::run_search_algorithm, trace::Trace,
4+
};
5+
6+
mod mappings;
7+
mod memory;
8+
mod program;
9+
pub mod search;
10+
mod trace;
11+
12+
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
13+
#[repr(transparent)]
14+
pub struct StepId(pub usize);
15+
16+
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
17+
#[repr(transparent)]
18+
pub struct ValueId(pub usize);
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
use bincode::de::read::SliceReader;
2+
use cairo_vm::serde::deserialize_program::HintParams;
3+
use clap::Parser;
4+
use serde_json::Value;
5+
use sierra2casm_dbg::{
6+
run_search_algorithm,
7+
search::{DfsQueue, NodeId},
8+
GraphMappings, Memory, Trace,
9+
};
10+
use starknet_types_core::felt::Felt;
11+
use std::{collections::HashMap, fs, path::PathBuf, str::FromStr};
12+
13+
#[derive(Debug, Parser)]
14+
struct CmdArgs {
15+
#[clap(long)]
16+
memory_path: PathBuf,
17+
#[clap(long)]
18+
trace_path: PathBuf,
19+
#[clap(long)]
20+
program_path: Option<PathBuf>,
21+
22+
#[clap(short, long, value_parser = parse_felt252)]
23+
source_value: Felt,
24+
#[clap(short, long, value_parser = parse_felt252)]
25+
target_value: Felt,
26+
}
27+
28+
fn parse_felt252(input: &str) -> Result<Felt, String> {
29+
Felt::from_str(input).map_err(|e| e.to_string())
30+
}
31+
32+
fn main() {
33+
let args = CmdArgs::parse();
34+
35+
//
36+
// Load data from disk.
37+
//
38+
println!("Loading memory and trace.");
39+
let memory = Memory::decode(SliceReader::new(&fs::read(args.memory_path).unwrap()));
40+
let trace = Trace::decode(SliceReader::new(&fs::read(args.trace_path).unwrap()));
41+
println!(" {:?}", trace.first().unwrap());
42+
println!(" {:?}", trace.last().unwrap());
43+
44+
let hints = match args.program_path {
45+
None => HashMap::default(),
46+
Some(program_path) => {
47+
println!("Loading hints from provided program JSON.");
48+
let mut program_json: Value =
49+
serde_json::from_slice(&fs::read(program_path).unwrap()).unwrap();
50+
let hints = program_json
51+
.as_object_mut()
52+
.unwrap()
53+
.remove("hints")
54+
.unwrap();
55+
56+
let hints: HashMap<usize, Vec<HintParams>> = serde_json::from_value(hints).unwrap();
57+
hints
58+
.into_iter()
59+
.map(|(key, value)| {
60+
//
61+
(
62+
key,
63+
value
64+
.into_iter()
65+
.map(|hint_params| serde_json::from_str(&hint_params.code).unwrap())
66+
.collect(),
67+
)
68+
})
69+
.collect()
70+
}
71+
};
72+
73+
//
74+
// Generate graph mappings.
75+
//
76+
println!("Generating graph mappings.");
77+
let mappings = GraphMappings::new(&memory, &trace, &hints);
78+
79+
//
80+
// Find initial and final values.
81+
//
82+
println!("Finding initial and final values within the data.");
83+
let source_value = mappings
84+
.value2step()
85+
.keys()
86+
.copied()
87+
.filter(|x| memory[x.0] == Some(args.source_value))
88+
.min()
89+
.expect("Source value not found within accessed memory.");
90+
let target_value = mappings
91+
.value2step()
92+
.keys()
93+
.copied()
94+
.filter(|x| memory[x.0] == Some(args.target_value))
95+
.max()
96+
.expect("Target value not found within accessed memory.");
97+
println!(" Source value found at {}.", source_value.0);
98+
println!(" Target value found at {}.", target_value.0);
99+
100+
println!();
101+
102+
//
103+
// Find a path between the source and target nodes.
104+
//
105+
// Queue containers:
106+
// - BfsQueue: Will find the shortest path using the BFS algorithm.
107+
// - DfsQueue: Will find the left-most path using the DFS algorithm.
108+
//
109+
println!("Starting search algorithm.");
110+
let mut iter =
111+
run_search_algorithm::<DfsQueue<_>>(&memory, &mappings, source_value, target_value);
112+
println!();
113+
println!();
114+
115+
let mut num_solutions = 0;
116+
while let Some(path) = iter.next() {
117+
num_solutions += 1;
118+
119+
println!("Found solution at step {}.", iter.queue().current_step());
120+
// println!("Connecting path (spans {} steps):", path.len() >> 1);
121+
// for id in path {
122+
// match id {
123+
// NodeId::Step(offset) => {
124+
// if let Some(hints) = hints.get(&offset.0) {
125+
// for hint in hints {
126+
// println!("; {}", hint.representing_string());
127+
// }
128+
// }
129+
// println!("{}", decode_instruction(&memory, trace[offset.0].pc));
130+
// println!(" {:?}", trace[offset.0]);
131+
// }
132+
// NodeId::Value(offset) => {
133+
// println!(" [{}] = {}", offset.0, memory[offset.0].unwrap());
134+
// println!();
135+
// }
136+
// }
137+
// }
138+
// println!();
139+
140+
let mut prev_value: Option<Felt> = None;
141+
for id in path.into_iter().filter_map(|x| match x {
142+
NodeId::Step(_) => None,
143+
NodeId::Value(id) => Some(id),
144+
}) {
145+
let curr_value = memory[id.0].unwrap();
146+
match prev_value {
147+
Some(prev_value) if curr_value != prev_value => println!(
148+
" [{}] = {curr_value} (Δ{})",
149+
id.0,
150+
curr_value.to_bigint() - prev_value.to_bigint()
151+
),
152+
None => println!(" [{}] = {curr_value}", id.0),
153+
_ => {}
154+
}
155+
prev_value = Some(curr_value);
156+
}
157+
println!();
158+
println!();
159+
}
160+
161+
println!("Done! Found {num_solutions} solutions.");
162+
}

0 commit comments

Comments
 (0)