Skip to content

Commit

Permalink
Feature: Add corelib types (#30)
Browse files Browse the repository at this point in the history
* Implement partial Ord for EthAddress

* add dummy non zero

* remove warning

* generate rust make task

* add-generated

* u256 tests

* to_bytes

* don't generate custom u256

* from felt pair outline

* trait From, from_bytes_le/be and tests

* code cleanup

* change the From implementation to TryFrom and adjust encoding

* Implement common traits

* add NonZero constraints

* remove comment

* update readme

* add const sizes

* update ordering

* add inner_mut

* simplify generics

* Revert "simplify generics"

This reverts commit 2a75052.

* fix: fix tests

* fix: fmt and clippy

---------

Co-authored-by: Mateusz <[email protected]>
Co-authored-by: glihm <[email protected]>
  • Loading branch information
3 people authored May 2, 2024
1 parent bed53f7 commit 60da94a
Show file tree
Hide file tree
Showing 16 changed files with 430 additions and 8 deletions.
1 change: 1 addition & 0 deletions contracts/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/target
/abi
/generated
5 changes: 5 additions & 0 deletions contracts/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ generate_artifacts:
scarb build
mkdir -p ${artifacts}

jq .abi ${scarb_build}basic${sierra} > ${artifacts}basic.abi.json
jq .abi ${scarb_build}simple_events${sierra} > ${artifacts}events.abi.json
jq .abi ${scarb_build}simple_get_set${sierra} > ${artifacts}simple_get_set.abi.json
jq .abi ${scarb_build}simple_types${sierra} > ${artifacts}simple_types.abi.json
Expand All @@ -19,6 +20,10 @@ generate_artifacts:
jq .abi ${scarb_build}structs${sierra} > ${artifacts}structs.abi.json
jq .abi ${scarb_build}byte_array${sierra} > ${artifacts}byte_array.abi.json

generate_rust:
scarb build
mkdir -p generated
cargo run --all-features -- --artifacts-path target/dev --output-dir generated --rust

setup: setup_simple_get_set

Expand Down
6 changes: 3 additions & 3 deletions contracts/src/basic.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@
mod basic {
#[storage]
struct Storage {
v1: felt252,
v1: NonZero<felt252>,
v2: u256,
v3: felt252,
}

#[external(v0)]
fn set_storage(ref self: ContractState, v1: felt252, v2: u256) {
fn set_storage(ref self: ContractState, v1: NonZero<felt252>, v2: u256) {
self.v1.write(v1);
self.v2.write(v2);
}

#[external(v0)]
fn read_storage_tuple(self: @ContractState) -> (felt252, u256) {
fn read_storage_tuple(self: @ContractState) -> (NonZero<felt252>, u256) {
(self.v1.read(), self.v2.read())
}
}
1 change: 1 addition & 0 deletions contracts/src/lib.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ mod abicov {
}

mod simple_get_set;
mod basic;
11 changes: 10 additions & 1 deletion crates/cairo-serde/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,14 @@ By implementing this trait, the Rust type becomes (de)serializable from / into a
The types considered built-in by Cairo Serde are the following:

```rust
pub const CAIRO_BASIC_STRUCTS: [&str; 4] = ["Span", "ClassHash", "ContractAddress", "EthAddress"];
pub const CAIRO_BASIC_STRUCTS: [&str; 6] = [
"Span",
"ClassHash",
"ContractAddress",
"EthAddress",
"NonZero",
"U256",
];

pub const CAIRO_BASIC_ENUMS: [&str; 3] = ["Option", "Result", "bool"];
```
Expand All @@ -29,6 +36,8 @@ Cairo Serde provides serialization support for the following types:
- `ClassHash` -> Custom type in this crate `ClassHash`.
- `Array/Span` -> `Vec`.
- `Tuple` -> native tuples + the unit `()` type.
- `NonZero` -> Custom type in this crate `NonZero`.
- `u256` -> Custom type in this crate `U256`.

## `CairoSerde` trait

Expand Down
2 changes: 2 additions & 0 deletions crates/cairo-serde/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ pub enum Error {
Provider(#[from] ProviderError),
#[error("Bytes31 out of range.")]
Bytes31OutOfRange,
#[error("NonZero that is zero")]
ZeroedNonZero,
}

impl CairoSerde for Error {
Expand Down
11 changes: 10 additions & 1 deletion crates/cairo-serde/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,23 @@ pub mod call;
pub mod types;
pub use types::array_legacy::*;
pub use types::byte_array::*;
pub use types::non_zero::*;
pub use types::starknet::*;
pub use types::u256::*;
pub use types::*;

use ::starknet::core::types::FieldElement;

/// Basic cairo structs that are already implemented inside
/// this crate and hence skipped during ABI generation.
pub const CAIRO_BASIC_STRUCTS: [&str; 4] = ["Span", "ClassHash", "ContractAddress", "EthAddress"];
pub const CAIRO_BASIC_STRUCTS: [&str; 6] = [
"Span",
"ClassHash",
"ContractAddress",
"EthAddress",
"NonZero",
"U256",
];

/// Same as `CAIRO_BASIC_STRUCTS`, but for enums.
pub const CAIRO_BASIC_ENUMS: [&str; 3] = ["Option", "Result", "bool"];
Expand Down
1 change: 1 addition & 0 deletions crates/cairo-serde/src/types/integers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ implement_trait_for_signed!(i16);
implement_trait_for_signed!(i32);
implement_trait_for_signed!(i64);
implement_trait_for_signed!(i128);
implement_trait_for_signed!(isize);

#[cfg(test)]
mod tests {
Expand Down
2 changes: 2 additions & 0 deletions crates/cairo-serde/src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ pub mod boolean;
pub mod byte_array;
pub mod felt;
pub mod integers;
pub mod non_zero;
pub mod option;
pub mod result;
pub mod starknet;
pub mod tuple;
pub mod u256;

#[cfg(test)]
mod tests {
Expand Down
143 changes: 143 additions & 0 deletions crates/cairo-serde/src/types/non_zero.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
//! CairoSerde implementation for NonZero.
//!
//! NonZero serializes with zero ( hehe :) ) overhead as the inner value
//!
//! https://github.com/starkware-libs/cairo/blob/main/corelib/src/zeroable.cairo#L38
use crate::{CairoSerde, ContractAddress, Result, U256};
use starknet::core::types::FieldElement;

#[derive(Debug, PartialEq, PartialOrd, Clone)]
pub struct NonZero<T: Zeroable>(T);

impl<T: Zeroable> NonZero<T> {
pub fn new(value: T) -> Option<Self> {
if value.is_zero() {
None
} else {
Some(NonZero(value))
}
}

pub fn inner(&self) -> &T {
&self.0
}

pub fn inner_mut(&mut self) -> &mut T {
&mut self.0
}

pub fn into_inner(self) -> T {
self.0
}
}

impl<T, RT> CairoSerde for NonZero<T>
where
T: CairoSerde<RustType = RT>,
T: Zeroable,
RT: Zeroable,
{
type RustType = NonZero<RT>;

const SERIALIZED_SIZE: Option<usize> = T::SERIALIZED_SIZE;
const DYNAMIC: bool = T::DYNAMIC;

#[inline]
fn cairo_serialized_size(rust: &Self::RustType) -> usize {
T::cairo_serialized_size(&rust.0)
}

fn cairo_serialize(rust: &Self::RustType) -> Vec<FieldElement> {
T::cairo_serialize(&rust.0)
}

fn cairo_deserialize(felts: &[FieldElement], offset: usize) -> Result<Self::RustType> {
NonZero::new(T::cairo_deserialize(felts, offset)?).ok_or(crate::Error::ZeroedNonZero)
}
}

pub trait Zeroable {
fn is_zero(&self) -> bool;
}

macro_rules! implement_nonzeroable_for_integer {
($type:ty) => {
impl Zeroable for $type {
fn is_zero(&self) -> bool {
*self == 0 as $type
}
}
};
}

implement_nonzeroable_for_integer!(u8);
implement_nonzeroable_for_integer!(u16);
implement_nonzeroable_for_integer!(u32);
implement_nonzeroable_for_integer!(u64);
implement_nonzeroable_for_integer!(u128);
implement_nonzeroable_for_integer!(usize);
implement_nonzeroable_for_integer!(i8);
implement_nonzeroable_for_integer!(i16);
implement_nonzeroable_for_integer!(i32);
implement_nonzeroable_for_integer!(i64);
implement_nonzeroable_for_integer!(i128);
implement_nonzeroable_for_integer!(isize);

impl Zeroable for U256 {
fn is_zero(&self) -> bool {
self.low.is_zero() && self.high.is_zero()
}
}

impl Zeroable for FieldElement {
fn is_zero(&self) -> bool {
*self == FieldElement::ZERO
}
}

impl Zeroable for ContractAddress {
fn is_zero(&self) -> bool {
self.0 == FieldElement::ZERO
}
}

#[cfg(test)]
mod tests {
use crate::Error;

use super::*;

#[test]
fn test_non_zero_cairo_serialize() {
let non_zero = NonZero(1_u32);
let felts = NonZero::<u32>::cairo_serialize(&non_zero);
assert_eq!(felts.len(), 1);
assert_eq!(felts[0], FieldElement::from(1_u32));
}

#[test]
fn test_non_zero_cairo_deserialize() {
let felts = vec![FieldElement::from(1_u32)];
let non_zero = NonZero::<u32>::cairo_deserialize(&felts, 0).unwrap();
assert_eq!(non_zero, NonZero(1_u32))
}

#[test]
fn test_non_zero_cairo_deserialize_zero() {
let felts = vec![FieldElement::ZERO, FieldElement::ZERO];
let non_zero = NonZero::<U256>::cairo_deserialize(&felts, 0);
match non_zero {
Err(Error::ZeroedNonZero) => (),
_ => panic!("Expected ZeroedNonZero error"),
}
}

#[test]
fn test_non_zero_const_size() {
assert_eq!(NonZero::<u32>::SERIALIZED_SIZE, Some(1));
assert_eq!(NonZero::<U256>::SERIALIZED_SIZE, Some(2));

let is_dynamic = NonZero::<i8>::DYNAMIC;
assert!(!is_dynamic);
}
}
2 changes: 1 addition & 1 deletion crates/cairo-serde/src/types/starknet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ impl CairoSerde for ClassHash {
}

/// EthAddress.
#[derive(Debug, Copy, Clone, PartialEq)]
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
pub struct EthAddress(pub FieldElement);

impl From<FieldElement> for EthAddress {
Expand Down
Loading

0 comments on commit 60da94a

Please sign in to comment.