diff --git a/Cargo.toml b/Cargo.toml index 7998389..5685927 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,5 @@ [workspace] members = [ - "crates/stark-felt", "crates/starknet-types-core", "crates/starknet-types-rpc", ] diff --git a/crates/stark-felt/Cargo.toml b/crates/stark-felt/Cargo.toml deleted file mode 100644 index d2968dc..0000000 --- a/crates/stark-felt/Cargo.toml +++ /dev/null @@ -1,28 +0,0 @@ -[package] -name = "stark-felt" -version = "0.0.3" -edition = "2021" -license = "MIT" -homepage = "https://github.com/starknet-io/types-rs" -repository = "https://github.com/starknet-io/types-rs" -categories = ["types", "math", "crypto"] -keywords = ["stark", "zkp", "cairo"] -description = "Field element type for Cairo." -readme = "README.md" - -[dependencies] -bitvec = { version = "1.0.1", default-features = false } -serde = { version = "1.0.163", optional = true, default-features = false } -lambdaworks-math = { version = "0.2.0", default_features = false } -arbitrary = { version = "1.3.0", optional = true, default-features = false } -num-traits = { version = "0.2.16", default-features = false } - -[features] -default = ["std", "serde"] -std = [] -alloc = ["serde?/alloc"] -arbitrary = ["std", "dep:arbitrary"] - -[dev-dependencies] -proptest = "1.1.0" -serde_test = "1.0.1" diff --git a/crates/stark-felt/README.md b/crates/stark-felt/README.md deleted file mode 100644 index 5addffb..0000000 --- a/crates/stark-felt/README.md +++ /dev/null @@ -1,53 +0,0 @@ -# stark-felt - -Field element representation for Cairo and STARK proofs. - -## Overview - -The `stark-felt` crate provides the universal `Felt` (Field Element) type for Cairo and STARK proofs. It was created to reduce the fragmentation in the Starknet Rust ecosystem by providing a standardized representation of the `Felt` type. - -## Features - -- Standardized `Felt` type: Simplify your codebase by using our standardized `Felt` type. -- Optimized for performance: The `Felt` type has been optimized for high-performance applications. - -## Examples - -Here are some examples of how to use the `Felt` type: - -```rust -// example code here -``` - -## Usage - -Include `stark-felt` in your library by adding the following to your `Cargo.toml`: - -```toml -[dependencies] -stark-felt = { version = "0.0.3", git = "https://github.com/starknet-io/types-rs" } -``` - -## Build from source - -Clone the repository and navigate to the stark-felt directory. Then run: - -```bash -cargo build --release -``` - -## Testing - -Clone the repository and navigate to the stark-felt directory. Then run: - -```bash -cargo test -``` - -## Contributing - -Contributions are welcome! Please read our [contributing guidelines](CONTRIBUTING.md) for more information. - -## License - -This repository is licensed under the MIT License, see [LICENSE](LICENSE) for more information. diff --git a/crates/starknet-types-core/Cargo.toml b/crates/starknet-types-core/Cargo.toml index 4f43988..2c6b64b 100644 --- a/crates/starknet-types-core/Cargo.toml +++ b/crates/starknet-types-core/Cargo.toml @@ -1,13 +1,31 @@ [package] name = "starknet-types-core" -version = "0.0.2" +version = "0.0.3" edition = "2021" license = "MIT" homepage = "https://github.com/starknet-io/types-rs" repository = "https://github.com/starknet-io/types-rs" categories = ["types", "math", "crypto"] keywords = ["stark", "zkp", "cairo"] -description = "Starknet core types." +description = "Core types representation for Starknet" readme = "README.md" [dependencies] +bitvec = { version = "1.0.1", default-features = false } +serde = { version = "1.0.163", optional = true, default-features = false } +lambdaworks-math = { git = "https://github.com/lambdaclass/lambdaworks.git", rev = "f940e14ed17370d29fe129951448037d11b65ce8", default-features = false} + + +arbitrary = { version = "1.3.0", optional = true, default-features = false } +num-traits = { version = "0.2.16", default-features = false } + +[features] +default = ["std", "serde", "curve"] +curve = [] +std = [] +alloc = ["serde?/alloc"] +arbitrary = ["std", "dep:arbitrary"] + +[dev-dependencies] +proptest = "1.1.0" +serde_test = "1.0.1" diff --git a/crates/starknet-types-core/README.md b/crates/starknet-types-core/README.md index 56305d3..6ead45f 100644 --- a/crates/starknet-types-core/README.md +++ b/crates/starknet-types-core/README.md @@ -1,8 +1,27 @@ # starknet-types-core -`starknet-types-core` is a crate focusing on Starknet types related to computation and execution. This crate is part of an initiative to standardize the representation of the `Felt` type in Rust, reducing code complexity and improving performance across the Starknet Rust ecosystem. +Core types representation for Starknet. -The types in this crate require performance and optimization for specific arithmetic and cryptographic operations, making it ideal for computational tasks within the Starknet ecosystem. +## Overview + +The `starknet-types-core` crate provides: +* The universal `Felt` (Field Element) type for Cairo and STARK proofs. It was created to reduce the fragmentation in the Starknet Rust ecosystem by providing a standardized representation of the `Felt` type. +* The `AffinePoint` and `ProjectivePoint` structs, which represent points on the Stark curve for performing elliptic curve operations. + +## Features + +- Standardized `Felt` type: Simplify your codebase by using our standardized `Felt` type. +- Optimized for performance: The `Felt` type has been optimized for high-performance applications. + +## Examples + +Here are some examples of how to use the `starknet-types-core` types: + +```rust + let felt = Felt::from(18); + let projective_point = ProjectivePoint::new(Felt::from(0), Felt::from(1), Felt::from(0)); + let affine_point = AffinePoint::new(Felt::from(0), Felt::from(1)).unwrap(); +``` ## Usage @@ -10,7 +29,7 @@ Include `starknet-types-core` in your library by adding the following to your `C ```toml [dependencies] -starknet-types-core = { version = "0.0.2", git = "https://github.com/starknet-io/types-rs" } +starknet-types-core = { version = "0.0.3", git = "https://github.com/starknet-io/types-rs" } ``` ## Build from source diff --git a/crates/starknet-types-core/src/curve/affine_point.rs b/crates/starknet-types-core/src/curve/affine_point.rs new file mode 100644 index 0000000..3e674d0 --- /dev/null +++ b/crates/starknet-types-core/src/curve/affine_point.rs @@ -0,0 +1,48 @@ +use crate::curve::curve_errors::CurveError; +use crate::felt::Felt; +use lambdaworks_math::cyclic_group::IsGroup; +use lambdaworks_math::elliptic_curve::short_weierstrass::curves::stark_curve::StarkCurve; +use lambdaworks_math::elliptic_curve::short_weierstrass::point::ShortWeierstrassProjectivePoint; +use lambdaworks_math::elliptic_curve::traits::FromAffine; + +/// Represents a point on the Stark elliptic curve. +/// Doc: https://docs.starkware.co/starkex/crypto/stark-curve.html +#[repr(transparent)] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct AffinePoint(pub(crate) ShortWeierstrassProjectivePoint); + +impl AffinePoint { + pub fn new(x: Felt, y: Felt) -> Result { + Ok(Self(ShortWeierstrassProjectivePoint::from_affine( + x.0, y.0, + )?)) + } + + /// The point at infinity. + pub fn identity() -> AffinePoint { + Self(ShortWeierstrassProjectivePoint::neutral_element()) + } + + /// Returns the `x` coordinate of the point. + pub fn x(&self) -> Felt { + Felt(*self.0.x()) + } + + /// Returns the `y` coordinate of the point. + pub fn y(&self) -> Felt { + Felt(*self.0.y()) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn affine_point_identity() { + let identity = AffinePoint::identity(); + + assert_eq!(identity.x(), Felt::from(0)); + assert_eq!(identity.y(), Felt::from(1)); + } +} diff --git a/crates/starknet-types-core/src/curve/curve_errors.rs b/crates/starknet-types-core/src/curve/curve_errors.rs new file mode 100644 index 0000000..ec9e8ef --- /dev/null +++ b/crates/starknet-types-core/src/curve/curve_errors.rs @@ -0,0 +1,13 @@ +use core::fmt::Debug; +use lambdaworks_math::elliptic_curve::traits::EllipticCurveError; + +#[derive(Debug, PartialEq, Eq)] +pub enum CurveError { + EllipticCurveError(EllipticCurveError), +} + +impl From for CurveError { + fn from(error: EllipticCurveError) -> CurveError { + CurveError::EllipticCurveError(error) + } +} diff --git a/crates/starknet-types-core/src/curve/mod.rs b/crates/starknet-types-core/src/curve/mod.rs new file mode 100644 index 0000000..7f315a8 --- /dev/null +++ b/crates/starknet-types-core/src/curve/mod.rs @@ -0,0 +1,7 @@ +mod affine_point; +mod curve_errors; +mod projective_point; + +pub use self::affine_point::*; +pub use self::curve_errors::*; +pub use self::projective_point::*; diff --git a/crates/starknet-types-core/src/curve/projective_point.rs b/crates/starknet-types-core/src/curve/projective_point.rs new file mode 100644 index 0000000..5d174fb --- /dev/null +++ b/crates/starknet-types-core/src/curve/projective_point.rs @@ -0,0 +1,215 @@ +use crate::curve::affine_point::AffinePoint; +use crate::curve::curve_errors::CurveError; +use crate::felt::Felt; +use core::ops; +use lambdaworks_math::cyclic_group::IsGroup; +use lambdaworks_math::elliptic_curve::short_weierstrass::curves::stark_curve::StarkCurve; +use lambdaworks_math::elliptic_curve::short_weierstrass::point::ShortWeierstrassProjectivePoint; +use lambdaworks_math::elliptic_curve::traits::EllipticCurveError::InvalidPoint; +use lambdaworks_math::unsigned_integer::traits::IsUnsignedInteger; + +/// Represents a projective point on the Stark elliptic curve. +#[repr(transparent)] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ProjectivePoint(pub(crate) ShortWeierstrassProjectivePoint); + +impl ProjectivePoint { + pub fn new(x: Felt, y: Felt, z: Felt) -> ProjectivePoint { + Self(ShortWeierstrassProjectivePoint::new([x.0, y.0, z.0])) + } + + /// The point at infinity. + pub fn identity() -> ProjectivePoint { + Self(ShortWeierstrassProjectivePoint::neutral_element()) + } + + /// Creates the same point in affine coordinates. That is, + /// returns (x * inv_z, y* inv_z, 1) + /// where `self` is the point (x, y, z) and inv_z is the multiplicative inverse of z + pub fn to_affine(&self) -> Result { + if self.z() == Felt::ZERO { + return Err(CurveError::EllipticCurveError(InvalidPoint)); + } + Ok(AffinePoint(self.0.to_affine())) + } + + /// Returns the `x` coordinate of the point. + pub fn x(&self) -> Felt { + Felt(*self.0.x()) + } + + /// Returns the `y` coordinate of the point. + pub fn y(&self) -> Felt { + Felt(*self.0.y()) + } + + /// Returns the `z` coordinate of the point. + pub fn z(&self) -> Felt { + Felt(*self.0.z()) + } +} + +impl ops::Add<&ProjectivePoint> for &ProjectivePoint { + type Output = ProjectivePoint; + + fn add(self, rhs: &ProjectivePoint) -> ProjectivePoint { + ProjectivePoint(self.0.operate_with(&rhs.0)) + } +} + +impl ops::AddAssign<&ProjectivePoint> for ProjectivePoint { + fn add_assign(&mut self, rhs: &ProjectivePoint) { + self.0 = self.0.operate_with(&rhs.0); + } +} + +impl ops::Mul for &ProjectivePoint { + type Output = ProjectivePoint; + + fn mul(self, rhs: Felt) -> ProjectivePoint { + ProjectivePoint(self.0.operate_with_self(rhs.0.representative())) + } +} + +impl ops::Mul for &ProjectivePoint +where + T: IsUnsignedInteger, +{ + type Output = ProjectivePoint; + + fn mul(self, rhs: T) -> ProjectivePoint { + ProjectivePoint(self.0.operate_with_self(rhs)) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn projective_point_identity() { + let identity = ProjectivePoint::identity(); + + assert_eq!( + identity, + ProjectivePoint::new(Felt::from(0), Felt::from(1), Felt::from(0)) + ); + + assert_eq!( + identity.to_affine(), + Err(CurveError::EllipticCurveError(InvalidPoint)) + ); + } + + #[test] + // Results checked against starknet-rs https://github.com/xJonathanLEI/starknet-rs/ + fn add_operations() { + let projective_point_1 = ProjectivePoint::new( + Felt::from_dec_str( + "874739451078007766457464989774322083649278607533249481151382481072868806602", + ) + .unwrap(), + Felt::from_dec_str( + "152666792071518830868575557812948353041420400780739481342941381225525861407", + ) + .unwrap(), + Felt::from(1), + ); + let projective_point_2 = projective_point_1.clone(); + let result = (&projective_point_1 + &projective_point_2) + .to_affine() + .unwrap(); + + assert_eq!( + result, + AffinePoint::new( + Felt::from_dec_str( + "3324833730090626974525872402899302150520188025637965566623476530814354734325", + ) + .unwrap(), + Felt::from_dec_str( + "3147007486456030910661996439995670279305852583596209647900952752170983517249", + ) + .unwrap() + ) + .unwrap() + ) + } + + #[test] + // Results checked against starknet-rs https://github.com/xJonathanLEI/starknet-rs/ + fn add_assign_operations() { + let mut projective_point_1 = ProjectivePoint::new( + Felt::from_dec_str( + "874739451078007766457464989774322083649278607533249481151382481072868806602", + ) + .unwrap(), + Felt::from_dec_str( + "152666792071518830868575557812948353041420400780739481342941381225525861407", + ) + .unwrap(), + Felt::from(1), + ); + let projective_point_2 = projective_point_1.clone(); + projective_point_1 += &projective_point_2; + + let result = projective_point_1.to_affine().unwrap(); + + assert_eq!( + result.x(), + Felt::from_dec_str( + "3324833730090626974525872402899302150520188025637965566623476530814354734325", + ) + .unwrap() + ); + + assert_eq!( + result.y(), + Felt::from_dec_str( + "3147007486456030910661996439995670279305852583596209647900952752170983517249", + ) + .unwrap() + ); + } + + #[test] + // Results checked against starknet-rs https://github.com/xJonathanLEI/starknet-rs/ + fn mul_operations() { + let identity = ProjectivePoint::identity(); + + assert_eq!(&identity * 11_u16, identity); + assert_eq!( + &identity * Felt::from_dec_str("8731298391798138132780",).unwrap(), + identity + ); + + let projective_point_1 = ProjectivePoint::new( + Felt::from_dec_str( + "685118385380464480289795596422487144864558069280897344382334516257395969277", + ) + .unwrap(), + Felt::from_dec_str( + "2157469546853095472290556201984093730375838368522549154974787195581425752638", + ) + .unwrap(), + Felt::from(1), + ); + + let result = (&projective_point_1 * 1812_u32).to_affine().unwrap(); + + assert_eq!( + result, + AffinePoint::new( + Felt::from_dec_str( + "3440268448213322209285127313797148367487473316555419755705577898182859853039", + ) + .unwrap(), + Felt::from_dec_str( + "1596323783766236796787317367695486687781666659527154739146733884430376982452", + ) + .unwrap() + ) + .unwrap() + ) + } +} diff --git a/crates/stark-felt/src/lib.rs b/crates/starknet-types-core/src/felt.rs similarity index 99% rename from crates/stark-felt/src/lib.rs rename to crates/starknet-types-core/src/felt.rs index 12c0328..6b5ee2d 100644 --- a/crates/stark-felt/src/lib.rs +++ b/crates/starknet-types-core/src/felt.rs @@ -1,13 +1,8 @@ -#![cfg_attr(not(feature = "std"), no_std)] - use core::ops::{Add, Neg}; use bitvec::array::BitArray; use num_traits::{FromPrimitive, ToPrimitive, Zero}; -#[cfg(test)] -mod arbitrary_proptest; - #[cfg(target_pointer_width = "64")] pub type BitArrayStore = [u64; 4]; @@ -31,9 +26,10 @@ use lambdaworks_math::{ #[cfg(feature = "arbitrary")] use arbitrary::{self, Arbitrary, Unstructured}; +#[repr(transparent)] /// Definition of the Field Element type. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Felt(FieldElement); +pub struct Felt(pub(crate) FieldElement); /// A non-zero [Felt]. pub struct NonZeroFelt(FieldElement); @@ -830,7 +826,7 @@ mod errors { mod test { use super::alloc::{format, string::String, vec::Vec}; use super::*; - use crate::arbitrary_proptest::nonzero_felt; + use crate::felt_arbitrary::nonzero_felt; use core::ops::Shl; use proptest::prelude::*; diff --git a/crates/stark-felt/src/arbitrary_proptest.rs b/crates/starknet-types-core/src/felt_arbitrary.rs similarity index 98% rename from crates/stark-felt/src/arbitrary_proptest.rs rename to crates/starknet-types-core/src/felt_arbitrary.rs index 49ea020..4f2b0f4 100644 --- a/crates/stark-felt/src/arbitrary_proptest.rs +++ b/crates/starknet-types-core/src/felt_arbitrary.rs @@ -2,7 +2,7 @@ use lambdaworks_math::{field::element::FieldElement, unsigned_integer::element:: use num_traits::Zero; use proptest::prelude::*; -use crate::Felt; +use crate::felt::Felt; const FIELD_HIGH: u128 = (1 << 123) + (17 << 64); // this is equal to 10633823966279327296825105735305134080 const FIELD_LOW: u128 = 1; diff --git a/crates/starknet-types-core/src/lib.rs b/crates/starknet-types-core/src/lib.rs index b8c7f30..18855ad 100644 --- a/crates/starknet-types-core/src/lib.rs +++ b/crates/starknet-types-core/src/lib.rs @@ -1,2 +1,6 @@ -//! Rust types for Starknet. -//! This crate is meant to be used by other crates that need to interact with Starknet. +#![cfg_attr(not(feature = "std"), no_std)] +#[cfg(feature = "curve")] +pub mod curve; +pub mod felt; +#[cfg(test)] +mod felt_arbitrary; diff --git a/ensure_no_std/Cargo.toml b/ensure_no_std/Cargo.toml index ee65b70..e18e611 100644 --- a/ensure_no_std/Cargo.toml +++ b/ensure_no_std/Cargo.toml @@ -4,11 +4,11 @@ version = "0.1.0" edition = "2021" [dependencies] -stark-felt = { path = "../crates/stark-felt", default-features = false, features = [ +starknet-types-core = { path = "../crates/starknet-types-core", default-features = false, features = [ "alloc", "serde", + "curve", ] } -starknet-types-core = { path = "../crates/starknet-types-core", default-features = false } starknet-types-rpc = { path = "../crates/starknet-types-rpc", default-features = false } wee_alloc = "0.4.5" diff --git a/ensure_no_std/src/main.rs b/ensure_no_std/src/main.rs index edbb888..ae94696 100644 --- a/ensure_no_std/src/main.rs +++ b/ensure_no_std/src/main.rs @@ -18,4 +18,4 @@ pub extern "C" fn _start() -> ! { static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; #[allow(unused_imports)] -use stark_felt; +use starknet_types_core;