Skip to content

Commit

Permalink
Implemented representations of Cairo u256 and u512
Browse files Browse the repository at this point in the history
commit-id:fad6001c
  • Loading branch information
integraledelebesgue committed Oct 3, 2024
1 parent b3eee6e commit 67f3995
Show file tree
Hide file tree
Showing 8 changed files with 184 additions and 25 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,9 @@ use cairo_vm::vm::{
use cairo_vm::Felt252;
use conversions::byte_array::ByteArray;
use conversions::felt252::TryInferFormat;
use conversions::serde::deserialize::{BufferReader, CairoDeserialize};
use conversions::serde::deserialize::BufferReader;
use conversions::serde::serialize::CairoSerialize;
use conversions::u256::CairoU256;
use runtime::{
CheatcodeHandlingResult, EnhancedHintError, ExtendedRuntime, ExtensionLogic,
SyscallHandlingResult,
Expand Down Expand Up @@ -491,30 +492,6 @@ impl<'a> ExtensionLogic for ForgeExtension<'a> {
}
}

#[derive(CairoDeserialize, CairoSerialize)]
struct CairoU256 {
low: u128,
high: u128,
}

impl CairoU256 {
fn from_bytes(bytes: &[u8]) -> Self {
Self {
low: u128::from_be_bytes(bytes[16..32].try_into().unwrap()),
high: u128::from_be_bytes(bytes[0..16].try_into().unwrap()),
}
}

fn to_be_bytes(&self) -> [u8; 32] {
let mut result = [0; 32];

result[16..].copy_from_slice(&self.low.to_be_bytes());
result[..16].copy_from_slice(&self.high.to_be_bytes());

result
}
}

#[derive(CairoSerialize)]
enum SignError {
InvalidSecretKey,
Expand Down
1 change: 1 addition & 0 deletions crates/conversions/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ thiserror.workspace = true
serde_json.workspace = true
serde.workspace = true
num-traits.workspace = true
num-bigint.workspace = true
itertools.workspace = true
cairo-serde-macros = { path = "cairo-serde-macros" }

Expand Down
52 changes: 52 additions & 0 deletions crates/conversions/src/bytes31.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use crate as conversions; // Must be imported because of derive macros
use cairo_serde_macros::{CairoDeserialize, CairoSerialize};
use starknet::core::types::Felt;
use starknet_types_core::felt::FromStrError;
use std::str::FromStr;

#[derive(CairoDeserialize, CairoSerialize, Debug, Clone, Copy, PartialEq, Eq)]
pub struct CairoBytes31 {
inner: Felt,
}

impl CairoBytes31 {
pub const MAX: CairoBytes31 = CairoBytes31 {
inner: Felt::from_hex_unchecked(
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
),
};
}

#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
pub enum ParseBytes31Error {
#[error("Failed to parse as Cairo type")]
CairoFromStrError, // Error thrown by Cairo is useless. We cannot write anything beyond that
#[error("Number is too large to fit in 31 bytes")]
Overflow,
}

impl From<FromStrError> for ParseBytes31Error {
fn from(_value: FromStrError) -> Self {
ParseBytes31Error::CairoFromStrError
}
}

impl FromStr for CairoBytes31 {
type Err = ParseBytes31Error;

fn from_str(input: &str) -> Result<Self, Self::Err> {
let inner = input.parse::<Felt>()?;

if inner > CairoBytes31::MAX.inner {
return Err(ParseBytes31Error::Overflow);
}

Ok(CairoBytes31 { inner })
}
}

impl From<CairoBytes31> for Felt {
fn from(value: CairoBytes31) -> Self {
value.inner
}
}
3 changes: 3 additions & 0 deletions crates/conversions/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::convert::Infallible;

pub mod byte_array;
pub mod bytes31;
pub mod class_hash;
pub mod contract_address;
pub mod entrypoint_selector;
Expand All @@ -10,6 +11,8 @@ pub mod nonce;
pub mod primitive;
pub mod serde;
pub mod string;
pub mod u256;
pub mod u512;

pub trait FromConv<T>: Sized {
fn from_(value: T) -> Self;
Expand Down
6 changes: 6 additions & 0 deletions crates/conversions/src/serde/serialize/serialize_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,12 @@ impl_serialize_for_num_type!(u64);
impl_serialize_for_num_type!(u128);
impl_serialize_for_num_type!(usize);

impl_serialize_for_num_type!(i8);
impl_serialize_for_num_type!(i16);
impl_serialize_for_num_type!(i32);
impl_serialize_for_num_type!(i64);
impl_serialize_for_num_type!(i128);

impl_serialize_for_tuple!();
impl_serialize_for_tuple!(A);
impl_serialize_for_tuple!(A, B);
Expand Down
57 changes: 57 additions & 0 deletions crates/conversions/src/u256.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use crate as conversions; // Must be imported because of derive macros
use cairo_serde_macros::{CairoDeserialize, CairoSerialize};
use num_bigint::{BigUint, ParseBigIntError};
use std::str::FromStr;

#[derive(CairoDeserialize, CairoSerialize, Clone, Copy, Debug, PartialEq, Eq)]
pub struct CairoU256 {
low: u128,
high: u128,
}

impl CairoU256 {
#[must_use]
pub fn from_bytes(bytes: &[u8]) -> Self {
// Takes slice without explicit size because of cheatnet's specific usages
Self {
low: u128::from_be_bytes(bytes[16..32].try_into().unwrap()),
high: u128::from_be_bytes(bytes[0..16].try_into().unwrap()),
}
}

#[must_use]
pub fn to_be_bytes(&self) -> [u8; 32] {
let mut result = [0; 32];

result[16..].copy_from_slice(&self.low.to_be_bytes());
result[..16].copy_from_slice(&self.high.to_be_bytes());

result
}
}

#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
pub enum ParseCairoU256Error {
#[error(transparent)]
InvalidString(#[from] ParseBigIntError),
#[error("Number is too large to fit in 32 bytes")]
Overflow,
}

impl FromStr for CairoU256 {
type Err = ParseCairoU256Error;

fn from_str(input: &str) -> Result<Self, Self::Err> {
let bytes = input.parse::<BigUint>()?.to_bytes_be();

if bytes.len() > 32 {
return Err(ParseCairoU256Error::Overflow);
}

let mut result = [0u8; 32];
let start = 32 - bytes.len();
result[start..].copy_from_slice(&bytes);

Ok(CairoU256::from_bytes(&result))
}
}
62 changes: 62 additions & 0 deletions crates/conversions/src/u512.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use crate as conversions; // Must be imported because of derive macros
use cairo_serde_macros::{CairoDeserialize, CairoSerialize};
use num_bigint::{BigUint, ParseBigIntError};
use std::str::FromStr;

#[derive(CairoDeserialize, CairoSerialize, Clone, Copy, Debug, PartialEq, Eq)]
pub struct CairoU512 {
limb_0: u128,
limb_1: u128,
limb_2: u128,
limb_3: u128,
}

impl CairoU512 {
#[must_use]
pub fn from_bytes(bytes: &[u8; 64]) -> Self {
Self {
limb_0: u128::from_be_bytes(bytes[48..64].try_into().unwrap()),
limb_1: u128::from_be_bytes(bytes[32..48].try_into().unwrap()),
limb_2: u128::from_be_bytes(bytes[16..32].try_into().unwrap()),
limb_3: u128::from_be_bytes(bytes[00..16].try_into().unwrap()),
}
}

#[must_use]
pub fn to_be_bytes(&self) -> [u8; 64] {
let mut result = [0; 64];

result[48..64].copy_from_slice(&self.limb_0.to_be_bytes());
result[32..48].copy_from_slice(&self.limb_1.to_be_bytes());
result[16..32].copy_from_slice(&self.limb_2.to_be_bytes());
result[00..16].copy_from_slice(&self.limb_3.to_be_bytes());

result
}
}

#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
pub enum ParseCairoU512Error {
#[error(transparent)]
InvalidString(#[from] ParseBigIntError),
#[error("Number is too large to fit in 64 bytes")]
Overflow,
}

impl FromStr for CairoU512 {
type Err = ParseCairoU512Error;

fn from_str(input: &str) -> Result<Self, Self::Err> {
let bytes = input.parse::<BigUint>()?.to_bytes_be();

if bytes.len() > 64 {
return Err(ParseCairoU512Error::Overflow);
}

let mut result = [0u8; 64];
let start = 64 - bytes.len();
result[start..].copy_from_slice(&bytes);

Ok(CairoU512::from_bytes(&result))
}
}

0 comments on commit 67f3995

Please sign in to comment.