forked from axelarnetwork/axelar-amplifier
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: rust implementation for some of the Cairo types (#18)
- Loading branch information
Showing
6 changed files
with
923 additions
and
0 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
pub mod types; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,272 @@ | ||
use starknet_core::types::{FieldElement, ValueOutOfRangeError}; | ||
use thiserror::Error; | ||
|
||
/// Represents Cairo's Array and Span types. | ||
/// Implements `TryFrom<Vec<FieldElement>>`, which is the way to create it. | ||
/// | ||
/// ## Example usage with the string "hello" | ||
/// | ||
/// ```rust | ||
/// use ampd::starknet::types::array_span::ArraySpan; | ||
/// use std::str::FromStr; | ||
/// use starknet_core::types::FieldElement; | ||
/// | ||
/// let data = vec![ | ||
/// FieldElement::from_str( | ||
/// "0x0000000000000000000000000000000000000000000000000000000000000005", | ||
/// ) | ||
/// .unwrap(), | ||
/// FieldElement::from_str( | ||
/// "0x0000000000000000000000000000000000000000000000000000000000000068", | ||
/// ) | ||
/// .unwrap(), | ||
/// FieldElement::from_str( | ||
/// "0x0000000000000000000000000000000000000000000000000000000000000065", | ||
/// ) | ||
/// .unwrap(), | ||
/// FieldElement::from_str( | ||
/// "0x000000000000000000000000000000000000000000000000000000000000006c", | ||
/// ) | ||
/// .unwrap(), | ||
/// FieldElement::from_str( | ||
/// "0x000000000000000000000000000000000000000000000000000000000000006c", | ||
/// ) | ||
/// .unwrap(), | ||
/// FieldElement::from_str( | ||
/// "0x000000000000000000000000000000000000000000000000000000000000006f", | ||
/// ) | ||
/// .unwrap(), | ||
/// ]; | ||
/// | ||
/// let array_span = ArraySpan::try_from(data).unwrap(); | ||
/// assert_eq!(array_span.bytes, vec![104, 101, 108, 108, 111]); | ||
/// assert_eq!(String::from_utf8(array_span.bytes).unwrap(), "hello"); | ||
/// ``` | ||
/// | ||
/// For more info: | ||
/// https://docs.starknet.io/documentation/architecture_and_concepts/Smart_Contracts/serialization_of_Cairo_types/#serialization_of_byte_arrays | ||
#[derive(Debug)] | ||
pub struct ArraySpan { | ||
pub bytes: Vec<u8>, | ||
} | ||
|
||
#[derive(Error, Debug)] | ||
pub enum ArraySpanError { | ||
#[error("Invalid array/span length")] | ||
InvalidLength, | ||
#[error("Failed to parse felt - {0}")] | ||
ParsingFelt(#[from] ValueOutOfRangeError), | ||
} | ||
|
||
impl TryFrom<Vec<FieldElement>> for ArraySpan { | ||
type Error = ArraySpanError; | ||
|
||
fn try_from(data: Vec<FieldElement>) -> Result<Self, Self::Error> { | ||
// First element is always the array length, which is a felt (so u8 is enough) | ||
let arr_length = u8::try_from(data[0])?; | ||
|
||
// -1 because we have to offset the first element (the length itself) | ||
let arr_length_usize = usize::from(arr_length); | ||
if arr_length_usize != data.len().wrapping_sub(1) { | ||
return Err(ArraySpanError::InvalidLength); | ||
} | ||
|
||
let bytes: Result<Vec<u8>, ArraySpanError> = data | ||
.get(1..) | ||
.ok_or(ArraySpanError::InvalidLength)? | ||
.iter() | ||
.copied() | ||
.map(|e| e.try_into().map_err(ArraySpanError::ParsingFelt)) | ||
.collect(); | ||
|
||
Ok(ArraySpan { bytes: bytes? }) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod array_span_tests { | ||
use std::str::FromStr; | ||
|
||
use starknet_core::types::FieldElement; | ||
|
||
use crate::starknet::types::array_span::ArraySpan; | ||
|
||
#[test] | ||
fn try_from_valid_zeros() { | ||
// the string "hello", but FieldElement is bigger than u8::max | ||
let data = vec![FieldElement::from_str( | ||
"0x0000000000000000000000000000000000000000000000000000000000000000", | ||
) | ||
.unwrap()]; | ||
|
||
let array_span = ArraySpan::try_from(data).unwrap(); | ||
assert_eq!(array_span.bytes, Vec::<u8>::new()); | ||
} | ||
|
||
#[test] | ||
fn try_from_failed_to_parse_element_to_u8() { | ||
// the string "hello", but FieldElement is bigger than u8::max | ||
let data = vec![ | ||
FieldElement::from_str( | ||
"0x0000000000000000000000000000000000000000000000000000000000000005", | ||
) | ||
.unwrap(), | ||
FieldElement::from_str( | ||
"0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", | ||
) | ||
.unwrap(), | ||
FieldElement::from_str( | ||
"0x0000000000000000000000000000000000000000000000000000000000000065", | ||
) | ||
.unwrap(), | ||
FieldElement::from_str( | ||
"0x000000000000000000000000000000000000000000000000000000000000006c", | ||
) | ||
.unwrap(), | ||
FieldElement::from_str( | ||
"0x000000000000000000000000000000000000000000000000000000000000006c", | ||
) | ||
.unwrap(), | ||
FieldElement::from_str( | ||
"0x000000000000000000000000000000000000000000000000000000000000006f", | ||
) | ||
.unwrap(), | ||
]; | ||
|
||
let array_span = ArraySpan::try_from(data); | ||
assert!(array_span.is_err()); | ||
} | ||
|
||
#[test] | ||
fn try_from_failed_to_parse_elements_length_to_u32() { | ||
// the string "hello", but element counte bigger than u32::max | ||
let data = vec![ | ||
FieldElement::from_str( | ||
"0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", | ||
) | ||
.unwrap(), | ||
FieldElement::from_str( | ||
"0x0000000000000000000000000000000000000000000000000000000000000068", | ||
) | ||
.unwrap(), | ||
FieldElement::from_str( | ||
"0x0000000000000000000000000000000000000000000000000000000000000065", | ||
) | ||
.unwrap(), | ||
FieldElement::from_str( | ||
"0x000000000000000000000000000000000000000000000000000000000000006c", | ||
) | ||
.unwrap(), | ||
FieldElement::from_str( | ||
"0x000000000000000000000000000000000000000000000000000000000000006c", | ||
) | ||
.unwrap(), | ||
FieldElement::from_str( | ||
"0x000000000000000000000000000000000000000000000000000000000000006f", | ||
) | ||
.unwrap(), | ||
]; | ||
|
||
let array_span = ArraySpan::try_from(data); | ||
assert!(array_span.is_err()); | ||
} | ||
|
||
#[test] | ||
fn try_from_invalid_number_of_elements() { | ||
// the string "hello", but with only 4 bytes | ||
let data = vec![ | ||
FieldElement::from_str( | ||
"0x0000000000000000000000000000000000000000000000000000000000000005", | ||
) | ||
.unwrap(), | ||
FieldElement::from_str( | ||
"0x0000000000000000000000000000000000000000000000000000000000000068", | ||
) | ||
.unwrap(), | ||
FieldElement::from_str( | ||
"0x0000000000000000000000000000000000000000000000000000000000000065", | ||
) | ||
.unwrap(), | ||
FieldElement::from_str( | ||
"0x000000000000000000000000000000000000000000000000000000000000006c", | ||
) | ||
.unwrap(), | ||
FieldElement::from_str( | ||
"0x000000000000000000000000000000000000000000000000000000000000006c", | ||
) | ||
.unwrap(), | ||
]; | ||
|
||
let array_span = ArraySpan::try_from(data); | ||
assert!(array_span.is_err()); | ||
} | ||
|
||
#[test] | ||
fn try_from_invalid_declared_length() { | ||
// the string "hello", with correct number of bytes, but only 4 declared, | ||
// instead of 5 | ||
let data = vec![ | ||
FieldElement::from_str( | ||
"0x0000000000000000000000000000000000000000000000000000000000000004", | ||
) | ||
.unwrap(), | ||
FieldElement::from_str( | ||
"0x0000000000000000000000000000000000000000000000000000000000000068", | ||
) | ||
.unwrap(), | ||
FieldElement::from_str( | ||
"0x0000000000000000000000000000000000000000000000000000000000000065", | ||
) | ||
.unwrap(), | ||
FieldElement::from_str( | ||
"0x000000000000000000000000000000000000000000000000000000000000006c", | ||
) | ||
.unwrap(), | ||
FieldElement::from_str( | ||
"0x000000000000000000000000000000000000000000000000000000000000006c", | ||
) | ||
.unwrap(), | ||
FieldElement::from_str( | ||
"0x000000000000000000000000000000000000000000000000000000000000006f", | ||
) | ||
.unwrap(), | ||
]; | ||
|
||
let array_span = ArraySpan::try_from(data); | ||
assert!(array_span.is_err()); | ||
} | ||
|
||
#[test] | ||
fn try_from_valid() { | ||
// the string "hello" | ||
let data = vec![ | ||
FieldElement::from_str( | ||
"0x0000000000000000000000000000000000000000000000000000000000000005", | ||
) | ||
.unwrap(), | ||
FieldElement::from_str( | ||
"0x0000000000000000000000000000000000000000000000000000000000000068", | ||
) | ||
.unwrap(), | ||
FieldElement::from_str( | ||
"0x0000000000000000000000000000000000000000000000000000000000000065", | ||
) | ||
.unwrap(), | ||
FieldElement::from_str( | ||
"0x000000000000000000000000000000000000000000000000000000000000006c", | ||
) | ||
.unwrap(), | ||
FieldElement::from_str( | ||
"0x000000000000000000000000000000000000000000000000000000000000006c", | ||
) | ||
.unwrap(), | ||
FieldElement::from_str( | ||
"0x000000000000000000000000000000000000000000000000000000000000006f", | ||
) | ||
.unwrap(), | ||
]; | ||
|
||
let array_span = ArraySpan::try_from(data).unwrap(); | ||
assert_eq!(array_span.bytes, vec![104, 101, 108, 108, 111]); | ||
} | ||
} |
Oops, something went wrong.