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

serde: implement serialize_pointers #75

Merged
merged 7 commits into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from 5 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
162 changes: 149 additions & 13 deletions crates/exex/src/serde.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use cairo_vm::{
serde::deserialize_program::Identifier,
vm::{runners::cairo_runner::CairoRunner, vm_memory::memory::Memory},
types::{errors::math_errors::MathError, relocatable::Relocatable},
vm::{errors::memory_errors::MemoryError, runners::cairo_runner::CairoRunner},
};
use std::collections::HashMap;
use thiserror::Error;

/// Represents errors that can occur during the serialization and deserialization processes between
Expand All @@ -27,6 +29,14 @@ pub enum KakarotSerdeError {
/// The number of matching identifiers found.
count: usize,
},

/// Error variant indicating a Math error in CairoVM operations
#[error(transparent)]
CairoVmMath(#[from] MathError),

/// Error variant indicating a memory error in CairoVM operations
#[error(transparent)]
CairoVmMemory(#[from] MemoryError),
}

/// A structure representing the Kakarot serialization and deserialization context for Cairo
Expand All @@ -35,7 +45,6 @@ pub enum KakarotSerdeError {
/// 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.
Expand All @@ -45,12 +54,6 @@ pub struct KakarotSerde {
/// 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 {
Expand Down Expand Up @@ -96,6 +99,44 @@ impl KakarotSerde {
}),
}
}

/// Serializes a pointer to a Hashmap by resolving its members from memory.
///
/// We provide:
/// - The name of the struct whose pointer is being serialized.
/// - The memory location (pointer) of the struct.
///
/// We expect:
/// - A map of member names to their corresponding values (or `None` if the pointer is null).
pub fn serialize_pointers(
&self,
struct_name: &str,
ptr: Relocatable,
) -> Result<HashMap<String, Option<Relocatable>>, KakarotSerdeError> {
// Fetch the struct definition (identifier) by name.
let identifier = self.get_identifier(struct_name, Some("struct".to_string()))?;

// Initialize the output map.
let mut output = HashMap::new();

// If the struct has members, iterate over them to resolve their values from memory.
if let Some(members) = identifier.members {
for (name, member) in members {
// Get the member's pointer in memory by adding its offset to the struct pointer.
let mut member_ptr = Some(self.runner.vm.get_relocatable((ptr + member.offset)?)?);

// If the member is a pointer and its value is 0, set it to `None`.
if member_ptr == Some(Relocatable::default()) && member.cairo_type.ends_with('*') {
member_ptr = None;
}

// Insert the resolved member pointer into the output map.
output.insert(name, member_ptr);
}
}

Ok(output)
}
}

#[cfg(test)]
Expand All @@ -105,7 +146,7 @@ mod tests {

fn setup_kakarot_serde() -> KakarotSerde {
// Load the valid program content from a JSON file
let program_content = include_bytes!("../../../cairo/programs/os.json");
let program_content = include_bytes!("../testdata/keccak_add_uint256.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();
Expand All @@ -114,7 +155,7 @@ mod tests {
let runner = CairoRunner::new(&program, LayoutName::plain, false, false).unwrap();

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

#[test]
Expand All @@ -126,7 +167,7 @@ mod tests {
assert_eq!(
kakarot_serde.get_identifier("main", Some("function".to_string())).unwrap(),
Identifier {
pc: Some(3478),
pc: Some(96),
type_: Some("function".to_string()),
value: None,
full_name: None,
Expand All @@ -142,7 +183,9 @@ mod tests {
pc: None,
type_: Some("reference".to_string()),
value: None,
full_name: Some("starkware.cairo.common.memcpy.memcpy.__temp0".to_string()),
full_name: Some(
"starkware.cairo.common.uint256.word_reverse_endian.__temp0".to_string()
),
members: None,
cairo_type: Some("felt".to_string())
}
Expand Down Expand Up @@ -218,9 +261,102 @@ mod tests {
{
assert_eq!(struct_name, "ImplicitArgs");
assert_eq!(expected_type, Some("struct".to_string()));
assert_eq!(count, 63);
assert_eq!(count, 6);
} else {
panic!("Expected KakarotSerdeError::MultipleIdentifiersFound");
}
}

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

// Add a new memory segment to the virtual machine (VM).
let base = kakarot_serde.runner.vm.add_memory_segment();

// Attempt to serialize pointer with "main", expecting an IdentifierNotFound error.
let result = kakarot_serde.serialize_pointers("main", base);

// Assert that the result is an error with the expected struct name and type.
match result {
Err(KakarotSerdeError::IdentifierNotFound { struct_name, expected_type }) => {
assert_eq!(struct_name, "main".to_string());
assert_eq!(expected_type, Some("struct".to_string()));
}
_ => panic!("Expected KakarotSerdeError::IdentifierNotFound, but got: {:?}", result),
}
}

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

// Insert relocatable values in memory
let base = kakarot_serde
.runner
.vm
.gen_arg(&vec![
Relocatable { segment_index: 0, offset: 0 },
Relocatable { segment_index: 10, offset: 11 },
Relocatable { segment_index: 10, offset: 11 },
])
.unwrap()
.get_relocatable()
.unwrap();

// Serialize the pointers of the "ImplicitArgs" struct using the new memory segment.
let result = kakarot_serde
.serialize_pointers("main.ImplicitArgs", base)
.expect("failed to serialize pointers");

// Assert that the result matches the expected serialized struct members.
assert_eq!(
result,
HashMap::from_iter([
("bitwise_ptr".to_string(), Some(Relocatable { segment_index: 10, offset: 11 })),
("output_ptr".to_string(), None),
(
"range_check_ptr".to_string(),
Some(Relocatable { segment_index: 10, offset: 11 })
),
tcoratger marked this conversation as resolved.
Show resolved Hide resolved
])
);
}

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

// Insert relocatable values in memory
let base = kakarot_serde
.runner
.vm
.gen_arg(&vec![
Relocatable { segment_index: 10, offset: 11 },
Relocatable { segment_index: 0, offset: 0 },
Relocatable { segment_index: 10, offset: 11 },
])
.unwrap()
.get_relocatable()
.unwrap();

// Serialize the pointers of the "ImplicitArgs" struct using the new memory segment.
let result = kakarot_serde
.serialize_pointers("main.ImplicitArgs", base)
.expect("failed to serialize pointers");

// Assert that the result matches the expected serialized struct members.
assert_eq!(
result,
HashMap::from_iter([
("bitwise_ptr".to_string(), Some(Relocatable { segment_index: 10, offset: 11 })),
// Not a pointer so that we shouldn't have a `None`
("range_check_ptr".to_string(), Some(Relocatable { segment_index: 0, offset: 0 })),
("output_ptr".to_string(), Some(Relocatable { segment_index: 10, offset: 11 })),
tcoratger marked this conversation as resolved.
Show resolved Hide resolved
])
);
}
}
30 changes: 30 additions & 0 deletions crates/exex/testdata/keccak_add_uint256.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
%builtins output range_check bitwise

from starkware.cairo.common.keccak_utils.keccak_utils import keccak_add_uint256
from starkware.cairo.common.uint256 import Uint256
from starkware.cairo.common.cairo_builtins import BitwiseBuiltin
from starkware.cairo.common.alloc import alloc
from starkware.cairo.common.serialize import serialize_word

func main{output_ptr: felt*, range_check_ptr, bitwise_ptr: BitwiseBuiltin*}() {
alloc_locals;

let (inputs) = alloc();
let inputs_start = inputs;

let num = Uint256(34623634663146736, 598249824422424658356);

keccak_add_uint256{inputs=inputs_start}(num=num, bigend=0);

assert inputs[0] = 34623634663146736;
assert inputs[1] = 0;
assert inputs[2] = 7954014063719006644;
assert inputs[3] = 32;

serialize_word(inputs[0]);
serialize_word(inputs[1]);
serialize_word(inputs[2]);
serialize_word(inputs[3]);

return ();
}
Loading
Loading