Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

KakarotSerde: implement get_identifier #68

Merged
merged 4 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions Cargo.lock

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

4 changes: 3 additions & 1 deletion crates/exex/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ workspace = true

[dependencies]
# Cairo-VM deps
cairo-vm = { git = "https://github.com/lambdaclass/cairo-vm.git", tag = "v1.0.1" }
cairo-vm = { git = "https://github.com/lambdaclass/cairo-vm.git", tag = "v1.0.1", features = [
"test_utils",
] }

kakarot-pool = { workspace = true }

Expand Down
1 change: 1 addition & 0 deletions crates/exex/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ pub mod execution;
pub mod exex;
pub mod hints;
pub mod model;
pub mod serde;
226 changes: 226 additions & 0 deletions crates/exex/src/serde.rs
tcoratger marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
use cairo_vm::{
serde::deserialize_program::Identifier,
vm::{runners::cairo_runner::CairoRunner, vm_memory::memory::Memory},
};
use thiserror::Error;

/// Represents errors that can occur during the serialization and deserialization processes between
/// Cairo VM programs and Rust representations.
#[derive(Debug, Error)]
pub enum KakarotSerdeError {
/// Error variant indicating that no identifier matching the specified name was found.
#[error("Expected one struct named '{struct_name}', found 0 matches. Expected type: {expected_type:?}")]
IdentifierNotFound {
/// The name of the struct that was not found.
struct_name: String,
/// The expected type of the struct (if applicable).
expected_type: Option<String>,
},

/// Error variant indicating that multiple identifiers matching the specified name were found.
#[error("Expected one struct named '{struct_name}', found {count} matches. Expected type: {expected_type:?}")]
MultipleIdentifiersFound {
/// The name of the struct for which multiple identifiers were found.
struct_name: String,
/// The expected type of the struct (if applicable).
expected_type: Option<String>,
/// The number of matching identifiers found.
count: usize,
},
}

/// A structure representing the Kakarot serialization and deserialization context for Cairo
/// programs.
///
/// This struct encapsulates the components required to serialize and deserialize
/// Kakarot programs, including:
/// - The Cairo runner responsible for executing the program
/// - The memory
#[allow(missing_debug_implementations)]
pub struct KakarotSerde {
/// The Cairo runner used to execute Kakarot programs.
///
/// This runner interacts with the Cairo virtual machine, providing the necessary
/// infrastructure for running and managing the execution of Cairo programs.
/// It is responsible for handling program execution flow, managing state, and
/// providing access to program identifiers.
runner: CairoRunner,

/// The memory used to store memory cells inside segments and relocation rules.
///
/// This is used to have direct access to the memory cells and their values.
#[allow(dead_code)]
memory: Memory,
}

impl KakarotSerde {
/// Retrieves a unique identifier from the Cairo program based on the specified struct name and
/// expected type.
///
/// This function searches for identifiers that match the provided struct name and type within
/// the Cairo program's identifier mappings. It returns an error if no identifiers or
/// multiple identifiers are found.
pub fn get_identifier(
&self,
struct_name: &str,
expected_type: Option<String>,
) -> Result<Identifier, KakarotSerdeError> {
// Retrieve identifiers from the program and filter them based on the struct name and
// expected type
let identifiers = self
.runner
.get_program()
.iter_identifiers()
.filter(|(key, value)| {
key.contains(struct_name) &&
key.split('.').last() == struct_name.split('.').last() &&
value.type_ == expected_type
})
.map(|(_, value)| value)
.collect::<Vec<_>>();

// Match on the number of found identifiers
match identifiers.len() {
// No identifiers found
0 => Err(KakarotSerdeError::IdentifierNotFound {
struct_name: struct_name.to_string(),
expected_type,
}),
// Exactly one identifier found, return it
1 => Ok(identifiers[0].clone()),
// More than one identifier found
count => Err(KakarotSerdeError::MultipleIdentifiersFound {
struct_name: struct_name.to_string(),
expected_type,
count,
}),
}
}
}

#[cfg(test)]
mod tests {
use super::*;
use cairo_vm::types::{layout_name::LayoutName, program::Program};

fn setup_kakarot_serde() -> KakarotSerde {
// Load the valid program content from a JSON file
let program_content = include_bytes!("../../../cairo/programs/os.json");

// Create a Program instance from the loaded bytes, specifying "main" as the entry point
let program = Program::from_bytes(program_content, Some("main")).unwrap();

// Initialize a CairoRunner with the created program and default parameters
let runner = CairoRunner::new(&program, LayoutName::plain, false, false).unwrap();

// Return an instance of KakarotSerde
KakarotSerde { runner, memory: Memory::new() }
}

#[test]
fn test_program_identifier_valid() {
// Setup the KakarotSerde instance
let kakarot_serde = setup_kakarot_serde();

// Check if the identifier "main" with expected type "function" is correctly retrieved
assert_eq!(
kakarot_serde.get_identifier("main", Some("function".to_string())).unwrap(),
Identifier {
pc: Some(3478),
type_: Some("function".to_string()),
value: None,
full_name: None,
members: None,
cairo_type: None
}
);
tcoratger marked this conversation as resolved.
Show resolved Hide resolved

// Check if the identifier "__temp0" with expected type "reference" is correctly retrieved
assert_eq!(
kakarot_serde.get_identifier("__temp0", Some("reference".to_string())).unwrap(),
Identifier {
pc: None,
type_: Some("reference".to_string()),
value: None,
full_name: Some("starkware.cairo.common.memcpy.memcpy.__temp0".to_string()),
members: None,
cairo_type: Some("felt".to_string())
}
);
tcoratger marked this conversation as resolved.
Show resolved Hide resolved
}

#[test]
fn test_non_existent_identifier() {
// Setup the KakarotSerde instance
let kakarot_serde = setup_kakarot_serde();

// Test for a non-existent identifier
let result =
kakarot_serde.get_identifier("non_existent_struct", Some("function".to_string()));

// Check if the error is valid and validate its parameters
if let Err(KakarotSerdeError::IdentifierNotFound { struct_name, expected_type }) = result {
assert_eq!(struct_name, "non_existent_struct");
assert_eq!(expected_type, Some("function".to_string()));
} else {
panic!("Expected KakarotSerdeError::IdentifierNotFound");
}
}

#[test]
fn test_incorrect_identifier_usage() {
// Setup the KakarotSerde instance
let kakarot_serde = setup_kakarot_serde();

// Test for an identifier used incorrectly (not the last segment of the full name)
let result = kakarot_serde.get_identifier("check_range", Some("struct".to_string()));

// Check if the error is valid and validate its parameters
if let Err(KakarotSerdeError::IdentifierNotFound { struct_name, expected_type }) = result {
assert_eq!(struct_name, "check_range");
assert_eq!(expected_type, Some("struct".to_string()));
} else {
panic!("Expected KakarotSerdeError::IdentifierNotFound");
}
}

#[test]
fn test_valid_identifier_incorrect_type() {
// Setup the KakarotSerde instance
let kakarot_serde = setup_kakarot_serde();

// Test for a valid identifier but with an incorrect type
let result = kakarot_serde.get_identifier("main", Some("struct".to_string()));

// Check if the error is valid and validate its parameters
if let Err(KakarotSerdeError::IdentifierNotFound { struct_name, expected_type }) = result {
assert_eq!(struct_name, "main");
assert_eq!(expected_type, Some("struct".to_string()));
} else {
panic!("Expected KakarotSerdeError::IdentifierNotFound");
}
}

#[test]
fn test_identifier_with_multiple_matches() {
// Setup the KakarotSerde instance
let kakarot_serde = setup_kakarot_serde();

// Test for an identifier with multiple matches
let result = kakarot_serde.get_identifier("ImplicitArgs", Some("struct".to_string()));

// Check if the error is valid and validate its parameters
if let Err(KakarotSerdeError::MultipleIdentifiersFound {
struct_name,
expected_type,
count,
}) = result
{
assert_eq!(struct_name, "ImplicitArgs");
assert_eq!(expected_type, Some("struct".to_string()));
assert_eq!(count, 63);
} else {
panic!("Expected KakarotSerdeError::MultipleIdentifiersFound");
}
}
}
Loading