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_uint256 #77

Merged
merged 4 commits into from
Oct 24, 2024
Merged
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
182 changes: 180 additions & 2 deletions crates/exex/src/serde.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use crate::model::U128_BYTES_SIZE;
use alloy_primitives::U256;
use cairo_vm::{
serde::deserialize_program::Identifier,
types::{
Expand Down Expand Up @@ -41,6 +43,13 @@ pub enum KakarotSerdeError {
/// Error variant indicating a memory error in CairoVM operations
#[error(transparent)]
CairoVmMemory(#[from] MemoryError),

/// Error variant indicating that a required field was not found during serialization.
#[error("Missing required field '{field}' in serialization process.")]
MissingField {
/// The name of the missing field.
field: String,
},
}

/// A structure representing the Kakarot serialization and deserialization context for Cairo
Expand Down Expand Up @@ -146,12 +155,50 @@ impl KakarotSerde {

Ok(output)
}

/// Serializes a Cairo VM `Uint256` structure (with `low` and `high` fields) into a Rust
/// [`U256`] value.
///
/// This function retrieves the `Uint256` struct from memory, extracts its `low` and `high`
/// values, converts them into a big-endian byte representation, and combines them into a
/// single [`U256`].
pub fn serialize_uint256(&self, ptr: Relocatable) -> Result<U256, KakarotSerdeError> {
// Fetches the `Uint256` structure from memory.
let raw = self.serialize_pointers("Uint256", ptr)?;

// Retrieves the `low` field from the deserialized struct, ensuring it's a valid integer.
let low = match raw.get("low") {
Some(Some(MaybeRelocatable::Int(value))) => value.clone(),
_ => return Err(KakarotSerdeError::MissingField { field: "low".to_string() }),
};

// Retrieves the `high` field from the deserialized struct, ensuring it's a valid integer.
let high = match raw.get("high") {
Some(Some(MaybeRelocatable::Int(value))) => value.clone(),
_ => return Err(KakarotSerdeError::MissingField { field: "high".to_string() }),
};

// Converts the `low` and `high` values into big-endian byte arrays.
let high_bytes = high.to_bytes_be();
let low_bytes = low.to_bytes_be();

// Concatenates the last 16 bytes (128 bits) of the `high` and `low` byte arrays.
//
// This forms a 256-bit number, where:
// - The `high` bytes make up the most significant 128 bits
// - The `low` bytes make up the least significant 128 bits.
let bytes = [&high_bytes[U128_BYTES_SIZE..], &low_bytes[U128_BYTES_SIZE..]].concat();

// Creates a `U256` value from the concatenated big-endian byte array.
Ok(U256::from_be_slice(&bytes))
}
}

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

fn setup_kakarot_serde() -> KakarotSerde {
// Load the valid program content from a JSON file
Expand Down Expand Up @@ -321,7 +368,7 @@ mod tests {
let range_check_ptr = kakarot_serde.runner.vm.add_memory_segment();
let bitwise_ptr = kakarot_serde.runner.vm.add_memory_segment();

// Insert relocatable values in memory
// Insert values in memory
let base = kakarot_serde
.runner
.vm
Expand Down Expand Up @@ -363,7 +410,7 @@ mod tests {
let range_check_ptr = Felt252::ZERO;
let bitwise_ptr = Felt252::from(55);

// Insert relocatable values in memory
// Insert values in memory
let base = kakarot_serde
.runner
.vm
Expand Down Expand Up @@ -392,4 +439,135 @@ mod tests {
])
);
}

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

// U256 to be serialized
let x = U256::ZERO;

// Setup with the high and low parts of the U256
let low =
Felt252::from_bytes_be_slice(&x.to_be_bytes::<{ U256::BYTES }>()[U128_BYTES_SIZE..]);
let high =
Felt252::from_bytes_be_slice(&x.to_be_bytes::<{ U256::BYTES }>()[0..U128_BYTES_SIZE]);

// Insert values in memory
let base = kakarot_serde
.runner
.vm
.gen_arg(&vec![MaybeRelocatable::Int(low), MaybeRelocatable::Int(high)])
.unwrap()
.get_relocatable()
.unwrap();

// Serialize the Uint256 struct using the new memory segment.
let result = kakarot_serde.serialize_uint256(base).expect("failed to serialize pointers");

// Assert that the result is 0.
assert_eq!(result, U256::ZERO);
}

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

// U256 to be serialized
let x =
U256::from_str("0x52f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb")
.unwrap();

// Setup with the high and low parts of the U256
let low =
Felt252::from_bytes_be_slice(&x.to_be_bytes::<{ U256::BYTES }>()[U128_BYTES_SIZE..]);
let high =
Felt252::from_bytes_be_slice(&x.to_be_bytes::<{ U256::BYTES }>()[0..U128_BYTES_SIZE]);

// Insert values in memory
let base = kakarot_serde
.runner
.vm
.gen_arg(&vec![MaybeRelocatable::Int(low), MaybeRelocatable::Int(high)])
.unwrap()
.get_relocatable()
.unwrap();

// Serialize the Uint256 struct using the new memory segment.
let result = kakarot_serde.serialize_uint256(base).expect("failed to serialize pointers");

// Assert that the result matches the expected U256 value.
assert_eq!(result, x);
}

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

// U256 to be serialized
let x = U256::MAX;

// Setup with the high and low parts of the U256
let low =
Felt252::from_bytes_be_slice(&x.to_be_bytes::<{ U256::BYTES }>()[U128_BYTES_SIZE..]);
// High is not an Int to trigger the error
let high = Relocatable { segment_index: 10, offset: 11 };

// Insert values in memory
let base = kakarot_serde
.runner
.vm
.gen_arg(&vec![MaybeRelocatable::Int(low), MaybeRelocatable::RelocatableValue(high)])
.unwrap()
.get_relocatable()
.unwrap();

// Try to serialize the Uint256 struct using the new memory segment.
let result = kakarot_serde.serialize_uint256(base);

// Assert that the result is an error with the expected missing field.
match result {
Err(KakarotSerdeError::MissingField { field }) => {
assert_eq!(field, "high");
}
_ => panic!("Expected a missing field error, but got: {:?}", result),
}
}

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

// U256 to be serialized
let x = U256::MAX;

// Low is not an Int to trigger the error
let low = Relocatable { segment_index: 10, offset: 11 };
let high =
Felt252::from_bytes_be_slice(&x.to_be_bytes::<{ U256::BYTES }>()[0..U128_BYTES_SIZE]);

// Insert values in memory
let base = kakarot_serde
.runner
.vm
.gen_arg(&vec![MaybeRelocatable::RelocatableValue(low), MaybeRelocatable::Int(high)])
.unwrap()
.get_relocatable()
.unwrap();

// Try to serialize the Uint256 struct using the new memory segment.
let result = kakarot_serde.serialize_uint256(base);

// Assert that the result is an error with the expected missing field.
match result {
Err(KakarotSerdeError::MissingField { field }) => {
assert_eq!(field, "low");
}
_ => panic!("Expected a missing field error, but got: {:?}", result),
}
}
}
Loading