diff --git a/MANIFEST.in b/MANIFEST.in index 0133df06..73b9c694 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,4 @@ include LICENSE recursive-include src/nile/artifacts/ * recursive-include src/nile/base_project/ * +recursive-include src/nile/core/cairo1/compilers/ * diff --git a/src/nile/cli.py b/src/nile/cli.py index cb3fe019..cfcbe3c3 100644 --- a/src/nile/cli.py +++ b/src/nile/cli.py @@ -8,6 +8,7 @@ import asyncclick as click from nile.common import is_alias +from nile.core.cairo1.compile import compile as compile_1_command from nile.core.call_or_invoke import call_or_invoke as call_or_invoke_command from nile.core.clean import clean as clean_command from nile.core.compile import compile as compile_command @@ -350,6 +351,15 @@ def compile( ) +@cli.command() +@click.argument("contracts", nargs=-1) +@click.option("--directory") +@enable_stack_trace +def compile_1(ctx, contracts, directory): + """Compile cairo1 contracts.""" + compile_1_command(contracts, directory) + + @cli.command() @enable_stack_trace def clean(ctx): diff --git a/src/nile/common.py b/src/nile/common.py index bd1ef0fb..afa38406 100644 --- a/src/nile/common.py +++ b/src/nile/common.py @@ -90,7 +90,7 @@ def get_all_contracts(ext=None, directory=None): ext = ".cairo" files = list() - for (dirpath, _, filenames) in os.walk( + for dirpath, _, filenames in os.walk( directory if directory else CONTRACTS_DIRECTORY ): files += [ diff --git a/src/nile/core/cairo1/common.py b/src/nile/core/cairo1/common.py new file mode 100644 index 00000000..6beabb45 --- /dev/null +++ b/src/nile/core/cairo1/common.py @@ -0,0 +1,7 @@ +"""Cairo1 common.""" + +from pathlib import Path + +COMPILERS_BIN_PATH = Path(__file__).parent / "./compilers/src/bin" +BUILD_DIRECTORY = "artifacts/cairo1" +ABIS_DIRECTORY = f"{BUILD_DIRECTORY}/abis" diff --git a/src/nile/core/cairo1/compile.py b/src/nile/core/cairo1/compile.py new file mode 100644 index 00000000..6547eef4 --- /dev/null +++ b/src/nile/core/cairo1/compile.py @@ -0,0 +1,94 @@ +"""Command to compile cairo1 files.""" +import json +import logging +import os +import subprocess + +from nile.common import CONTRACTS_DIRECTORY, get_all_contracts +from nile.core.cairo1.common import ABIS_DIRECTORY, BUILD_DIRECTORY, COMPILERS_BIN_PATH + + +def compile( + contracts, + directory=None, +): + """Compile command.""" + contracts_directory = directory if directory else CONTRACTS_DIRECTORY + + if not os.path.exists(ABIS_DIRECTORY): + logging.info(f"📁 Creating {ABIS_DIRECTORY} to store compilation artifacts") + os.makedirs(ABIS_DIRECTORY, exist_ok=True) + + all_contracts = contracts + + if len(contracts) == 0: + logging.info( + f"🤖 Compiling all Cairo contracts in the {contracts_directory} directory" + ) + all_contracts = get_all_contracts(directory=contracts_directory) + + results = [] + + for contract in all_contracts: + status_code, sierra_file, filename = _compile_to_sierra( + contract, contracts_directory + ) + results.append(status_code) + if status_code == 0: + _extract_abi(sierra_file, filename) + _compile_to_casm(sierra_file, filename) + + failed_contracts = [c for (c, r) in zip(all_contracts, results) if r != 0] + failures = len(failed_contracts) + + if failures == 0: + logging.info("✅ Done") + else: + exp = f"{failures} contract" + if failures > 1: + exp += "s" # pluralize + logging.info(f"🛑 Failed to compile the following {exp}:") + for contract in failed_contracts: + logging.info(f" {contract}") + + +def _compile_to_sierra( + path, + directory=None, +): + """Compile from Cairo1 to Sierra.""" + base = os.path.basename(path) + filename = os.path.splitext(base)[0] + logging.info(f"🔨 Compiling {path}") + + sierra_file = f"{BUILD_DIRECTORY}/{filename}.sierra" + + cmd = f""" + {COMPILERS_BIN_PATH}/starknet-compile {path} \ + {sierra_file} + """ + + process = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE) + process.communicate() + + return process.returncode, sierra_file, filename + + +def _compile_to_casm(sierra_file, filename): + """Compile from Sierra to Casm.""" + cmd = f""" + {COMPILERS_BIN_PATH}/starknet-sierra-compile {sierra_file} \ + {BUILD_DIRECTORY}/{filename}.casm + """ + + process = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE) + process.communicate() + return process.returncode + + +def _extract_abi(sierra_file, filename): + with open(sierra_file, "r") as f: + data = json.load(f) + + with open(f"{ABIS_DIRECTORY}/{filename}.json", "w") as f: + json.dump(data["abi"], f, indent=2) diff --git a/src/nile/core/cairo1/compilers/corelib/array.cairo b/src/nile/core/cairo1/compilers/corelib/array.cairo new file mode 100644 index 00000000..ab7e6beb --- /dev/null +++ b/src/nile/core/cairo1/compilers/corelib/array.cairo @@ -0,0 +1,51 @@ +extern type Array; +extern fn array_new() -> Array:: nopanic; +extern fn array_append(ref arr: Array::, value: T) nopanic; +extern fn array_pop_front(ref arr: Array::) -> Option:: nopanic; +#[panic_with('Array out of bounds', array_at)] +extern fn array_get( + ref arr: Array::, index: usize +) -> Option:: implicits(RangeCheck) nopanic; +extern fn array_len(ref arr: Array::) -> usize nopanic; + +trait ArrayTrait { + fn new() -> Array::; + fn append(ref self: Array::, value: T); + fn pop_front(ref self: Array::) -> Option::; + fn get(ref self: Array::, index: usize) -> Option::; + fn at(ref self: Array::, index: usize) -> T; + fn len(ref self: Array::) -> usize; + fn is_empty(ref self: Array::) -> bool; +} +impl ArrayImpl of ArrayTrait:: { + #[inline(always)] + fn new() -> Array:: { + array_new() + } + #[inline(always)] + fn append(ref self: Array::, value: T) { + array_append(ref self, value) + } + #[inline(always)] + fn pop_front(ref self: Array::) -> Option:: { + array_pop_front(ref self) + } + #[inline(always)] + fn get(ref self: Array::, index: usize) -> Option:: { + array_get(ref self, index) + } + fn at(ref self: Array::, index: usize) -> T { + array_at(ref self, index) + } + #[inline(always)] + fn len(ref self: Array::) -> usize { + array_len(ref self) + } + #[inline(always)] + fn is_empty(ref self: Array::) -> bool { + self.len() == 0_usize + } +} + +// Impls for common generic types +impl ArrayFeltDrop of Drop::>; diff --git a/src/nile/core/cairo1/compilers/corelib/box.cairo b/src/nile/core/cairo1/compilers/corelib/box.cairo new file mode 100644 index 00000000..5eed438e --- /dev/null +++ b/src/nile/core/cairo1/compilers/corelib/box.cairo @@ -0,0 +1,6 @@ +extern type Box; +impl BoxFeltCopy of Copy::>; +impl BoxFeltDrop of Drop::>; + +extern fn into_box(value: T) -> Box:: nopanic; +extern fn unbox(box: Box::) -> T nopanic; diff --git a/src/nile/core/cairo1/compilers/corelib/cairo_project.toml b/src/nile/core/cairo1/compilers/corelib/cairo_project.toml new file mode 100644 index 00000000..67cd29d4 --- /dev/null +++ b/src/nile/core/cairo1/compilers/corelib/cairo_project.toml @@ -0,0 +1,2 @@ +[crate_roots] +core = "." diff --git a/src/nile/core/cairo1/compilers/corelib/debug.cairo b/src/nile/core/cairo1/compilers/corelib/debug.cairo new file mode 100644 index 00000000..846d2a02 --- /dev/null +++ b/src/nile/core/cairo1/compilers/corelib/debug.cairo @@ -0,0 +1,9 @@ +use array::ArrayTrait; + +extern fn print(message: Array::) nopanic; + +fn print_felt(message: felt) { + let mut arr = ArrayTrait::new(); + arr.append(message); + print(arr); +} diff --git a/src/nile/core/cairo1/compilers/corelib/dict.cairo b/src/nile/core/cairo1/compilers/corelib/dict.cairo new file mode 100644 index 00000000..e55eca9d --- /dev/null +++ b/src/nile/core/cairo1/compilers/corelib/dict.cairo @@ -0,0 +1,32 @@ +extern type DictManager; +extern type DictFeltTo; +extern type SquashedDictFeltTo; +impl SquashedDictFeltToFeltDrop of Drop::>; + +extern fn dict_felt_to_new() -> DictFeltTo:: implicits(DictManager) nopanic; +extern fn dict_felt_to_write(ref dict: DictFeltTo::, key: felt, value: T) nopanic; +extern fn dict_felt_to_read(ref dict: DictFeltTo::, key: felt) -> T nopanic; +extern fn dict_felt_to_squash( + dict: DictFeltTo:: +) -> SquashedDictFeltTo:: implicits(RangeCheck, GasBuiltin, DictManager) nopanic; + +trait DictFeltToTrait { + fn new() -> DictFeltTo::; + fn insert(ref self: DictFeltTo::, key: felt, value: T); + fn get(ref self: DictFeltTo::, key: felt) -> T; + fn squash(self: DictFeltTo::) -> SquashedDictFeltTo::; +} +impl DictFeltToImpl of DictFeltToTrait:: { + fn new() -> DictFeltTo:: { + dict_felt_to_new() + } + fn insert(ref self: DictFeltTo::, key: felt, value: T) { + dict_felt_to_write(ref self, key, value) + } + fn get(ref self: DictFeltTo::, key: felt) -> T { + dict_felt_to_read(ref self, key) + } + fn squash(self: DictFeltTo::) -> SquashedDictFeltTo:: { + dict_felt_to_squash(self) + } +} diff --git a/src/nile/core/cairo1/compilers/corelib/ec.cairo b/src/nile/core/cairo1/compilers/corelib/ec.cairo new file mode 100644 index 00000000..26869774 --- /dev/null +++ b/src/nile/core/cairo1/compilers/corelib/ec.cairo @@ -0,0 +1,145 @@ +mod StarkCurve { + /// The STARK Curve is defined by the equation `y^2 = x^3 + ALPHA*x + BETA`. + const ALPHA: felt = 1; + /// The STARK Curve is defined by the equation `y^2 = x^3 + ALPHA*x + BETA`. + const BETA: felt = 0x6f21413efbe40de150e596d72f7a8c5609ad26c15c915c1f4cdfcb99cee9e89; + /// The order (number of points) of the STARK Curve. + const ORDER: felt = 0x800000000000010ffffffffffffffffb781126dcae7b2321e66a241adc64d2f; + /// The x coordinate of the generator point used in the ECDSA signature. + const GEN_X: felt = 0x1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca; + /// The y coordinate of the generator point used in the ECDSA signature. + const GEN_Y: felt = 0x5668060aa49730b7be4801df46ec62de53ecd11abe43a32873000c36e8dc1f; +} + +extern type EcOp; +#[derive(Copy, Drop)] +extern type EcPoint; +type NonZeroEcPoint = NonZero::; + +impl NonZeroEcPointCopy of Copy::; +impl OptionNonZeroEcPointCopy of Copy::>; +impl NonZeroEcPointDrop of Drop::; + +/// Returns the zero point of the curve ("the point at infinity"). +extern fn ec_point_zero() -> EcPoint nopanic; +/// Constructs a non-zero point from its (x, y) coordinates. +/// +/// * `ec_point_try_new_nz` returns `None` if the point (x, y) is not on the curve. +/// * `ec_point_new_nz` panics in that case. +#[panic_with('not on EC', ec_point_new_nz)] +extern fn ec_point_try_new_nz(x: felt, y: felt) -> Option:: nopanic; + +#[inline(always)] +fn ec_point_try_new(x: felt, y: felt) -> Option:: { + match ec_point_try_new_nz(:x, :y) { + Option::Some(pt) => Option::Some(unwrap_nz(pt)), + Option::None(()) => Option::None(()), + } +} + +fn ec_point_new(x: felt, y: felt) -> EcPoint { + unwrap_nz(ec_point_new_nz(:x, :y)) +} + +extern fn ec_point_from_x_nz(x: felt) -> Option:: nopanic; + +#[inline(always)] +fn ec_point_from_x(x: felt) -> Option:: { + match ec_point_from_x_nz(:x) { + Option::Some(pt) => Option::Some(unwrap_nz(pt)), + Option::None(()) => Option::None(()), + } +} + +extern fn ec_point_unwrap(p: NonZeroEcPoint) -> (felt, felt) nopanic; +/// Computes the negation of an elliptic curve point (-p). +extern fn ec_neg(p: EcPoint) -> EcPoint nopanic; +/// Checks whether the given `EcPoint` is the zero point. +extern fn ec_point_is_zero(p: EcPoint) -> IsZeroResult:: nopanic; + +/// Converts `p` to `NonZeroEcPoint`. Panics if `p` is the zero point. +fn ec_point_non_zero(p: EcPoint) -> NonZeroEcPoint { + match ec_point_is_zero(p) { + IsZeroResult::Zero(()) => { + let mut data = array_new(); + array_append(ref data, 'Zero point'); + panic(data) + }, + IsZeroResult::NonZero(p_nz) => p_nz, + } +} + +// EC state. + +// TODO(lior): Allow explicit clone() for EcState, since we don't allow implicit dup (Copy). +#[derive(Drop)] +extern type EcState; + +/// Initializes an EC computation with the zero point. +extern fn ec_state_init() -> EcState nopanic; +/// Adds a point to the computation. +extern fn ec_state_add(ref s: EcState, p: NonZeroEcPoint) nopanic; +/// Finalizes the EC computation and returns the result (returns `None` if the result is the +/// zero point). +extern fn ec_state_try_finalize_nz(s: EcState) -> Option:: nopanic; +/// Adds the product p * m to the state. +extern fn ec_state_add_mul(ref s: EcState, m: felt, p: NonZeroEcPoint) implicits(EcOp) nopanic; + +/// Finalizes the EC computation and returns the result. +#[inline(always)] +fn ec_state_finalize(s: EcState) -> EcPoint nopanic { + match ec_state_try_finalize_nz(s) { + Option::Some(pt) => unwrap_nz(pt), + Option::None(()) => ec_point_zero(), + } +} + +/// Computes the product of an EC point `p` by the given scalar `m`. +fn ec_mul(p: EcPoint, m: felt) -> EcPoint { + match ec_point_is_zero(p) { + IsZeroResult::Zero(()) => p, + IsZeroResult::NonZero(p_nz) => { + let mut state = ec_state_init(); + ec_state_add_mul(ref state, m, p_nz); + ec_state_finalize(state) + } + } +} + +impl EcPointAdd of Add:: { + /// Computes the sum of two points on the curve. + // TODO(lior): Implement using a libfunc to make it more efficient. + fn add(p: EcPoint, q: EcPoint) -> EcPoint { + let p_nz = match ec_point_is_zero(p) { + IsZeroResult::Zero(()) => { + return q; + }, + IsZeroResult::NonZero(pt) => pt, + }; + let q_nz = match ec_point_is_zero(q) { + IsZeroResult::Zero(()) => { + return p; + }, + IsZeroResult::NonZero(pt) => pt, + }; + let mut state = ec_state_init(); + ec_state_add(ref state, p_nz); + ec_state_add(ref state, q_nz); + ec_state_finalize(state) + } +} + +impl EcPointSub of Sub:: { + /// Computes the difference between two points on the curve. + fn sub(p: EcPoint, q: EcPoint) -> EcPoint { + match ec_point_is_zero(q) { + IsZeroResult::Zero(()) => { + // p - 0 = p. + return p; + }, + IsZeroResult::NonZero(_) => {}, + }; + // p - q = p + (-q). + p + ec_neg(q) + } +} diff --git a/src/nile/core/cairo1/compilers/corelib/ecdsa.cairo b/src/nile/core/cairo1/compilers/corelib/ecdsa.cairo new file mode 100644 index 00000000..fb9c89c2 --- /dev/null +++ b/src/nile/core/cairo1/compilers/corelib/ecdsa.cairo @@ -0,0 +1,105 @@ +// TODO(lior): Remove this once the impl of ec.cairo is automatically found. +impl OptionEcPointCopy of Copy::>; + +// Checks if (`signature_r`, `signature_s`) is a valid ECDSA signature for the given `public_key` +// on the given `message`. +// +// Note: the verification algorithm implemented by this function slightly deviates from the +// standard ECDSA. +// While this does not allow to create valid signatures if one does not possess the private key, +// it means that the signature algorithm used should be modified accordingly. +// Namely, it should check that `r, s < StarkCurve::ORDER`. +// +// Arguments: +// * `message_hash` - the signed message. +// * `public_key` - the public key corresponding to the key with which the message was signed. +// * `signature_r` - the `r` component of the ECDSA signature. +// * `signature_s` - the `s` component of the ECDSA signature. +// +// Returns: +// `true` if the signature is valid and `false` otherwise. +// TODO(lior): Make this function nopanic once possible. +fn check_ecdsa_signature( + message_hash: felt, public_key: felt, signature_r: felt, signature_s: felt +) -> bool { + // TODO(lior): Change to || once short circuiting is supported. + + // Check that s != 0 (mod StarkCurve.ORDER). + if (signature_s == 0) { + return false; + } + if (signature_s == ec::StarkCurve::ORDER) { + return false; + } + if (signature_r == ec::StarkCurve::ORDER) { + return false; + } + + // Check that the public key is the x coordinate of a point on the curve and get such a point. + let public_key_point = match ec::ec_point_from_x(public_key) { + Option::Some(point) => point, + Option::None(()) => { + return false; + }, + }; + + // Check that `r` is the x coordinate of a point on the curve and get such a point. + // Note that this ensures that `r != 0`. + let signature_r_point = match ec::ec_point_from_x(signature_r) { + Option::Some(point) => point, + Option::None(()) => { + return false; + }, + }; + + // Retrieve the generator point. + let gen_point = match ec_point_try_new(ec::StarkCurve::GEN_X, ec::StarkCurve::GEN_Y) { + Option::Some(point) => point, + Option::None(()) => { + return false; + }, + }; + + // To verify ECDSA, obtain: + // zG = z * G, where z is the message and G is a generator of the EC. + // rQ = r * Q, where Q.x = public_key. + // sR = s * R, where R.x = r. + // and check that: + // zG +/- rQ = +/- sR, or more efficiently that: + // (zG +/- rQ).x = sR.x. + + let sR: EcPoint = ec_mul(signature_r_point, signature_s); + let sR_x = match ec_point_is_zero(sR) { + IsZeroResult::Zero(()) => { + return false; + }, + IsZeroResult::NonZero(pt) => { + let (x, y) = ec_point_unwrap(pt); + x + }, + }; + + let zG: EcPoint = ec_mul(gen_point, message_hash); + let rQ: EcPoint = ec_mul(public_key_point, signature_r); + match ec_point_is_zero(zG + rQ) { + IsZeroResult::Zero(()) => {}, + IsZeroResult::NonZero(pt) => { + let (x, y) = ec_point_unwrap(pt); + if (x == sR_x) { + return true; + } + }, + }; + + match ec_point_is_zero(zG - rQ) { + IsZeroResult::Zero(()) => {}, + IsZeroResult::NonZero(pt) => { + let (x, y) = ec_point_unwrap(pt); + if (x == sR_x) { + return true; + } + }, + }; + + return false; +} diff --git a/src/nile/core/cairo1/compilers/corelib/gas.cairo b/src/nile/core/cairo1/compilers/corelib/gas.cairo new file mode 100644 index 00000000..3a675ce3 --- /dev/null +++ b/src/nile/core/cairo1/compilers/corelib/gas.cairo @@ -0,0 +1,9 @@ +#[derive(Copy, Drop)] +extern type BuiltinCosts; +extern type GasBuiltin; + +extern fn get_gas() -> Option::<()> implicits(RangeCheck, GasBuiltin) nopanic; +extern fn get_gas_all( + costs: BuiltinCosts +) -> Option::<()> implicits(RangeCheck, GasBuiltin) nopanic; +extern fn get_builtin_costs() -> BuiltinCosts nopanic; diff --git a/src/nile/core/cairo1/compilers/corelib/hash.cairo b/src/nile/core/cairo1/compilers/corelib/hash.cairo new file mode 100644 index 00000000..b5018a6c --- /dev/null +++ b/src/nile/core/cairo1/compilers/corelib/hash.cairo @@ -0,0 +1,51 @@ +extern type Pedersen; + +extern fn pedersen(a: felt, b: felt) -> felt implicits(Pedersen) nopanic; + +trait LegacyHash { + fn hash(state: felt, value: T) -> felt; +} + +impl LegacyHashFelt of LegacyHash:: { + fn hash(state: felt, value: felt) -> felt { + pedersen(state, value) + } +} + +impl LegacyHashBool of LegacyHash:: { + fn hash(state: felt, value: bool) -> felt { + LegacyHash::::hash(state, if value { + 1 + } else { + 0 + }) + } +} + +impl LegacyHashU8 of LegacyHash:: { + fn hash(state: felt, value: u8) -> felt { + LegacyHash::::hash(state, u8_to_felt(value)) + } +} + +impl LegacyHashU128 of LegacyHash:: { + fn hash(state: felt, value: u128) -> felt { + LegacyHash::::hash(state, u128_to_felt(value)) + } +} + +impl LegacyHashU256 of LegacyHash:: { + fn hash(state: felt, value: u256) -> felt { + let state = LegacyHash::::hash(state, value.low); + LegacyHash::::hash(state, value.high) + } +} + +// TODO(orizi): Move to generic impl. +impl LegacyHashFeltPair of LegacyHash::<(felt, felt)> { + fn hash(state: felt, pair: (felt, felt)) -> felt { + let (first, second) = pair; + let state = LegacyHash::hash(state, first); + LegacyHash::hash(state, second) + } +} diff --git a/src/nile/core/cairo1/compilers/corelib/integer.cairo b/src/nile/core/cairo1/compilers/corelib/integer.cairo new file mode 100644 index 00000000..b7b6ed22 --- /dev/null +++ b/src/nile/core/cairo1/compilers/corelib/integer.cairo @@ -0,0 +1,815 @@ +use result::ResultTrait; +use result::ResultTraitImpl; +use option::OptionTrait; +use option::OptionTraitImpl; + +#[derive(Copy, Drop)] +extern type u128; +extern fn u128_const() -> u128 nopanic; + +enum U128sFromFeltResult { + Narrow: u128, + Wide: (u128, u128), +} +extern fn u128s_from_felt(a: felt) -> U128sFromFeltResult implicits(RangeCheck) nopanic; + +#[panic_with('u128_from OF', u128_from_felt)] +fn u128_try_from_felt(a: felt) -> Option:: implicits(RangeCheck) nopanic { + match u128s_from_felt(a) { + U128sFromFeltResult::Narrow(x) => Option::::Some(x), + U128sFromFeltResult::Wide(x) => Option::::None(()), + } +} + +extern fn u128_to_felt(a: u128) -> felt nopanic; + +extern fn u128_overflowing_add( + a: u128, b: u128 +) -> Result:: implicits(RangeCheck) nopanic; +extern fn u128_overflowing_sub( + a: u128, b: u128 +) -> Result:: implicits(RangeCheck) nopanic; + +fn u128_wrapping_add(a: u128, b: u128) -> u128 implicits(RangeCheck) nopanic { + match u128_overflowing_add(a, b) { + Result::Ok(x) => x, + Result::Err(x) => x, + } +} + +extern fn u128_wide_mul(a: u128, b: u128) -> (u128, u128) implicits(RangeCheck) nopanic; +extern fn u128_sqrt(value: u128) -> u128 implicits(RangeCheck) nopanic; + +fn u128_overflowing_mul(a: u128, b: u128) -> (u128, bool) implicits(RangeCheck) nopanic { + let (top_word, bottom_word) = u128_wide_mul(a, b); + match u128_to_felt(top_word) { + 0 => (bottom_word, false), + _ => (bottom_word, true), + } +} + + +fn u128_checked_add(a: u128, b: u128) -> Option:: implicits(RangeCheck) nopanic { + match u128_overflowing_add(a, b) { + Result::Ok(r) => Option::::Some(r), + Result::Err(r) => Option::::None(()), + } +} + +impl U128Add of Add:: { + fn add(a: u128, b: u128) -> u128 { + u128_overflowing_add(a, b).expect('u128_add Overflow') + } +} + +#[panic_with('u128_sub OF', u128_sub)] +fn u128_checked_sub(a: u128, b: u128) -> Option:: implicits(RangeCheck) nopanic { + match u128_overflowing_sub(a, b) { + Result::Ok(r) => Option::::Some(r), + Result::Err(r) => Option::::None(()), + } +} + +impl U128Sub of Sub:: { + fn sub(a: u128, b: u128) -> u128 { + u128_overflowing_sub(a, b).expect('u128_sub Overflow') + } +} + +fn u128_checked_mul(a: u128, b: u128) -> Option:: implicits(RangeCheck) nopanic { + let (top_word, bottom_word) = u128_wide_mul(a, b); + match u128_to_felt(top_word) { + 0 => Option::::Some(bottom_word), + _ => Option::::None(()), + } +} + +impl U128Mul of Mul:: { + fn mul(a: u128, b: u128) -> u128 { + u128_checked_mul(a, b).expect('u128_mul Overflow') + } +} + +impl NonZeroU128Copy of Copy::>; +impl NonZeroU128Drop of Drop::>; + +#[panic_with('u128 is 0', u128_as_non_zero)] +fn u128_try_as_non_zero(a: u128) -> Option::> implicits() nopanic { + match u128_is_zero(a) { + IsZeroResult::Zero(()) => Option::>::None(()), + IsZeroResult::NonZero(x) => Option::>::Some(x), + } +} + +impl U128Div of Div:: { + fn div(a: u128, b: u128) -> u128 { + let (q, r) = u128_safe_divmod(a, u128_as_non_zero(b)); + q + } +} + +impl U128Rem of Rem:: { + fn rem(a: u128, b: u128) -> u128 { + let (q, r) = u128_safe_divmod(a, u128_as_non_zero(b)); + r + } +} + +extern fn u128_safe_divmod( + a: u128, b: NonZero:: +) -> (u128, u128) implicits(RangeCheck) nopanic; + +extern fn u128_lt(a: u128, b: u128) -> bool implicits(RangeCheck) nopanic; +extern fn u128_eq(a: u128, b: u128) -> bool implicits() nopanic; +extern fn u128_le(a: u128, b: u128) -> bool implicits(RangeCheck) nopanic; + +impl U128PartialEq of PartialEq:: { + #[inline(always)] + fn eq(a: u128, b: u128) -> bool { + u128_eq(a, b) + } + #[inline(always)] + fn ne(a: u128, b: u128) -> bool { + !(a == b) + } +} + +impl U128PartialOrd of PartialOrd:: { + #[inline(always)] + fn le(a: u128, b: u128) -> bool { + u128_le(a, b) + } + #[inline(always)] + fn ge(a: u128, b: u128) -> bool { + u128_le(b, a) + } + #[inline(always)] + fn lt(a: u128, b: u128) -> bool { + u128_lt(a, b) + } + #[inline(always)] + fn gt(a: u128, b: u128) -> bool { + u128_lt(b, a) + } +} + +extern type Bitwise; +extern fn bitwise(a: u128, b: u128) -> (u128, u128, u128) implicits(Bitwise) nopanic; +impl U128BitAnd of BitAnd:: { + #[inline(always)] + fn bitand(a: u128, b: u128) -> u128 { + let (v, _, _) = bitwise(a, b); + v + } +} +impl U128BitXor of BitXor:: { + #[inline(always)] + fn bitxor(a: u128, b: u128) -> u128 { + let (_, v, _) = bitwise(a, b); + v + } +} +impl U128BitOr of BitOr:: { + #[inline(always)] + fn bitor(a: u128, b: u128) -> u128 { + let (_, _, v) = bitwise(a, b); + v + } +} + +extern fn u128_is_zero(a: u128) -> IsZeroResult:: implicits() nopanic; + +#[derive(Copy, Drop)] +extern type u8; +extern fn u8_const() -> u8 nopanic; +extern fn u8_to_felt(a: u8) -> felt nopanic; + +#[panic_with('u8_from OF', u8_from_felt)] +extern fn u8_try_from_felt(a: felt) -> Option:: implicits(RangeCheck) nopanic; + +extern fn u8_lt(a: u8, b: u8) -> bool implicits(RangeCheck) nopanic; +extern fn u8_eq(a: u8, b: u8) -> bool implicits() nopanic; +extern fn u8_le(a: u8, b: u8) -> bool implicits(RangeCheck) nopanic; + +impl U8PartialEq of PartialEq:: { + #[inline(always)] + fn eq(a: u8, b: u8) -> bool { + u8_eq(a, b) + } + #[inline(always)] + fn ne(a: u8, b: u8) -> bool { + !(a == b) + } +} + +impl U8PartialOrd of PartialOrd:: { + #[inline(always)] + fn le(a: u8, b: u8) -> bool { + u8_le(a, b) + } + #[inline(always)] + fn ge(a: u8, b: u8) -> bool { + u8_le(b, a) + } + #[inline(always)] + fn lt(a: u8, b: u8) -> bool { + u8_lt(a, b) + } + #[inline(always)] + fn gt(a: u8, b: u8) -> bool { + u8_lt(b, a) + } +} + +extern fn u8_overflowing_add(a: u8, b: u8) -> Result:: implicits(RangeCheck) nopanic; +extern fn u8_overflowing_sub(a: u8, b: u8) -> Result:: implicits(RangeCheck) nopanic; + +fn u8_wrapping_add(a: u8, b: u8) -> u8 implicits(RangeCheck) nopanic { + match u8_overflowing_add(a, b) { + Result::Ok(x) => x, + Result::Err(x) => x, + } +} + +fn u8_wrapping_sub(a: u8, b: u8) -> u8 implicits(RangeCheck) nopanic { + match u8_overflowing_sub(a, b) { + Result::Ok(x) => x, + Result::Err(x) => x, + } +} + +fn u8_checked_add(a: u8, b: u8) -> Option:: implicits(RangeCheck) nopanic { + match u8_overflowing_add(a, b) { + Result::Ok(r) => Option::::Some(r), + Result::Err(r) => Option::::None(()), + } +} + +impl U8Add of Add:: { + fn add(a: u8, b: u8) -> u8 { + u8_overflowing_add(a, b).expect('u8_add Overflow') + } +} + +fn u8_checked_sub(a: u8, b: u8) -> Option:: implicits(RangeCheck) nopanic { + match u8_overflowing_sub(a, b) { + Result::Ok(r) => Option::::Some(r), + Result::Err(r) => Option::::None(()), + } +} + +impl U8Sub of Sub:: { + fn sub(a: u8, b: u8) -> u8 { + u8_overflowing_sub(a, b).expect('u8_sub Overflow') + } +} + +extern fn u8_wide_mul(a: u8, b: u8) -> u16 implicits() nopanic; +impl U8Mul of Mul:: { + fn mul(a: u8, b: u8) -> u8 { + // TODO(orizi): Use direct conversion, instead of going through felt. + u8_try_from_felt(u16_to_felt(u8_wide_mul(a, b))).expect('u8_mul Overflow') + } +} + +extern fn u8_is_zero(a: u8) -> IsZeroResult:: implicits() nopanic; +extern fn u8_safe_divmod(a: u8, b: NonZero::) -> (u8, u8) implicits(RangeCheck) nopanic; + +#[panic_with('u8 is 0', u8_as_non_zero)] +fn u8_try_as_non_zero(a: u8) -> Option::> implicits() nopanic { + match u8_is_zero(a) { + IsZeroResult::Zero(()) => Option::None(()), + IsZeroResult::NonZero(x) => Option::Some(x), + } +} + +impl U8Div of Div:: { + fn div(a: u8, b: u8) -> u8 { + let (q, r) = u8_safe_divmod(a, u8_as_non_zero(b)); + q + } +} + +impl U8Rem of Rem:: { + fn rem(a: u8, b: u8) -> u8 { + let (q, r) = u8_safe_divmod(a, u8_as_non_zero(b)); + r + } +} + +#[derive(Copy, Drop)] +extern type u16; +extern fn u16_const() -> u16 nopanic; +extern fn u16_to_felt(a: u16) -> felt nopanic; + +#[panic_with('u16_from OF', u16_from_felt)] +extern fn u16_try_from_felt(a: felt) -> Option:: implicits(RangeCheck) nopanic; + +extern fn u16_lt(a: u16, b: u16) -> bool implicits(RangeCheck) nopanic; +extern fn u16_eq(a: u16, b: u16) -> bool implicits() nopanic; +extern fn u16_le(a: u16, b: u16) -> bool implicits(RangeCheck) nopanic; + +impl U16PartialEq of PartialEq:: { + #[inline(always)] + fn eq(a: u16, b: u16) -> bool { + u16_eq(a, b) + } + #[inline(always)] + fn ne(a: u16, b: u16) -> bool { + !(a == b) + } +} + +impl U16PartialOrd of PartialOrd:: { + #[inline(always)] + fn le(a: u16, b: u16) -> bool { + u16_le(a, b) + } + #[inline(always)] + fn ge(a: u16, b: u16) -> bool { + u16_le(b, a) + } + #[inline(always)] + fn lt(a: u16, b: u16) -> bool { + u16_lt(a, b) + } + #[inline(always)] + fn gt(a: u16, b: u16) -> bool { + u16_lt(b, a) + } +} + +extern fn u16_overflowing_add(a: u16, b: u16) -> Result:: implicits(RangeCheck) nopanic; +extern fn u16_overflowing_sub(a: u16, b: u16) -> Result:: implicits(RangeCheck) nopanic; + +fn u16_wrapping_add(a: u16, b: u16) -> u16 implicits(RangeCheck) nopanic { + match u16_overflowing_add(a, b) { + Result::Ok(x) => x, + Result::Err(x) => x, + } +} + +fn u16_wrapping_sub(a: u16, b: u16) -> u16 implicits(RangeCheck) nopanic { + match u16_overflowing_sub(a, b) { + Result::Ok(x) => x, + Result::Err(x) => x, + } +} + +fn u16_checked_add(a: u16, b: u16) -> Option:: implicits(RangeCheck) nopanic { + match u16_overflowing_add(a, b) { + Result::Ok(r) => Option::::Some(r), + Result::Err(r) => Option::::None(()), + } +} + +impl U16Add of Add:: { + fn add(a: u16, b: u16) -> u16 { + u16_overflowing_add(a, b).expect('u16_add Overflow') + } +} + +fn u16_checked_sub(a: u16, b: u16) -> Option:: implicits(RangeCheck) nopanic { + match u16_overflowing_sub(a, b) { + Result::Ok(r) => Option::::Some(r), + Result::Err(r) => Option::::None(()), + } +} + +impl U16Sub of Sub:: { + fn sub(a: u16, b: u16) -> u16 { + u16_overflowing_sub(a, b).expect('u16_sub Overflow') + } +} + +extern fn u16_wide_mul(a: u16, b: u16) -> u32 implicits() nopanic; +impl U16Mul of Mul:: { + fn mul(a: u16, b: u16) -> u16 { + // TODO(orizi): Use direct conversion, instead of going through felt. + u16_try_from_felt(u32_to_felt(u16_wide_mul(a, b))).expect('u16_mul Overflow') + } +} + +extern fn u16_is_zero(a: u16) -> IsZeroResult:: implicits() nopanic; +extern fn u16_safe_divmod(a: u16, b: NonZero::) -> (u16, u16) implicits(RangeCheck) nopanic; + +#[panic_with('u16 is 0', u16_as_non_zero)] +fn u16_try_as_non_zero(a: u16) -> Option::> implicits() nopanic { + match u16_is_zero(a) { + IsZeroResult::Zero(()) => Option::None(()), + IsZeroResult::NonZero(x) => Option::Some(x), + } +} + +impl U16Div of Div:: { + fn div(a: u16, b: u16) -> u16 { + let (q, r) = u16_safe_divmod(a, u16_as_non_zero(b)); + q + } +} + +impl U16Rem of Rem:: { + fn rem(a: u16, b: u16) -> u16 { + let (q, r) = u16_safe_divmod(a, u16_as_non_zero(b)); + r + } +} + +#[derive(Copy, Drop)] +extern type u32; +extern fn u32_const() -> u32 nopanic; +extern fn u32_to_felt(a: u32) -> felt nopanic; + +#[panic_with('u32_from OF', u32_from_felt)] +extern fn u32_try_from_felt(a: felt) -> Option:: implicits(RangeCheck) nopanic; + +extern fn u32_lt(a: u32, b: u32) -> bool implicits(RangeCheck) nopanic; +extern fn u32_eq(a: u32, b: u32) -> bool implicits() nopanic; +extern fn u32_le(a: u32, b: u32) -> bool implicits(RangeCheck) nopanic; + +impl U32PartialEq of PartialEq:: { + #[inline(always)] + fn eq(a: u32, b: u32) -> bool { + u32_eq(a, b) + } + #[inline(always)] + fn ne(a: u32, b: u32) -> bool { + !(a == b) + } +} + +impl U32PartialOrd of PartialOrd:: { + #[inline(always)] + fn le(a: u32, b: u32) -> bool { + u32_le(a, b) + } + #[inline(always)] + fn ge(a: u32, b: u32) -> bool { + u32_le(b, a) + } + #[inline(always)] + fn lt(a: u32, b: u32) -> bool { + u32_lt(a, b) + } + #[inline(always)] + fn gt(a: u32, b: u32) -> bool { + u32_lt(b, a) + } +} + +extern fn u32_overflowing_add(a: u32, b: u32) -> Result:: implicits(RangeCheck) nopanic; +extern fn u32_overflowing_sub(a: u32, b: u32) -> Result:: implicits(RangeCheck) nopanic; + +fn u32_wrapping_add(a: u32, b: u32) -> u32 implicits(RangeCheck) nopanic { + match u32_overflowing_add(a, b) { + Result::Ok(x) => x, + Result::Err(x) => x, + } +} + +fn u32_wrapping_sub(a: u32, b: u32) -> u32 implicits(RangeCheck) nopanic { + match u32_overflowing_sub(a, b) { + Result::Ok(x) => x, + Result::Err(x) => x, + } +} + +fn u32_checked_add(a: u32, b: u32) -> Option:: implicits(RangeCheck) nopanic { + match u32_overflowing_add(a, b) { + Result::Ok(r) => Option::::Some(r), + Result::Err(r) => Option::::None(()), + } +} + +impl U32Add of Add:: { + fn add(a: u32, b: u32) -> u32 { + u32_overflowing_add(a, b).expect('u32_add Overflow') + } +} + +fn u32_checked_sub(a: u32, b: u32) -> Option:: implicits(RangeCheck) nopanic { + match u32_overflowing_sub(a, b) { + Result::Ok(r) => Option::::Some(r), + Result::Err(r) => Option::::None(()), + } +} + +impl U32Sub of Sub:: { + fn sub(a: u32, b: u32) -> u32 { + u32_overflowing_sub(a, b).expect('u32_sub Overflow') + } +} + +extern fn u32_wide_mul(a: u32, b: u32) -> u64 implicits() nopanic; +impl U32Mul of Mul:: { + fn mul(a: u32, b: u32) -> u32 { + // TODO(orizi): Use direct conversion, instead of going through felt. + u32_try_from_felt(u64_to_felt(u32_wide_mul(a, b))).expect('u32_mul Overflow') + } +} + +extern fn u32_is_zero(a: u32) -> IsZeroResult:: implicits() nopanic; +extern fn u32_safe_divmod(a: u32, b: NonZero::) -> (u32, u32) implicits(RangeCheck) nopanic; + +#[panic_with('u32 is 0', u32_as_non_zero)] +fn u32_try_as_non_zero(a: u32) -> Option::> implicits() nopanic { + match u32_is_zero(a) { + IsZeroResult::Zero(()) => Option::None(()), + IsZeroResult::NonZero(x) => Option::Some(x), + } +} + +impl U32Div of Div:: { + fn div(a: u32, b: u32) -> u32 { + let (q, r) = u32_safe_divmod(a, u32_as_non_zero(b)); + q + } +} + +impl U32Rem of Rem:: { + fn rem(a: u32, b: u32) -> u32 { + let (q, r) = u32_safe_divmod(a, u32_as_non_zero(b)); + r + } +} + +#[derive(Copy, Drop)] +extern type u64; +extern fn u64_const() -> u64 nopanic; +extern fn u64_to_felt(a: u64) -> felt nopanic; + +#[panic_with('u64_from OF', u64_from_felt)] +extern fn u64_try_from_felt(a: felt) -> Option:: implicits(RangeCheck) nopanic; + +extern fn u64_lt(a: u64, b: u64) -> bool implicits(RangeCheck) nopanic; +extern fn u64_eq(a: u64, b: u64) -> bool implicits() nopanic; +extern fn u64_le(a: u64, b: u64) -> bool implicits(RangeCheck) nopanic; + +impl U64PartialEq of PartialEq:: { + #[inline(always)] + fn eq(a: u64, b: u64) -> bool { + u64_eq(a, b) + } + #[inline(always)] + fn ne(a: u64, b: u64) -> bool { + !(a == b) + } +} + +impl U64PartialOrd of PartialOrd:: { + #[inline(always)] + fn le(a: u64, b: u64) -> bool { + u64_le(a, b) + } + #[inline(always)] + fn ge(a: u64, b: u64) -> bool { + u64_le(b, a) + } + #[inline(always)] + fn lt(a: u64, b: u64) -> bool { + u64_lt(a, b) + } + #[inline(always)] + fn gt(a: u64, b: u64) -> bool { + u64_lt(b, a) + } +} + +extern fn u64_overflowing_add(a: u64, b: u64) -> Result:: implicits(RangeCheck) nopanic; +extern fn u64_overflowing_sub(a: u64, b: u64) -> Result:: implicits(RangeCheck) nopanic; + +fn u64_wrapping_add(a: u64, b: u64) -> u64 implicits(RangeCheck) nopanic { + match u64_overflowing_add(a, b) { + Result::Ok(x) => x, + Result::Err(x) => x, + } +} + +fn u64_wrapping_sub(a: u64, b: u64) -> u64 implicits(RangeCheck) nopanic { + match u64_overflowing_sub(a, b) { + Result::Ok(x) => x, + Result::Err(x) => x, + } +} + +fn u64_checked_add(a: u64, b: u64) -> Option:: implicits(RangeCheck) nopanic { + match u64_overflowing_add(a, b) { + Result::Ok(r) => Option::::Some(r), + Result::Err(r) => Option::::None(()), + } +} + +impl U64Add of Add:: { + fn add(a: u64, b: u64) -> u64 { + u64_overflowing_add(a, b).expect('u64_add Overflow') + } +} + +fn u64_checked_sub(a: u64, b: u64) -> Option:: implicits(RangeCheck) nopanic { + match u64_overflowing_sub(a, b) { + Result::Ok(r) => Option::::Some(r), + Result::Err(r) => Option::::None(()), + } +} + +impl U64Sub of Sub:: { + fn sub(a: u64, b: u64) -> u64 { + u64_overflowing_sub(a, b).expect('u64_sub Overflow') + } +} + +extern fn u64_wide_mul(a: u64, b: u64) -> u128 implicits() nopanic; +impl U64Mul of Mul:: { + fn mul(a: u64, b: u64) -> u64 { + // TODO(orizi): Use direct conversion, instead of going through felt. + u64_try_from_felt(u128_to_felt(u64_wide_mul(a, b))).expect('u64_mul Overflow') + } +} + +extern fn u64_is_zero(a: u64) -> IsZeroResult:: implicits() nopanic; +extern fn u64_safe_divmod(a: u64, b: NonZero::) -> (u64, u64) implicits(RangeCheck) nopanic; + +#[panic_with('u64 is 0', u64_as_non_zero)] +fn u64_try_as_non_zero(a: u64) -> Option::> implicits() nopanic { + match u64_is_zero(a) { + IsZeroResult::Zero(()) => Option::None(()), + IsZeroResult::NonZero(x) => Option::Some(x), + } +} + +impl U64Div of Div:: { + fn div(a: u64, b: u64) -> u64 { + let (q, r) = u64_safe_divmod(a, u64_as_non_zero(b)); + q + } +} + +impl U64Rem of Rem:: { + fn rem(a: u64, b: u64) -> u64 { + let (q, r) = u64_safe_divmod(a, u64_as_non_zero(b)); + r + } +} + +#[derive(Copy, Drop)] +struct u256 { + low: u128, + high: u128, +} + +fn u256_overflowing_add(a: u256, b: u256) -> (u256, bool) implicits(RangeCheck) nopanic { + let (high, overflow) = match u128_overflowing_add(a.high, b.high) { + Result::Ok(high) => (high, false), + Result::Err(high) => (high, true), + }; + match u128_overflowing_add(a.low, b.low) { + Result::Ok(low) => (u256 { low, high }, overflow), + Result::Err(low) => { + match u128_overflowing_add(high, 1_u128) { + Result::Ok(high) => (u256 { low, high }, overflow), + Result::Err(high) => (u256 { low, high }, true), + } + }, + } +} + +fn u256_overflow_sub(a: u256, b: u256) -> (u256, bool) implicits(RangeCheck) nopanic { + let (high, overflow) = match u128_overflowing_sub(a.high, b.high) { + Result::Ok(high) => (high, false), + Result::Err(high) => (high, true), + }; + match u128_overflowing_sub(a.low, b.low) { + Result::Ok(low) => (u256 { low, high }, overflow), + Result::Err(low) => { + match u128_overflowing_sub(high, 1_u128) { + Result::Ok(high) => (u256 { low, high }, overflow), + Result::Err(high) => (u256 { low, high }, true), + } + }, + } +} + +fn u256_overflow_mul(a: u256, b: u256) -> (u256, bool) { + let (high1, low) = u128_wide_mul(a.low, b.low); + let (overflow_value1, high2) = u128_wide_mul(a.low, b.high); + let (overflow_value2, high3) = u128_wide_mul(a.high, b.low); + let (high, overflow) = match u128_overflowing_add(high1, high2) { + Result::Ok(high) => ( + high, + overflow_value1 != 0_u128 | overflow_value2 != 0_u128 | (a.high > 0_u128 & b.high > 0_u128) + ), + Result::Err(high) => (high, true), + }; + let (high, overflow) = match u128_overflowing_add(high, high3) { + Result::Ok(high) => (high, overflow), + Result::Err(high) => (high, true), + }; + (u256 { low, high }, overflow) +} + +fn u256_checked_add(a: u256, b: u256) -> Option:: implicits(RangeCheck) nopanic { + let (r, overflow) = u256_overflowing_add(a, b); + if overflow { + Option::::None(()) + } else { + Option::::Some(r) + } +} + +impl U256Add of Add:: { + fn add(a: u256, b: u256) -> u256 { + u256_checked_add(a, b).expect('u256_add Overflow') + } +} + +#[panic_with('u256_sub OF', u256_sub)] +fn u256_checked_sub(a: u256, b: u256) -> Option:: implicits(RangeCheck) nopanic { + let (r, overflow) = u256_overflow_sub(a, b); + if overflow { + Option::::None(()) + } else { + Option::::Some(r) + } +} + +impl U256Sub of Sub:: { + fn sub(a: u256, b: u256) -> u256 { + u256_checked_sub(a, b).expect('u256_sub Overflow') + } +} + +fn u256_checked_mul(a: u256, b: u256) -> Option:: implicits(RangeCheck) { + let (r, overflow) = u256_overflow_mul(a, b); + if overflow { + Option::::None(()) + } else { + Option::::Some(r) + } +} + +impl U256Mul of Mul:: { + fn mul(a: u256, b: u256) -> u256 { + u256_checked_mul(a, b).expect('u256_mul Overflow') + } +} + +impl U256PartialEq of PartialEq:: { + #[inline(always)] + fn eq(a: u256, b: u256) -> bool { + a.low == b.low & a.high == b.high + } + #[inline(always)] + fn ne(a: u256, b: u256) -> bool { + !(a == b) + } +} + +impl U256PartialOrd of PartialOrd:: { + #[inline(always)] + fn le(a: u256, b: u256) -> bool { + !(b < a) + } + #[inline(always)] + fn ge(a: u256, b: u256) -> bool { + !(a < b) + } + fn lt(a: u256, b: u256) -> bool { + if a.high < b.high { + true + } else if a.high == b.high { + a.low < b.low + } else { + false + } + } + #[inline(always)] + fn gt(a: u256, b: u256) -> bool { + b < a + } +} + +impl U256BitAnd of BitAnd:: { + #[inline(always)] + fn bitand(a: u256, b: u256) -> u256 { + u256 { low: a.low & b.low, high: a.high & b.high } + } +} +impl U256BitXor of BitXor:: { + #[inline(always)] + fn bitxor(a: u256, b: u256) -> u256 { + u256 { low: a.low ^ b.low, high: a.high ^ b.high } + } +} +impl U256BitOr of BitOr:: { + #[inline(always)] + fn bitor(a: u256, b: u256) -> u256 { + u256 { low: a.low | b.low, high: a.high | b.high } + } +} + +fn u256_from_felt(a: felt) -> u256 implicits(RangeCheck) nopanic { + match u128s_from_felt(a) { + U128sFromFeltResult::Narrow(low) => u256 { low, high: 0_u128 }, + U128sFromFeltResult::Wide((high, low)) => u256 { low, high }, + } +} diff --git a/src/nile/core/cairo1/compilers/corelib/internal.cairo b/src/nile/core/cairo1/compilers/corelib/internal.cairo new file mode 100644 index 00000000..b63ad574 --- /dev/null +++ b/src/nile/core/cairo1/compilers/corelib/internal.cairo @@ -0,0 +1 @@ +extern fn revoke_ap_tracking() implicits() nopanic; diff --git a/src/nile/core/cairo1/compilers/corelib/lib.cairo b/src/nile/core/cairo1/compilers/corelib/lib.cairo new file mode 100644 index 00000000..998b6cc7 --- /dev/null +++ b/src/nile/core/cairo1/compilers/corelib/lib.cairo @@ -0,0 +1,368 @@ +mod traits; +use traits::Add; +use traits::BitAnd; +use traits::BitOr; +use traits::BitXor; +use traits::Copy; +use traits::Div; +use traits::Drop; +use traits::Mul; +use traits::PartialEq; +use traits::PartialOrd; +use traits::Rem; +use traits::Sub; +use traits::ToBool; + +#[derive(Copy, Drop)] +enum bool { + False: (), + True: (), +} + +extern fn bool_and_impl(a: bool, b: bool) -> (bool, ) implicits() nopanic; +impl BoolBitAnd of BitAnd:: { + #[inline(always)] + fn bitand(a: bool, b: bool) -> bool { + let (r, ) = bool_and_impl(a, b); + r + } +} + +extern fn bool_or_impl(a: bool, b: bool) -> (bool, ) implicits() nopanic; +impl BoolBitOr of BitOr:: { + #[inline(always)] + fn bitor(a: bool, b: bool) -> bool { + let (r, ) = bool_or_impl(a, b); + r + } +} + +extern fn bool_not_impl(a: bool) -> (bool, ) implicits() nopanic; +#[inline(always)] +fn bool_not(a: bool) -> bool implicits() nopanic { + let (r, ) = bool_not_impl(a); + r +} + +extern fn bool_xor_impl(a: bool, b: bool) -> (bool, ) implicits() nopanic; +impl BoolBitXor of BitXor:: { + #[inline(always)] + fn bitxor(a: bool, b: bool) -> bool { + let (r, ) = bool_xor_impl(a, b); + r + } +} + +extern fn bool_eq(a: bool, b: bool) -> bool implicits() nopanic; + +impl BoolPartialEq of PartialEq:: { + #[inline(always)] + fn eq(a: bool, b: bool) -> bool { + bool_eq(a, b) + } + #[inline(always)] + fn ne(a: bool, b: bool) -> bool { + !(a == b) + } +} + +// Felt. +extern type RangeCheck; + +#[derive(Copy, Drop)] +extern type felt; +extern fn felt_const() -> felt nopanic; + +// TODO(spapini): Make unnamed. +impl FeltCopy of Copy::; +impl FeltDrop of Drop::; + +impl FeltAdd of Add:: { + #[inline(always)] + fn add(a: felt, b: felt) -> felt { + felt_add(a, b) + } +} +extern fn felt_add(a: felt, b: felt) -> felt nopanic; +impl FeltSub of Sub:: { + #[inline(always)] + fn sub(a: felt, b: felt) -> felt { + felt_sub(a, b) + } +} +extern fn felt_sub(a: felt, b: felt) -> felt nopanic; +impl FeltMul of Mul:: { + #[inline(always)] + fn mul(a: felt, b: felt) -> felt { + felt_mul(a, b) + } +} +extern fn felt_mul(a: felt, b: felt) -> felt nopanic; +#[inline(always)] +fn felt_neg(a: felt) -> felt { + a * felt_const::<-1>() +} + +extern type NonZero; +// TODO(spapini): Add generic impls for NonZero for Copy, Drop. +enum IsZeroResult { + Zero: (), + NonZero: NonZero::, +} +extern fn unwrap_nz(a: NonZero::) -> T nopanic; + +impl IsZeroResultToBool of ToBool::> { + fn to_bool(self: IsZeroResult::) -> bool { + match self { + IsZeroResult::Zero(()) => true, + IsZeroResult::NonZero(_) => false, + } + } +} + +impl NonZeroFeltCopy of Copy::>; +impl NonZeroFeltDrop of Drop::>; +extern fn felt_div(a: felt, b: NonZero::) -> felt nopanic; + +impl FeltPartialEq of PartialEq:: { + #[inline(always)] + fn eq(a: felt, b: felt) -> bool { + match a - b { + 0 => bool::True(()), + _ => bool::False(()), + } + } + #[inline(always)] + fn ne(a: felt, b: felt) -> bool { + !(a == b) + } +} + +impl PartialOrdFelt of PartialOrd:: { + #[inline(always)] + fn le(a: felt, b: felt) -> bool { + !(b < a) + } + #[inline(always)] + fn ge(a: felt, b: felt) -> bool { + !(a < b) + } + #[inline(always)] + fn lt(a: felt, b: felt) -> bool { + u256_from_felt(a) < u256_from_felt(b) + } + #[inline(always)] + fn gt(a: felt, b: felt) -> bool { + b < a + } +} + +extern fn felt_is_zero(a: felt) -> IsZeroResult:: nopanic; + +// TODO(spapini): Constraint using Copy and Drop traits. +extern fn dup(obj: T) -> (T, T) nopanic; +extern fn drop(obj: T) nopanic; + +// Boxes. +mod box; +use box::Box; +use box::into_box; +use box::unbox; + +// Nullable +mod nullable; +use nullable::FromNullableResult; +use nullable::Nullable; +use nullable::null; +use nullable::into_nullable; +use nullable::from_nullable; + +// Arrays. +mod array; +use array::Array; +use array::array_new; +use array::array_append; +use array::array_pop_front; +use array::array_get; +use array::array_at; +use array::array_len; +use array::ArrayTrait; +use array::ArrayImpl; +impl ArrayFeltDrop of Drop::>; +type usize = u32; + +// Dictionary. +mod dict; +use dict::DictFeltTo; +use dict::SquashedDictFeltTo; +use dict::dict_felt_to_new; +use dict::dict_felt_to_write; +use dict::dict_felt_to_read; +use dict::dict_felt_to_squash; +use dict::DictFeltToTrait; +use dict::DictFeltToImpl; + +// Result. +mod result; +use result::Result; + +// Option. +mod option; +use option::Option; +use option::OptionUnitCopy; +use option::OptionUnitDrop; + +// EC. +mod ec; +use ec::EcOp; +use ec::EcPoint; +use ec::EcPointAdd; +use ec::EcPointSub; +use ec::EcState; +use ec::NonZeroEcPoint; +use ec::NonZeroEcPointCopy; +use ec::OptionNonZeroEcPointCopy; +use ec::ec_mul; +use ec::ec_neg; +use ec::ec_point_from_x; +use ec::ec_point_from_x_nz; +use ec::ec_point_is_zero; +use ec::ec_point_new; +use ec::ec_point_new_nz; +use ec::ec_point_non_zero; +use ec::ec_point_try_new; +use ec::ec_point_try_new_nz; +use ec::ec_point_unwrap; +use ec::ec_point_zero; +use ec::ec_state_add_mul; +use ec::ec_state_add; +use ec::ec_state_finalize; +use ec::ec_state_init; +use ec::ec_state_try_finalize_nz; + +mod ecdsa; + +// Integer. +mod integer; +use integer::u128; +use integer::u128_const; +use integer::u128_from_felt; +use integer::u128_try_from_felt; +use integer::u128_to_felt; +use integer::u128_sqrt; +use integer::U128Add; +use integer::U128Sub; +use integer::U128Mul; +use integer::U128Div; +use integer::U128Rem; +use integer::U128PartialOrd; +use integer::U128PartialEq; +use integer::U128BitAnd; +use integer::U128BitOr; +use integer::U128BitXor; +use integer::u128_is_zero; +use integer::u8; +use integer::u8_const; +use integer::u8_from_felt; +use integer::u8_try_from_felt; +use integer::u8_to_felt; +use integer::U8Add; +use integer::U8Div; +use integer::U8PartialEq; +use integer::U8PartialOrd; +use integer::U8Rem; +use integer::U8Sub; +use integer::U8Mul; +use integer::u16; +use integer::u16_const; +use integer::u16_from_felt; +use integer::u16_try_from_felt; +use integer::u16_to_felt; +use integer::U16Add; +use integer::U16Div; +use integer::U16PartialEq; +use integer::U16PartialOrd; +use integer::U16Rem; +use integer::U16Sub; +use integer::U16Mul; +use integer::u32; +use integer::u32_const; +use integer::u32_from_felt; +use integer::u32_try_from_felt; +use integer::u32_to_felt; +use integer::U32Add; +use integer::U32Div; +use integer::U32PartialEq; +use integer::U32PartialOrd; +use integer::U32Rem; +use integer::U32Sub; +use integer::U32Mul; +use integer::u64; +use integer::u64_const; +use integer::u64_from_felt; +use integer::u64_try_from_felt; +use integer::u64_to_felt; +use integer::U64Add; +use integer::U64Div; +use integer::U64PartialEq; +use integer::U64PartialOrd; +use integer::U64Rem; +use integer::U64Sub; +use integer::U64Mul; +use integer::u256; +use integer::U256Add; +use integer::U256Sub; +use integer::U256Mul; +use integer::U256PartialOrd; +use integer::U256PartialEq; +use integer::U256BitAnd; +use integer::U256BitOr; +use integer::U256BitXor; +use integer::u256_from_felt; +use integer::Bitwise; + +// Gas. +mod gas; +use gas::BuiltinCosts; +use gas::GasBuiltin; +use gas::get_builtin_costs; +use gas::get_gas; +use gas::get_gas_all; + +// Panics. +enum PanicResult { + Ok: T, + Err: Array::, +} +enum never {} +extern fn panic(data: Array::) -> never; + +fn assert(cond: bool, err_code: felt) { + if !cond { + let mut data = ArrayTrait::new(); + data.append(err_code); + panic(data); + } +} + +// Serialization and Deserialization. +mod serde; + +// Hash functions. +mod hash; +use hash::pedersen; +use hash::Pedersen; + +// Debug. +mod debug; + +// StarkNet +mod starknet; +use starknet::System; +use starknet::ContractAddress; + +// Internals. +mod internal; + +#[cfg(test)] +mod test; diff --git a/src/nile/core/cairo1/compilers/corelib/nullable.cairo b/src/nile/core/cairo1/compilers/corelib/nullable.cairo new file mode 100644 index 00000000..7d2089cb --- /dev/null +++ b/src/nile/core/cairo1/compilers/corelib/nullable.cairo @@ -0,0 +1,10 @@ +extern type Nullable; + +enum FromNullableResult { + Null: (), + NotNull: Box::, +} + +extern fn null() -> Nullable:: nopanic; +extern fn into_nullable(value: Box::) -> Nullable:: nopanic; +extern fn from_nullable(value: Nullable::) -> FromNullableResult:: nopanic; diff --git a/src/nile/core/cairo1/compilers/corelib/option.cairo b/src/nile/core/cairo1/compilers/corelib/option.cairo new file mode 100644 index 00000000..f401ebb3 --- /dev/null +++ b/src/nile/core/cairo1/compilers/corelib/option.cairo @@ -0,0 +1,47 @@ +use array::ArrayTrait; + +enum Option { + Some: T, + None: (), +} +trait OptionTrait { + /// If `val` is `Option::Some(x)`, returns `x`. Otherwise, panics with `err`. + fn expect(self: Option::, err: felt) -> T; + /// If `val` is `Option::Some(x)`, returns `x`. Otherwise, panics. + fn unwrap(self: Option::) -> T; + /// Returns `true` if the `Option` is `Option::Some`. + fn is_some(self: Option::) -> bool; + /// Returns `true` if the `Option` is `Option::None`. + fn is_none(self: Option::) -> bool; +} +impl OptionTraitImpl of OptionTrait:: { + fn expect(self: Option::, err: felt) -> T { + match self { + Option::Some(x) => x, + Option::None(()) => { + let mut data = ArrayTrait::new(); + data.append(err); + panic(data) + }, + } + } + fn unwrap(self: Option::) -> T { + self.expect('Option::unwrap failed.') + } + fn is_some(self: Option::) -> bool { + match self { + Option::Some(_) => true, + Option::None(_) => false, + } + } + fn is_none(self: Option::) -> bool { + match self { + Option::Some(_) => false, + Option::None(_) => true, + } + } +} + +// Impls for common generic types +impl OptionUnitCopy of Copy::>; +impl OptionUnitDrop of Drop::>; diff --git a/src/nile/core/cairo1/compilers/corelib/result.cairo b/src/nile/core/cairo1/compilers/corelib/result.cairo new file mode 100644 index 00000000..62a5f8d5 --- /dev/null +++ b/src/nile/core/cairo1/compilers/corelib/result.cairo @@ -0,0 +1,59 @@ +use array::ArrayTrait; +enum Result { + Ok: T, + Err: E, +} +trait ResultTrait { + /// If `val` is `Result::Ok(x)`, returns `x`. Otherwise, panics with `err`. + fn expect(self: Result::, err: felt) -> T; + /// If `val` is `Result::Ok(x)`, returns `x`. Otherwise, panics. + fn unwrap(self: Result::) -> T; + /// If `val` is `Result::Err(x)`, returns `x`. Otherwise, panics with `err`. + fn expect_err(self: Result::, err: felt) -> E; + /// If `val` is `Result::Err(x)`, returns `x`. Otherwise, panics. + fn unwrap_err(self: Result::) -> E; + /// Returns `true` if the `Result` is `Result::Ok`. + fn is_ok(self: Result::) -> bool; + /// Returns `true` if the `Result` is `Result::Err`. + fn is_err(self: Result::) -> bool; +} +impl ResultTraitImpl of ResultTrait:: { + fn expect(self: Result::, err: felt) -> T { + match self { + Result::Ok(x) => x, + Result::Err(_) => { + let mut data = ArrayTrait::new(); + data.append(err); + panic(data) + }, + } + } + fn unwrap(self: Result::) -> T { + self.expect('Result::unwrap failed.') + } + fn expect_err(self: Result::, err: felt) -> E { + match self { + Result::Ok(_) => { + let mut data = ArrayTrait::new(); + data.append(err); + panic(data) + }, + Result::Err(x) => x, + } + } + fn unwrap_err(self: Result::) -> E { + self.expect_err('Result::unwrap_err failed.') + } + fn is_ok(self: Result::) -> bool { + match self { + Result::Ok(_) => true, + Result::Err(_) => false, + } + } + fn is_err(self: Result::) -> bool { + match self { + Result::Ok(_) => false, + Result::Err(_) => true, + } + } +} diff --git a/src/nile/core/cairo1/compilers/corelib/serde.cairo b/src/nile/core/cairo1/compilers/corelib/serde.cairo new file mode 100644 index 00000000..196e7d5a --- /dev/null +++ b/src/nile/core/cairo1/compilers/corelib/serde.cairo @@ -0,0 +1,128 @@ +use array::ArrayTrait; +trait Serde { + fn serialize(ref serialized: Array::, input: T); + fn deserialize(ref serialized: Array::) -> Option::; +} + +impl FeltSerde of Serde:: { + fn serialize(ref serialized: Array::, input: felt) { + serialized.append(input); + } + fn deserialize(ref serialized: Array::) -> Option:: { + serialized.pop_front() + } +} + +impl BoolSerde of Serde:: { + fn serialize(ref serialized: Array::, input: bool) { + Serde::::serialize(ref serialized, if input { + 1 + } else { + 0 + }); + } + fn deserialize(ref serialized: Array::) -> Option:: { + Option::Some(Serde::::deserialize(ref serialized)? != 0) + } +} + +impl U8Serde of Serde:: { + fn serialize(ref serialized: Array::, input: u8) { + Serde::::serialize(ref serialized, u8_to_felt(input)); + } + fn deserialize(ref serialized: Array::) -> Option:: { + Option::Some(u8_try_from_felt(Serde::::deserialize(ref serialized)?)?) + } +} + +impl U32Serde of Serde:: { + fn serialize(ref serialized: Array::, input: u32) { + Serde::::serialize(ref serialized, u32_to_felt(input)); + } + fn deserialize(ref serialized: Array::) -> Option:: { + Option::Some(u32_try_from_felt(Serde::::deserialize(ref serialized)?)?) + } +} + +impl U64Serde of Serde:: { + fn serialize(ref serialized: Array::, input: u64) { + Serde::::serialize(ref serialized, u64_to_felt(input)); + } + fn deserialize(ref serialized: Array::) -> Option:: { + Option::Some(u64_try_from_felt(Serde::::deserialize(ref serialized)?)?) + } +} + +impl U128Serde of Serde:: { + fn serialize(ref serialized: Array::, input: u128) { + Serde::::serialize(ref serialized, u128_to_felt(input)); + } + fn deserialize(ref serialized: Array::) -> Option:: { + Option::Some(u128_try_from_felt(Serde::::deserialize(ref serialized)?)?) + } +} + +impl U256Serde of Serde:: { + fn serialize(ref serialized: Array::, input: u256) { + Serde::::serialize(ref serialized, input.low); + Serde::::serialize(ref serialized, input.high); + } + fn deserialize(ref serialized: Array::) -> Option:: { + Option::Some( + u256 { + low: Serde::::deserialize(ref serialized)?, + high: Serde::::deserialize(ref serialized)?, + } + ) + } +} + +impl ArrayFeltSerde of Serde::> { + fn serialize(ref serialized: Array::, mut input: Array::) { + Serde::::serialize(ref serialized, input.len()); + serialize_array_felt_helper(ref serialized, ref input); + } + fn deserialize(ref serialized: Array::) -> Option::> { + let length = Serde::::deserialize(ref serialized)?; + let mut arr = ArrayTrait::new(); + deserialize_array_felt_helper(ref serialized, arr, length) + } +} + +fn serialize_array_felt_helper(ref serialized: Array::, ref input: Array::) { + // TODO(orizi): Replace with simple call once inlining is supported. + match get_gas() { + Option::Some(_) => {}, + Option::None(_) => { + let mut data = ArrayTrait::new(); + data.append('Out of gas'); + panic(data); + }, + } + match input.pop_front() { + Option::Some(value) => { + Serde::::serialize(ref serialized, value); + serialize_array_felt_helper(ref serialized, ref input); + }, + Option::None(_) => {}, + } +} + +fn deserialize_array_felt_helper( + ref serialized: Array::, mut curr_output: Array::, remaining: felt +) -> Option::> { + // TODO(orizi): Replace with simple call once inlining is supported. + match get_gas() { + Option::Some(_) => {}, + Option::None(_) => { + let mut data = ArrayTrait::new(); + data.append('Out of gas'); + panic(data); + }, + } + if remaining == 0 { + return Option::>::Some(curr_output); + } + curr_output.append(Serde::::deserialize(ref serialized)?); + deserialize_array_felt_helper(ref serialized, curr_output, remaining - 1) +} diff --git a/src/nile/core/cairo1/compilers/corelib/starknet.cairo b/src/nile/core/cairo1/compilers/corelib/starknet.cairo new file mode 100644 index 00000000..ba2d1c1d --- /dev/null +++ b/src/nile/core/cairo1/compilers/corelib/starknet.cairo @@ -0,0 +1,147 @@ +extern type System; +#[derive(Copy, Drop)] +extern type StorageBaseAddress; +#[derive(Copy, Drop)] +extern type StorageAddress; +#[derive(Copy, Drop)] +extern type ContractAddress; + +// An Helper function to force the inclusion of `System` in the list of implicits. +fn use_system_implicit() implicits(System) {} + +// Storage. +extern fn storage_base_address_const() -> StorageBaseAddress nopanic; +extern fn storage_base_address_from_felt( + addr: felt +) -> StorageBaseAddress implicits(RangeCheck) nopanic; +extern fn storage_address_from_base_and_offset( + base: StorageBaseAddress, offset: u8 +) -> StorageAddress nopanic; +extern fn storage_address_from_base(base: StorageBaseAddress) -> StorageAddress nopanic; + +// Only address_domain 0 is currently supported. +// This parameter is going to be used to access address spaces with different +// data availability guarantees. +extern fn storage_read_syscall( + address_domain: felt, address: StorageAddress, +) -> SyscallResult:: implicits(GasBuiltin, System) nopanic; +extern fn storage_write_syscall( + address_domain: felt, address: StorageAddress, value: felt +) -> SyscallResult::<()> implicits(GasBuiltin, System) nopanic; + +// Interoperability. +extern fn contract_address_const() -> ContractAddress nopanic; +extern fn call_contract_syscall( + address: ContractAddress, calldata: Array:: +) -> SyscallResult::> implicits(GasBuiltin, System) nopanic; + +extern fn contract_address_try_from_felt( + address: felt +) -> Option:: implicits(RangeCheck) nopanic; + +// Events. +extern fn emit_event_syscall( + keys: Array::, data: Array:: +) -> SyscallResult::<()> implicits(GasBuiltin, System) nopanic; + +// Getters. +extern fn get_caller_address_syscall() -> SyscallResult:: implicits( + GasBuiltin, System +) nopanic; + +fn get_caller_address() -> felt { + get_caller_address_syscall().unwrap_syscall() +} + +trait StorageAccess { + fn read(address_domain: felt, base: StorageBaseAddress) -> SyscallResult::; + fn write(address_domain: felt, base: StorageBaseAddress, value: T) -> SyscallResult::<()>; +} + +impl StorageAccessFelt of StorageAccess:: { + #[inline(always)] + fn read(address_domain: felt, base: StorageBaseAddress) -> SyscallResult:: { + storage_read_syscall(address_domain, storage_address_from_base(base)) + } + #[inline(always)] + fn write(address_domain: felt, base: StorageBaseAddress, value: felt) -> SyscallResult::<()> { + storage_write_syscall(address_domain, storage_address_from_base(base), value) + } +} + +impl StorageAccessBool of StorageAccess:: { + fn read(address_domain: felt, base: StorageBaseAddress) -> SyscallResult:: { + Result::Ok(StorageAccess::::read(address_domain, base)? != 0) + } + #[inline(always)] + fn write(address_domain: felt, base: StorageBaseAddress, value: bool) -> SyscallResult::<()> { + StorageAccess::::write(address_domain, base, if value { + 1 + } else { + 0 + }) + } +} + +impl StorageAccessU8 of StorageAccess:: { + fn read(address_domain: felt, base: StorageBaseAddress) -> Result::> { + Result::Ok(u8_from_felt(StorageAccess::::read(address_domain, base)?)) + } + #[inline(always)] + fn write( + address_domain: felt, base: StorageBaseAddress, value: u8 + ) -> Result::<(), Array::> { + StorageAccess::::write(address_domain, base, u8_to_felt(value)) + } +} + +impl StorageAccessU128 of StorageAccess:: { + fn read(address_domain: felt, base: StorageBaseAddress) -> SyscallResult:: { + Result::Ok(u128_from_felt(StorageAccess::::read(address_domain, base)?)) + } + #[inline(always)] + fn write(address_domain: felt, base: StorageBaseAddress, value: u128) -> SyscallResult::<()> { + StorageAccess::::write(address_domain, base, u128_to_felt(value)) + } +} + +impl StorageAccessU256 of StorageAccess:: { + fn read(address_domain: felt, base: StorageBaseAddress) -> SyscallResult:: { + Result::Ok( + u256 { + low: StorageAccess::::read(address_domain, base)?, + high: u128_from_felt( + storage_read_syscall( + address_domain, storage_address_from_base_and_offset(base, 1_u8) + )? + ) + } + ) + } + fn write(address_domain: felt, base: StorageBaseAddress, value: u256) -> SyscallResult::<()> { + StorageAccess::::write(address_domain, base, value.low)?; + storage_write_syscall( + address_domain, + storage_address_from_base_and_offset(base, 1_u8), + u128_to_felt(value.high) + ) + } +} + +/// The result type for a syscall. +type SyscallResult = Result::>; + +trait SyscallResultTrait { + /// If `val` is `Result::Ok(x)`, returns `x`. Otherwise, panics with the revert reason. + fn unwrap_syscall(self: SyscallResult::) -> T; +} +impl SyscallResultTraitImpl of SyscallResultTrait:: { + fn unwrap_syscall(self: SyscallResult::) -> T { + match self { + Result::Ok(x) => x, + Result::Err(revert_reason) => { + panic(revert_reason) + }, + } + } +} diff --git a/src/nile/core/cairo1/compilers/corelib/test.cairo b/src/nile/core/cairo1/compilers/corelib/test.cairo new file mode 100644 index 00000000..6c69ad5e --- /dev/null +++ b/src/nile/core/cairo1/compilers/corelib/test.cairo @@ -0,0 +1,872 @@ +use array::ArrayTrait; +use dict::DictFeltToTrait; +use option::OptionTrait; +use option::OptionTraitImpl; + +#[test] +#[should_panic] +fn test_assert_false() { + assert(false, 'assert(false)'); +} + +#[test] +fn test_assert_true() { + assert(true, 'assert(true)'); +} + +#[test] +fn test_bool_operators() { + assert(true == true, 't == t'); + assert(false == false, 'f == f'); + assert(!true == false, '!t == f'); + assert(!false == true, '!f == t'); + assert(true != false, 't != f'); + assert(false != true, 'f != t'); + assert(!(false & false), '!(f & f)'); + assert(!(true & false), '!(t & f)'); + assert(!(false & true), '!(f & t)'); + assert(true & true, 't & t'); + assert(!(false | false), '!(f | f)'); + assert(true | false, 't | f'); + assert(false | true, 'f | t'); + assert(true | true, 't | t'); + assert(!(false ^ false), '!(f ^ f)'); + assert(true ^ false, 't ^ f'); + assert(false ^ true, 'f ^ t'); + assert(!(true ^ true), '!(t ^ t)'); +} + +impl OptionEcPointCopy of Copy::>; +impl NonZeroEcPointDrop of Drop::; +use core::traits::ToBool; + +#[test] +fn test_ec_operations() { + // Beta + 2 is a square, and for x = 1 and alpha = 1, x^3 + alpha * x + beta = beta + 2. + let beta_p2_root = 2487829544412206244690656897973144572467842667075005257202960243805141046681; + let p = ec_point_from_x(1).unwrap(); + let p_nz = ec_point_non_zero(p); + let (x, y) = ec_point_unwrap(p_nz); + assert(x == 1, 'x != 1'); + assert(y == beta_p2_root | y == -beta_p2_root, 'y is wrong'); + + let mut state = ec_state_init(); + ec_state_add(ref state, p_nz); + let q = ec_state_try_finalize_nz(state).expect('zero point'); + let (qx, qy) = ec_point_unwrap(q); + assert(qx == x, 'bad finalize x'); + assert(qy == y, 'bad finalize y'); + + // Try doing the same thing with the EC op builtin. + let mut state = ec_state_init(); + ec_state_add_mul(ref state, 1, p_nz); + let q3 = ec_state_try_finalize_nz(state).expect('zero point'); + let (qx, qy) = ec_point_unwrap(q3); + assert(qx == x, 'bad EC op x'); + assert(qy == y, 'bad EC op y'); + + // Try computing `p + p` using the ec_mul function. + let double_p = ec_mul(p, 2); + let (double_x, double_y) = ec_point_unwrap(ec_point_non_zero(double_p)); + let expected_double_y = + 3572434102142093425782752266058856056057826477682467661647843687948039943621; + assert( + double_x == 75984168971785666410219869038140038216102669781812169677875295511117260233, + 'bad double x' + ); + assert(double_y == expected_double_y | double_y == -expected_double_y, 'bad double y'); + + // Compute `2p - p`. + let (sub_x, sub_y) = ec_point_unwrap(ec_point_non_zero(double_p - p)); + assert(sub_x == x, 'bad x for 2p - p'); + assert(sub_y == y, 'bad y for 2p - p'); + + // Compute `p - p`. + assert(ec_point_is_zero(p - p).to_bool(), 'p - p did not return 0.'); + + // Compute `(-p) - p`. + let (sub2_x, sub2_y) = ec_point_unwrap(ec_point_non_zero(ec_neg(p) - p)); + assert(sub2_x == double_x, 'bad x for (-p) - p'); + assert(sub2_y == -double_y, 'bad y for (-p) - p'); +} + +#[test] +#[should_panic] +fn test_bad_ec_point_creation() { + ec_point_new(0, 0); +} + +#[test] +fn test_ec_point_finalization_zero() { + let state = ec_state_init(); + let point_at_infinity = ec_state_try_finalize_nz(state); + assert(point_at_infinity.is_none(), 'Wrong point'); +} + +#[test] +fn test_ecdsa() { + let message_hash = 0x503f4bea29baee10b22a7f10bdc82dda071c977c1f25b8f3973d34e6b03b2c; + let public_key = 0x7b7454acbe7845da996377f85eb0892044d75ae95d04d3325a391951f35d2ec; + let signature_r = 0xbe96d72eb4f94078192c2e84d5230cde2a70f4b45c8797e2c907acff5060bb; + let signature_s = 0x677ae6bba6daf00d2631fab14c8acf24be6579f9d9e98f67aa7f2770e57a1f5; + assert( + ecdsa::check_ecdsa_signature(:message_hash, :public_key, :signature_r, :signature_s), + 'ecdsa returned false' + ); + assert( + !ecdsa::check_ecdsa_signature( + message_hash: message_hash + 1, :public_key, :signature_r, :signature_s + ), + 'ecdsa - wrong message' + ); + assert( + !ecdsa::check_ecdsa_signature( + :message_hash, public_key: public_key + 1, :signature_r, :signature_s + ), + 'ecdsa - wrong public_key' + ); + assert( + !ecdsa::check_ecdsa_signature( + :message_hash, :public_key, signature_r: signature_r + 1, :signature_s + ), + 'ecdsa - wrong r' + ); + assert( + !ecdsa::check_ecdsa_signature( + :message_hash, :public_key, :signature_r, signature_s: signature_s + 1 + ), + 'ecdsa - wrong s' + ); +} + +#[test] +fn test_ec_mul() { + let p = ec_point_new( + x: 336742005567258698661916498343089167447076063081786685068305785816009957563, + y: 1706004133033694959518200210163451614294041810778629639790706933324248611779, + ); + let m = 2713877091499598330239944961141122840311015265600950719674787125185463975936; + let (x, y) = ec_point_unwrap(ec_point_non_zero(ec_mul(p, m))); + + assert( + x == 2881632108168892236043523177391659237686965655035240771134509747985978822780, + 'ec_mul failed (x).' + ); + assert( + y == 591135563672138037839394207500885413019058613584891498394077262936524140839, + 'ec_mul failed (y).' + ); +} + +#[test] +fn test_felt_operators() { + assert(1 + 3 == 4, '1 + 3 == 4'); + assert(3 + 6 == 9, '3 + 6 == 9'); + assert(3 - 1 == 2, '3 - 1 == 2'); + assert(1231 - 231 == 1000, '1231-231=1000'); + assert(1 * 3 == 3, '1 * 3 == 3'); + assert(3 * 6 == 18, '3 * 6 == 18'); + assert(1 < 4, '1 < 4'); + assert(1 <= 4, '1 <= 4'); + assert(!(4 < 4), '!(4 < 4)'); + assert(4 <= 4, '4 <= 4'); + assert(5 > 2, '5 > 2'); + assert(5 >= 2, '5 >= 2'); + assert(!(3 > 3), '!(3 > 3)'); + assert(3 >= 3, '3 >= 3'); +} + +#[test] +fn test_u8_operators() { + assert(1_u8 == 1_u8, '1 == 1'); + assert(1_u8 != 2_u8, '1 != 2'); + assert(1_u8 + 3_u8 == 4_u8, '1 + 3 == 4'); + assert(3_u8 + 6_u8 == 9_u8, '3 + 6 == 9'); + assert(3_u8 - 1_u8 == 2_u8, '3 - 1 == 2'); + assert(1_u8 * 3_u8 == 3_u8, '1 * 3 == 3'); + assert(2_u8 * 4_u8 == 8_u8, '2 * 4 == 8'); + assert(19_u8 / 7_u8 == 2_u8, '19 / 7 == 2'); + assert(19_u8 % 7_u8 == 5_u8, '19 % 7 == 5'); + assert(231_u8 - 131_u8 == 100_u8, '231-131=100'); + assert(1_u8 < 4_u8, '1 < 4'); + assert(1_u8 <= 4_u8, '1 <= 4'); + assert(!(4_u8 < 4_u8), '!(4 < 4)'); + assert(4_u8 <= 4_u8, '4 <= 4'); + assert(5_u8 > 2_u8, '5 > 2'); + assert(5_u8 >= 2_u8, '5 >= 2'); + assert(!(3_u8 > 3_u8), '!(3 > 3)'); + assert(3_u8 >= 3_u8, '3 >= 3'); +} + +#[test] +#[should_panic] +fn test_u8_sub_overflow_1() { + 0_u8 - 1_u8; +} + +#[test] +#[should_panic] +fn test_u8_sub_overflow_2() { + 0_u8 - 3_u8; +} + +#[test] +#[should_panic] +fn test_u8_sub_overflow_3() { + 1_u8 - 3_u8; +} + +#[test] +#[should_panic] +fn test_u8_sub_overflow_4() { + 100_u8 - 250_u8; +} + +#[test] +#[should_panic] +fn test_u8_add_overflow_1() { + 128_u8 + 128_u8; +} + +#[test] +#[should_panic] +fn test_u8_add_overflow_2() { + 200_u8 + 60_u8; +} + +#[test] +#[should_panic] +fn test_u8_mul_overflow_1() { + 0x10_u8 * 0x10_u8; +} + +#[test] +#[should_panic] +fn test_u8_mul_overflow_2() { + 0x11_u8 * 0x10_u8; +} + +#[test] +#[should_panic] +fn test_u8_mul_overflow_3() { + 2_u8 * 0x80_u8; +} + +#[test] +#[should_panic] +fn test_u8_div_by_0() { + 2_u8 / 0_u8; +} + +#[test] +#[should_panic] +fn test_u8_mod_by_0() { + 2_u8 % 0_u8; +} + +#[test] +fn test_u16_operators() { + assert(1_u16 == 1_u16, '1 == 1'); + assert(1_u16 != 2_u16, '1 != 2'); + assert(1_u16 + 3_u16 == 4_u16, '1 + 3 == 4'); + assert(3_u16 + 6_u16 == 9_u16, '3 + 6 == 9'); + assert(3_u16 - 1_u16 == 2_u16, '3 - 1 == 2'); + assert(231_u16 - 131_u16 == 100_u16, '231-131=100'); + assert(1_u16 * 3_u16 == 3_u16, '1 * 3 == 3'); + assert(2_u16 * 4_u16 == 8_u16, '2 * 4 == 8'); + assert(51725_u16 / 7_u16 == 7389_u16, '51725 / 7 == 7389'); + assert(51725_u16 % 7_u16 == 2_u16, '51725 % 7 == 2'); + assert(1_u16 < 4_u16, '1 < 4'); + assert(1_u16 <= 4_u16, '1 <= 4'); + assert(!(4_u16 < 4_u16), '!(4 < 4)'); + assert(4_u16 <= 4_u16, '4 <= 4'); + assert(5_u16 > 2_u16, '5 > 2'); + assert(5_u16 >= 2_u16, '5 >= 2'); + assert(!(3_u16 > 3_u16), '!(3 > 3)'); + assert(3_u16 >= 3_u16, '3 >= 3'); +} + +#[test] +#[should_panic] +fn test_u16_sub_overflow_1() { + 0_u16 - 1_u16; +} + +#[test] +#[should_panic] +fn test_u16_sub_overflow_2() { + 0_u16 - 3_u16; +} + +#[test] +#[should_panic] +fn test_u16_sub_overflow_3() { + 1_u16 - 3_u16; +} + +#[test] +#[should_panic] +fn test_u16_sub_overflow_4() { + 100_u16 - 250_u16; +} + +#[test] +#[should_panic] +fn test_u16_add_overflow_1() { + 0x8000_u16 + 0x8000_u16; +} + +#[test] +#[should_panic] +fn test_u16_add_overflow_2() { + 0x9000_u16 + 0x8001_u16; +} + +#[test] +#[should_panic] +fn test_u16_mul_overflow_1() { + 0x100_u16 * 0x100_u16; +} + +#[test] +#[should_panic] +fn test_u16_mul_overflow_2() { + 0x101_u16 * 0x100_u16; +} + +#[test] +#[should_panic] +fn test_u16_mul_overflow_3() { + 2_u16 * 0x8000_u16; +} + +#[test] +#[should_panic] +fn test_u16_div_by_0() { + 2_u16 / 0_u16; +} + +#[test] +#[should_panic] +fn test_u16_mod_by_0() { + 0_u16 % 0_u16; +} + +#[test] +fn test_u32_operators() { + assert(1_u32 == 1_u32, '1 == 1'); + assert(1_u32 != 2_u32, '1 != 2'); + assert(1_u32 + 3_u32 == 4_u32, '1 + 3 == 4'); + assert(3_u32 + 6_u32 == 9_u32, '3 + 6 == 9'); + assert(3_u32 - 1_u32 == 2_u32, '3 - 1 == 2'); + assert(231_u32 - 131_u32 == 100_u32, '231-131=100'); + assert(1_u32 * 3_u32 == 3_u32, '1 * 3 == 3'); + assert(2_u32 * 4_u32 == 8_u32, '2 * 4 == 8'); + assert(510670725_u32 / 7_u32 == 72952960_u32, '510670725 / 7 == 72952960'); + assert(510670725_u32 % 7_u32 == 5_u32, '510670725 % 7 == 5'); + assert(1_u32 < 4_u32, '1 < 4'); + assert(1_u32 <= 4_u32, '1 <= 4'); + assert(!(4_u32 < 4_u32), '!(4 < 4)'); + assert(4_u32 <= 4_u32, '4 <= 4'); + assert(5_u32 > 2_u32, '5 > 2'); + assert(5_u32 >= 2_u32, '5 >= 2'); + assert(!(3_u32 > 3_u32), '!(3 > 3)'); + assert(3_u32 >= 3_u32, '3 >= 3'); +} + +#[test] +#[should_panic] +fn test_u32_sub_overflow_1() { + 0_u32 - 1_u32; +} + +#[test] +#[should_panic] +fn test_u32_sub_overflow_2() { + 0_u32 - 3_u32; +} + +#[test] +#[should_panic] +fn test_u32_sub_overflow_3() { + 1_u32 - 3_u32; +} + +#[test] +#[should_panic] +fn test_u32_sub_overflow_4() { + 100_u32 - 250_u32; +} + +#[test] +#[should_panic] +fn test_u32_add_overflow_1() { + 0x80000000_u32 + 0x80000000_u32; +} + +#[test] +#[should_panic] +fn test_u32_add_overflow_2() { + 0x90000000_u32 + 0x80000001_u32; +} + +#[test] +#[should_panic] +fn test_u32_mul_overflow_1() { + 0x10000_u32 * 0x10000_u32; +} + +#[test] +#[should_panic] +fn test_u32_mul_overflow_2() { + 0x10001_u32 * 0x10000_u32; +} + +#[test] +#[should_panic] +fn test_u32_mul_overflow_3() { + 2_u32 * 0x80000000_u32; +} + +#[test] +#[should_panic] +fn test_u32_div_by_0() { + 2_u32 / 0_u32; +} + +#[test] +#[should_panic] +fn test_u32_mod_by_0() { + 0_u32 % 0_u32; +} + +#[test] +fn test_u64_operators() { + assert(1_u64 == 1_u64, '1 == 1'); + assert(1_u64 != 2_u64, '1 != 2'); + assert(1_u64 + 3_u64 == 4_u64, '1 + 3 == 4'); + assert(3_u64 + 6_u64 == 9_u64, '3 + 6 == 9'); + assert(3_u64 - 1_u64 == 2_u64, '3 - 1 == 2'); + assert(231_u64 - 131_u64 == 100_u64, '231-131=100'); + assert(1_u64 * 3_u64 == 3_u64, '1 * 3 == 3'); + assert(2_u64 * 4_u64 == 8_u64, '2 * 4 == 8'); + assert( + 5010670477878974275_u64 / 7_u64 == 715810068268424896_u64, + '5010670477878974275 / 7 == 715810068268424896' + ); + assert(5010670477878974275_u64 % 7_u64 == 3_u64, '5010670477878974275 % 7 == 5'); + assert(1_u64 < 4_u64, '1 < 4'); + assert(1_u64 <= 4_u64, '1 <= 4'); + assert(!(4_u64 < 4_u64), '!(4 < 4)'); + assert(4_u64 <= 4_u64, '4 <= 4'); + assert(5_u64 > 2_u64, '5 > 2'); + assert(5_u64 >= 2_u64, '5 >= 2'); + assert(!(3_u64 > 3_u64), '!(3 > 3)'); + assert(3_u64 >= 3_u64, '3 >= 3'); +} + +#[test] +#[should_panic] +fn test_u64_sub_overflow_1() { + 0_u64 - 1_u64; +} + +#[test] +#[should_panic] +fn test_u64_sub_overflow_2() { + 0_u64 - 3_u64; +} + +#[test] +#[should_panic] +fn test_u64_sub_overflow_3() { + 1_u64 - 3_u64; +} + +#[test] +#[should_panic] +fn test_u64_sub_overflow_4() { + 100_u64 - 250_u64; +} + +#[test] +#[should_panic] +fn test_u64_add_overflow_1() { + 0x8000000000000000_u64 + 0x8000000000000000_u64; +} + +#[test] +#[should_panic] +fn test_u64_add_overflow_2() { + 0x9000000000000000_u64 + 0x8000000000000001_u64; +} + +#[test] +#[should_panic] +fn test_u64_mul_overflow_1() { + 0x100000000_u64 * 0x100000000_u64; +} + +#[test] +#[should_panic] +fn test_u64_mul_overflow_2() { + 0x100000001_u64 * 0x100000000_u64; +} + +#[test] +#[should_panic] +fn test_u64_mul_overflow_3() { + 2_u64 * 0x8000000000000000_u64; +} + +#[test] +#[should_panic] +fn test_u64_div_by_0() { + 2_u64 / 0_u64; +} + +#[test] +#[should_panic] +fn test_u64_mod_by_0() { + 0_u64 % 0_u64; +} + +#[test] +fn test_u128_operators() { + assert(1_u128 == 1_u128, '1 == 1'); + assert(!(1_u128 == 2_u128), '!(1 == 2)'); + assert(1_u128 + 3_u128 == 4_u128, '1 + 3 == 4'); + assert(3_u128 + 6_u128 == 9_u128, '3 + 6 == 9'); + assert(3_u128 - 1_u128 == 2_u128, '3 - 1 == 2'); + assert(1231_u128 - 231_u128 == 1000_u128, '1231-231=1000'); + assert(1_u128 * 3_u128 == 3_u128, '1 * 3 == 3'); + assert(2_u128 * 4_u128 == 8_u128, '2 * 4 == 8'); + assert(8_u128 / 2_u128 == 4_u128, '8 / 2 == 4'); + assert(8_u128 % 2_u128 == 0_u128, '8 % 2 == 0'); + assert(7_u128 / 3_u128 == 2_u128, '7 / 3 == 2'); + assert(7_u128 % 3_u128 == 1_u128, '7 % 3 == 1'); + assert(1_u128 < 4_u128, '1 < 4'); + assert(1_u128 <= 4_u128, '1 <= 4'); + assert(!(4_u128 < 4_u128), '!(4 < 4)'); + assert(4_u128 <= 4_u128, '4 <= 4'); + assert(5_u128 > 2_u128, '5 > 2'); + assert(5_u128 >= 2_u128, '5 >= 2'); + assert(!(3_u128 > 3_u128), '!(3 > 3)'); + assert(3_u128 >= 3_u128, '3 >= 3'); + assert((1_u128 | 2_u128) == 3_u128, '1 | 2 == 3'); + assert((1_u128 & 2_u128) == 0_u128, '1 & 2 == 0'); + assert((1_u128 ^ 2_u128) == 3_u128, '1 ^ 2 == 3'); + assert((2_u128 | 2_u128) == 2_u128, '2 | 2 == 2'); + assert((2_u128 & 2_u128) == 2_u128, '2 & 2 == 2'); + assert((2_u128 & 3_u128) == 2_u128, '2 & 3 == 2'); + assert((3_u128 ^ 6_u128) == 5_u128, '3 ^ 6 == 5'); + assert(u128_sqrt(9_u128) == 3_u128, 'u128_sqrt(9) == 3'); + assert(u128_sqrt(10_u128) == 3_u128, 'u128_sqrt(10) == 3'); + assert( + u128_sqrt(1267650600228229401496703205376_u128) == 1125899906842624_u128, + 'u128_sqrt(2^100) == 2^50' + ); + assert( + u128_sqrt(340282366920938463463374607431768211455_u128) == 18446744073709551615_u128, + 'u128_sqrt(2^128 - 1) == 18,446,744,073,709,551,615' + ); + assert(u128_sqrt(1_u128) == 1_u128, 'u128_sqrt(1) == 1'); + assert(u128_sqrt(0_u128) == 0_u128, 'u128_sqrt(0) == 0'); +} + +fn pow_2_127() -> u128 { + 0x80000000000000000000000000000000_u128 +} + +fn pow_2_64() -> u128 { + 0x10000000000000000_u128 +} + +#[test] +#[should_panic] +fn test_u128_sub_overflow_1() { + 0_u128 - 1_u128; +} + +#[test] +#[should_panic] +fn test_u128_sub_overflow_2() { + 0_u128 - 3_u128; +} + +#[test] +#[should_panic] +fn test_u128_sub_overflow_3() { + 1_u128 - 3_u128; +} + +#[test] +#[should_panic] +fn test_u128_sub_overflow_4() { + 100_u128 - 1000_u128; +} + +#[test] +#[should_panic] +fn test_u128_add_overflow_1() { + pow_2_127() + pow_2_127(); +} + +#[test] +#[should_panic] +fn test_u128_add_overflow_2() { + (pow_2_127() + 12_u128) + pow_2_127(); +} + +#[test] +#[should_panic] +fn test_u128_mul_overflow_1() { + pow_2_64() * pow_2_64(); +} + +#[test] +#[should_panic] +fn test_u128_mul_overflow_2() { + (pow_2_64() + 1_u128) * pow_2_64(); +} + +#[test] +#[should_panic] +fn test_u128_mul_overflow_3() { + 2_u128 * pow_2_127(); +} + +#[test] +#[should_panic] +fn test_u128_div_by_0() { + 2_u128 / 0_u128; +} + +#[test] +#[should_panic] +fn test_u128_mod_by_0() { + 2_u128 % 0_u128; +} + +// TODO(orizi): Remove when u256 literals are supported. +fn as_u256(high: u128, low: u128) -> u256 { + u256 { low, high } +} + +#[test] +fn test_u256_from_felt() { + assert(u256_from_felt(1) == as_u256(0_u128, 1_u128), 'into 1'); + assert( + u256_from_felt(170141183460469231731687303715884105728 * 2) == as_u256(1_u128, 0_u128), + 'into 2**128' + ); +} + +// TODO(orizi): Use u256 literals when supported. +#[test] +fn test_u256_operators() { + let max_u128 = 0xffffffffffffffffffffffffffffffff_u128; + assert(as_u256(1_u128, 1_u128) + as_u256(3_u128, 2_u128) == as_u256(4_u128, 3_u128), 'no OF'); + assert( + as_u256(1_u128, pow_2_127()) + as_u256(3_u128, pow_2_127()) == as_u256(5_u128, 0_u128), + 'basic OF' + ); + assert(as_u256(4_u128, 3_u128) - as_u256(1_u128, 1_u128) == as_u256(3_u128, 2_u128), 'no UF'); + assert( + as_u256(5_u128, 0_u128) - as_u256(1_u128, pow_2_127()) == as_u256(3_u128, pow_2_127()), + 'basic UF' + ); + assert( + as_u256(4_u128, 3_u128) * as_u256(0_u128, 1_u128) == as_u256(4_u128, 3_u128), 'mul by 1' + ); + assert( + as_u256(4_u128, 3_u128) * as_u256(0_u128, 2_u128) == as_u256(8_u128, 6_u128), 'mul by 2' + ); + assert( + as_u256(0_u128, pow_2_127()) * as_u256(0_u128, 2_u128) == as_u256(1_u128, 0_u128), + 'basic mul OF' + ); + assert( + as_u256(0_u128, max_u128) + * as_u256(0_u128, max_u128) == as_u256(0xfffffffffffffffffffffffffffffffe_u128, 1_u128), + 'max_u128 * max_u128' + ); + assert( + as_u256(0_u128, max_u128) * as_u256(0_u128, 1_u128) == as_u256(0_u128, max_u128), + 'max_u128 * 1' + ); + assert( + as_u256(0_u128, 1_u128) * as_u256(0_u128, max_u128) == as_u256(0_u128, max_u128), + '1 * max_u128' + ); + assert( + (as_u256(1_u128, 2_u128) | as_u256(2_u128, 2_u128)) == as_u256(3_u128, 2_u128), + '1.2|2.2==3.2' + ); + assert( + (as_u256(2_u128, 1_u128) | as_u256(2_u128, 2_u128)) == as_u256(2_u128, 3_u128), + '2.1|2.2==2.3' + ); + assert( + (as_u256(2_u128, 2_u128) | as_u256(1_u128, 2_u128)) == as_u256(3_u128, 2_u128), + '2.2|1.2==3.2' + ); + assert( + (as_u256(2_u128, 2_u128) | as_u256(2_u128, 1_u128)) == as_u256(2_u128, 3_u128), + '2.2|2.1==2.3' + ); + assert( + (as_u256(1_u128, 2_u128) & as_u256(2_u128, 2_u128)) == as_u256(0_u128, 2_u128), + '1.2&2.2==0.2' + ); + assert( + (as_u256(2_u128, 1_u128) & as_u256(2_u128, 2_u128)) == as_u256(2_u128, 0_u128), + '2.1&2.2==2.0' + ); + assert( + (as_u256(2_u128, 2_u128) & as_u256(1_u128, 2_u128)) == as_u256(0_u128, 2_u128), + '2.2&1.2==0.2' + ); + assert( + (as_u256(2_u128, 2_u128) & as_u256(2_u128, 1_u128)) == as_u256(2_u128, 0_u128), + '2.2&2.1==2.0' + ); + assert( + (as_u256(1_u128, 2_u128) ^ as_u256(2_u128, 2_u128)) == as_u256(3_u128, 0_u128), + '1.2^2.2==3.0' + ); + assert( + (as_u256(2_u128, 1_u128) ^ as_u256(2_u128, 2_u128)) == as_u256(0_u128, 3_u128), + '2.1^2.2==0.3' + ); + assert( + (as_u256(2_u128, 2_u128) ^ as_u256(1_u128, 2_u128)) == as_u256(3_u128, 0_u128), + '2.2^1.2==3.0' + ); + assert( + (as_u256(2_u128, 2_u128) ^ as_u256(2_u128, 1_u128)) == as_u256(0_u128, 3_u128), + '2.2^2.1==0.3' + ); + assert(as_u256(1_u128, 2_u128) < as_u256(2_u128, 2_u128), '1.2<2.2'); + assert(as_u256(2_u128, 1_u128) < as_u256(2_u128, 2_u128), '2.1<2.2'); + assert(!(as_u256(2_u128, 2_u128) < as_u256(1_u128, 2_u128)), '2.2<1.2'); + assert(!(as_u256(2_u128, 2_u128) < as_u256(2_u128, 1_u128)), '2.2<2.1'); + assert(!(as_u256(2_u128, 2_u128) < as_u256(2_u128, 2_u128)), '2.2<2.2'); + assert(as_u256(1_u128, 2_u128) <= as_u256(2_u128, 2_u128), '1.2<=2.2'); + assert(as_u256(2_u128, 1_u128) <= as_u256(2_u128, 2_u128), '2.1<=2.2'); + assert(!(as_u256(2_u128, 2_u128) <= as_u256(1_u128, 2_u128)), '2.2<=1.2'); + assert(!(as_u256(2_u128, 2_u128) <= as_u256(2_u128, 1_u128)), '2.2<=2.1'); + assert(as_u256(2_u128, 2_u128) <= as_u256(2_u128, 2_u128), '2.2<=2.2'); + assert(!(as_u256(1_u128, 2_u128) > as_u256(2_u128, 2_u128)), '1.2>2.2'); + assert(!(as_u256(2_u128, 1_u128) > as_u256(2_u128, 2_u128)), '2.1>2.2'); + assert(as_u256(2_u128, 2_u128) > as_u256(1_u128, 2_u128), '2.2>1.2'); + assert(as_u256(2_u128, 2_u128) > as_u256(2_u128, 1_u128), '2.2>2.1'); + assert(!(as_u256(2_u128, 2_u128) > as_u256(2_u128, 2_u128)), '2.2>2.2'); + assert(!(as_u256(1_u128, 2_u128) >= as_u256(2_u128, 2_u128)), '1.2>=2.2'); + assert(!(as_u256(2_u128, 1_u128) >= as_u256(2_u128, 2_u128)), '2.1>=2.2'); + assert(as_u256(2_u128, 2_u128) >= as_u256(1_u128, 2_u128), '2.2>=1.2'); + assert(as_u256(2_u128, 2_u128) >= as_u256(2_u128, 1_u128), '2.2>=2.1'); + assert(as_u256(2_u128, 2_u128) >= as_u256(2_u128, 2_u128), '2.2>=2.2'); +} + +#[test] +#[should_panic] +fn test_u256_add_overflow() { + as_u256(pow_2_127(), 1_u128) + as_u256(pow_2_127(), 1_u128); +} + +#[test] +#[should_panic] +fn test_u256_sub_overflow() { + as_u256(1_u128, 1_u128) - as_u256(1_u128, 2_u128); +} + +#[test] +#[should_panic] +fn test_u256_mul_overflow_1() { + as_u256(1_u128, 1_u128) * as_u256(1_u128, 2_u128); +} + +#[test] +#[should_panic] +fn test_u256_mul_overflow_2() { + as_u256(0_u128, pow_2_127()) * as_u256(2_u128, 0_u128); +} + +// TODO(orizi): Switch to operators and literals when added. +fn test_array_helper(idx: usize) -> felt { + let mut arr = ArrayTrait::new(); + arr.append(10); + arr.append(11); + arr.append(12); + array_at(ref arr, idx) +} + +#[test] +fn test_array() { + assert(test_array_helper(0_usize) == 10, 'array[0] == 10'); + assert(test_array_helper(1_usize) == 11, 'array[1] == 11'); + assert(test_array_helper(2_usize) == 12, 'array[2] == 12'); +} + +#[test] +#[should_panic] +fn test_array_out_of_bound_1() { + test_array_helper(3_usize); +} + +#[test] +#[should_panic] +fn test_array_out_of_bound_2() { + test_array_helper(11_usize); +} + +#[test] +fn test_dict_new() -> DictFeltTo:: { + DictFeltToTrait::new() +} + +#[test] +fn test_dict_default_val() { + let mut dict = DictFeltToTrait::new(); + let default_val = dict.get(0); + let squashed_dict = dict.squash(); + assert(default_val == 0, 'default_val == 0'); +} + +// TODO(Gil): Assert before the squash when drop will autosquash the dict. +#[test] +fn test_dict_write_read() { + let mut dict = DictFeltToTrait::new(); + dict.insert(10, 110); + dict.insert(11, 111); + let val10 = dict.get(10); + let val11 = dict.get(11); + let val12 = dict.get(12); + let squashed_dict = dict.squash(); + assert(val10 == 110, 'dict[10] == 110'); + assert(val11 == 111, 'dict[11] == 111'); + assert(val12 == 0, 'default_val == 0'); +} + +#[test] +fn test_box_unbox_felts() { + let x = 10; + let boxed_x = into_box::(x); + let y = 11; + let boxed_y = into_box::(y); + assert(unbox::(boxed_x) == 10, 'x == 10'); + assert(unbox::(boxed_y) == 11, 'y == 11'); +} + + +// Test objects of size>1. +#[test] +fn test_box_unbox_u256() { + let x = as_u256(1_u128, 0_u128); + let boxed_x = into_box::(x); + let y = as_u256(1_u128, 1_u128); + let boxed_y = into_box::(y); + assert(unbox::(boxed_x) == as_u256(1_u128, 0_u128), 'unbox u256 x'); + assert(unbox::(boxed_y) == as_u256(1_u128, 1_u128), 'unbox u256 y'); +} diff --git a/src/nile/core/cairo1/compilers/corelib/traits.cairo b/src/nile/core/cairo1/compilers/corelib/traits.cairo new file mode 100644 index 00000000..134849b6 --- /dev/null +++ b/src/nile/core/cairo1/compilers/corelib/traits.cairo @@ -0,0 +1,59 @@ +trait Copy; +trait Drop; + +// TODO(spapini): When associated types are supported, support the general trait Add. +trait Add { + fn add(a: T, b: T) -> T; +} + +// TODO(spapini): When associated types are supported, support the general trait Sub. +trait Sub { + fn sub(a: T, b: T) -> T; +} + +// TODO(spapini): When associated types are supported, support the general trait Mul. +trait Mul { + fn mul(a: T, b: T) -> T; +} + +// TODO(spapini): When associated types are supported, support the general trait Div. +trait Div { + fn div(a: T, b: T) -> T; +} + +// TODO(spapini): When associated types are supported, support the general trait Rem. +trait Rem { + fn rem(a: T, b: T) -> T; +} + +trait PartialEq { + fn eq(a: T, b: T) -> bool; + fn ne(a: T, b: T) -> bool; +} + +// TODO(spapini): When associated types are supported, support the general trait BitAnd. +trait BitAnd { + fn bitand(a: T, b: T) -> T; +} + +// TODO(spapini): When associated types are supported, support the general trait BitOr. +trait BitOr { + fn bitor(a: T, b: T) -> T; +} + +// TODO(spapini): When associated types are supported, support the general trait BitXor. +trait BitXor { + fn bitxor(a: T, b: T) -> T; +} + +trait PartialOrd { + fn le(a: T, b: T) -> bool; + fn ge(a: T, b: T) -> bool; + fn lt(a: T, b: T) -> bool; + fn gt(a: T, b: T) -> bool; +} + +/// Represents a type that can be converted to `bool` using the `to_bool()` method. +trait ToBool { + fn to_bool(self: T) -> bool; +} diff --git a/src/nile/core/cairo1/compilers/src/bin/starknet-compile b/src/nile/core/cairo1/compilers/src/bin/starknet-compile new file mode 100755 index 00000000..0165f43e Binary files /dev/null and b/src/nile/core/cairo1/compilers/src/bin/starknet-compile differ diff --git a/src/nile/core/cairo1/compilers/src/bin/starknet-sierra-compile b/src/nile/core/cairo1/compilers/src/bin/starknet-sierra-compile new file mode 100755 index 00000000..13aaeb68 Binary files /dev/null and b/src/nile/core/cairo1/compilers/src/bin/starknet-sierra-compile differ diff --git a/tests/commands/test_deploy.py b/tests/commands/test_deploy.py index ff16311f..9b243574 100644 --- a/tests/commands/test_deploy.py +++ b/tests/commands/test_deploy.py @@ -62,7 +62,6 @@ async def test_deploy_contract( overriding_path, watch_mode, ): - logging.getLogger().setLevel(logging.INFO) account = await MockAccount("TEST_KEY", NETWORK) diff --git a/tests/core/types/test_transactions.py b/tests/core/types/test_transactions.py index df08556f..c2d5b6cd 100644 --- a/tests/core/types/test_transactions.py +++ b/tests/core/types/test_transactions.py @@ -277,7 +277,6 @@ async def test_deploy_account_transaction_init_defaults(mock_get_class_hash): async def test_transaction_execute( mock_status, mock_call_args, mock_get_tx_hash, watch_mode ): - account = await MockAccount(KEY, NETWORK) mock_sig_r, mock_sig_s = account.signer.sign(TX_HASH) with patch("nile.core.types.transactions.execute_call") as mock_execute_call: @@ -315,7 +314,6 @@ async def test_transaction_execute( return_value={"param": "value"}, ) async def test_transaction_estimate_fee(mock_call_args, mock_get_tx_hash, caplog): - account = await MockAccount(KEY, NETWORK) mock_sig_r, mock_sig_s = account.signer.sign(TX_HASH) exp_output = 1000 @@ -356,7 +354,6 @@ async def test_transaction_estimate_fee(mock_call_args, mock_get_tx_hash, caplog return_value={"param": "value"}, ) async def test_transaction_simulate(mock_call_args, mock_get_tx_hash, caplog): - account = await MockAccount(KEY, NETWORK) mock_sig_r, mock_sig_s = account.signer.sign(TX_HASH) with patch("nile.core.types.transactions.execute_call") as mock_execute_call: