Skip to content

Commit

Permalink
feat: rust implementation for some of the Cairo types (#18)
Browse files Browse the repository at this point in the history
  • Loading branch information
ctoyan committed Sep 9, 2024
1 parent 65b0bd7 commit 9bf055a
Show file tree
Hide file tree
Showing 6 changed files with 923 additions and 0 deletions.
103 changes: 103 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions ampd/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use tonic::transport::Channel;
use tracing::info;
use types::TMAddress;

use crate::asyncutil::task::{CancellableTask, TaskError, TaskGroup};
use crate::config::Config;

mod asyncutil;
Expand Down
1 change: 1 addition & 0 deletions ampd/src/starknet/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod types;
272 changes: 272 additions & 0 deletions ampd/src/starknet/types/array_span.rs
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]);
}
}
Loading

0 comments on commit 9bf055a

Please sign in to comment.