From 07ad77823d5ac9ca8106ff0a0e6aa91b9286121b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Sm=C3=B3=C5=82ka?= Date: Tue, 1 Oct 2024 13:41:43 +0200 Subject: [PATCH] Implemented representations of Cairo `u256` and `u512` commit-id:fad6001c --- Cargo.lock | 1 + .../forge_runtime_extension/mod.rs | 27 +------- crates/conversions/Cargo.toml | 1 + crates/conversions/src/lib.rs | 2 + .../src/serde/serialize/serialize_impl.rs | 6 ++ crates/conversions/src/u256.rs | 56 +++++++++++++++++ crates/conversions/src/u512.rs | 62 +++++++++++++++++++ 7 files changed, 130 insertions(+), 25 deletions(-) create mode 100644 crates/conversions/src/u256.rs create mode 100644 crates/conversions/src/u512.rs diff --git a/Cargo.lock b/Cargo.lock index 3957fdc250..06dfc1e98b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1460,6 +1460,7 @@ dependencies = [ "ctor", "indoc", "itertools 0.12.1", + "num-bigint", "num-traits 0.2.19", "regex", "serde", diff --git a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs index f05bdbc8cd..81e4764ab5 100644 --- a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs +++ b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/mod.rs @@ -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, @@ -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, diff --git a/crates/conversions/Cargo.toml b/crates/conversions/Cargo.toml index ebd5845265..5130f33761 100644 --- a/crates/conversions/Cargo.toml +++ b/crates/conversions/Cargo.toml @@ -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" } diff --git a/crates/conversions/src/lib.rs b/crates/conversions/src/lib.rs index 5209b14002..7cef947986 100644 --- a/crates/conversions/src/lib.rs +++ b/crates/conversions/src/lib.rs @@ -10,6 +10,8 @@ pub mod nonce; pub mod primitive; pub mod serde; pub mod string; +pub mod u256; +pub mod u512; pub trait FromConv: Sized { fn from_(value: T) -> Self; diff --git a/crates/conversions/src/serde/serialize/serialize_impl.rs b/crates/conversions/src/serde/serialize/serialize_impl.rs index b944492e4e..7685e5e3b4 100644 --- a/crates/conversions/src/serde/serialize/serialize_impl.rs +++ b/crates/conversions/src/serde/serialize/serialize_impl.rs @@ -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); diff --git a/crates/conversions/src/u256.rs b/crates/conversions/src/u256.rs new file mode 100644 index 0000000000..11254bd2dd --- /dev/null +++ b/crates/conversions/src/u256.rs @@ -0,0 +1,56 @@ +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, Debug)] +pub struct CairoU256 { + low: u128, + high: u128, +} + +impl CairoU256 { + #[must_use] + pub 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()), + } + } + + #[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 { + let bytes = input.parse::()?.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)) + } +} diff --git a/crates/conversions/src/u512.rs b/crates/conversions/src/u512.rs new file mode 100644 index 0000000000..baf97a332e --- /dev/null +++ b/crates/conversions/src/u512.rs @@ -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, Debug)] +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]) -> 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 { + let bytes = input.parse::()?.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)) + } +}