From afae1f366b008fe9592f296db7875f1d7839da65 Mon Sep 17 00:00:00 2001 From: fmoletta <99273364+fmoletta@users.noreply.github.com> Date: Wed, 20 Dec 2023 13:08:48 +0200 Subject: [PATCH] Add tests using `alexandria` cairo library (#394) * document todos and implement bytes31 * layout * Download alexandria lib in CI * Add alexandria tests * Add scarb project * suggestion and fixes * Build scarb in test target * Remove dbg print * Add karatsuba test * Clean unused imports * Add more cases * Add tests using data structures * Add more tests * Clippy * Comment tests that produce invalid mlir * Avoid searching twice for func * Pin alexandria version * Run scarb formatter * Dont import unused features * use a different installation method for scarb * Fix * Remove unnecessary flag * Udpate .PHONY * Remove char * Use standard installer with flag * Remove whitespace * Fix * Fix target name * Try solution * Output scarb version * pin scarb version * Build test files in coverage target * Fix scarb version * fix * Fix command duplication * Use action to install scarb in macos CI --------- Co-authored-by: Edgar Luque --- .github/workflows/ci.yml | 8 +++ Cargo.lock | 1 + Cargo.toml | 1 + Makefile | 23 +++++-- tests/alexandria.rs | 58 ++++++++++++++++++ tests/alexandria/.gitignore | 1 + tests/alexandria/Scarb.lock | 35 +++++++++++ tests/alexandria/Scarb.toml | 11 ++++ tests/alexandria/src/lib.cairo | 109 +++++++++++++++++++++++++++++++++ tests/common.rs | 7 +-- 10 files changed, 242 insertions(+), 12 deletions(-) create mode 100644 tests/alexandria.rs create mode 100644 tests/alexandria/.gitignore create mode 100644 tests/alexandria/Scarb.lock create mode 100644 tests/alexandria/Scarb.toml create mode 100644 tests/alexandria/src/lib.cairo diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e04e5af4d..219a997d9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -134,6 +134,10 @@ jobs: uses: dtolnay/rust-toolchain@1.72.1 with: components: clippy + - name: Install scarb + uses: software-mansion/setup-scarb@v1 + with: + scarb-version: "2.4.0" - name: Install deps run: make deps - name: Run tests @@ -156,6 +160,10 @@ jobs: uses: Swatinem/rust-cache@v2 - name: Install cargo-llvm-cov uses: taiki-e/install-action@cargo-llvm-cov + - name: Install scarb + uses: software-mansion/setup-scarb@v1 + with: + scarb-version: "2.4.0" - name: Install deps run: make deps - name: test and generate coverage diff --git a/Cargo.lock b/Cargo.lock index 24fc109ec..8327d2c4f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -880,6 +880,7 @@ dependencies = [ "num-traits 0.2.17", "pretty_assertions_sorted", "proptest", + "serde_json", "starknet-types-core", "tempfile", "test-case", diff --git a/Cargo.toml b/Cargo.toml index a4ef281af..dd0aebbd6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,6 +72,7 @@ proptest = "1.2" tempfile = "3.6" test-case = "3.2.1" walkdir = "2" +serde_json = { version = "1.0"} [build-dependencies] cc = "1.0" diff --git a/Makefile b/Makefile index e9d8474fc..da0e37b54 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: usage build book build-dev build-native coverage check test bench bench-ci doc doc-open install clean +.PHONY: usage build book build-dev build-native coverage check test bench bench-ci doc doc-open install clean install-scarb install-scarb-macos build-alexandria # # Environment detection. @@ -52,13 +52,13 @@ check: check-llvm cargo fmt --all -- --check cargo clippy --all-targets --all-features -- -D warnings -test: check-llvm needs-cairo2 +test: check-llvm needs-cairo2 build-alexandria cargo test --profile optimized-dev --all-targets --all-features proptest: check-llvm needs-cairo2 cargo test --profile optimized-dev --all-targets --all-features proptest -coverage: check-llvm needs-cairo2 +coverage: check-llvm needs-cairo2 build-alexandria cargo llvm-cov --verbose --profile optimized-dev --all-features --workspace --lcov --output-path lcov.info doc: check-llvm @@ -81,15 +81,15 @@ clean: deps: ifeq ($(UNAME), Linux) -deps: build-cairo-2-compiler +deps: build-cairo-2-compiler install-scarb endif ifeq ($(UNAME), Darwin) -deps: build-cairo-2-compiler-macos deps-macos +deps: deps-macos endif -rm -rf corelib -ln -s cairo2/corelib corelib -deps-macos: build-cairo-2-compiler-macos +deps-macos: build-cairo-2-compiler-macos install-scarb-macos -brew install llvm@17 --quiet @echo "You can execute the env-macos.sh script to setup the needed env variables." @@ -116,3 +116,14 @@ cairo-%-macos.tar: cairo-%.tar: curl -L -o "$@" "https://github.com/starkware-libs/cairo/releases/download/v$*/release-x86_64-unknown-linux-musl.tar.gz" + +SCARB_VERSION = 2.4.0 + +install-scarb: + curl --proto '=https' --tlsv1.2 -sSf https://docs.swmansion.com/scarb/install.sh| sh -s -- --no-modify-path --version $(SCARB_VERSION) + +install-scarb-macos: + curl --proto '=https' --tlsv1.2 -sSf https://docs.swmansion.com/scarb/install.sh| sh -s -- --version $(SCARB_VERSION) + +build-alexandria: + cd tests/alexandria; scarb build diff --git a/tests/alexandria.rs b/tests/alexandria.rs new file mode 100644 index 000000000..12d9b7d62 --- /dev/null +++ b/tests/alexandria.rs @@ -0,0 +1,58 @@ +use crate::common::GAS; +use cairo_lang_runner::SierraCasmRunner; +use cairo_lang_sierra::program::Program; +use common::{compare_outputs, run_native_program, run_vm_program}; +use std::{fs::File, io::BufReader}; +use test_case::test_case; + +mod common; + +#[track_caller] +fn compare_inputless_function(function_name: &str) { + // Load file compiled using `scarb build`` + let file = File::open("tests/alexandria/target/dev/alexandria.sierra.json").unwrap(); + let reader = BufReader::new(file); + let program: Program = serde_json::from_reader(reader).unwrap(); + let module_name = "alexandria"; + let runner = SierraCasmRunner::new( + program.clone(), + Some(Default::default()), + Default::default(), + ) + .unwrap(); + + let program: (String, Program, SierraCasmRunner) = (module_name.to_string(), program, runner); + let program = &program; + + let result_vm = run_vm_program(program, function_name, &[], Some(GAS as usize)).unwrap(); + + let result_native = run_native_program(program, function_name, &[]); + + compare_outputs( + &program.1, + &program.2.find_function(function_name).unwrap().id, + &result_vm, + &result_native, + ) + .expect("compare error"); +} + +// alexandria_math +#[test_case("fib")] +#[test_case("karatsuba" => ignore["System out of memory"])] +#[test_case("armstrong_number")] +#[test_case("aliquot_sum" => ignore["System out of memory"])] +#[test_case("collatz_sequence" => ignore["Result mismatch"])] +#[test_case("extended_euclidean_algorithm")] +// alexandria_data_structures +#[test_case("vec" => ignore["Gas mismatch"])] +#[test_case("stack" => ignore["Gas mismatch"])] +#[test_case("queue")] +#[test_case("bit_array")] +// alexandria_encoding +#[test_case("base64_encode" => ignore["Gas mismatch"])] +#[test_case("reverse_bits" => ignore["Invalid MlIR"])] +#[test_case("reverse_bytes"=> ignore["Invalid MlIR"])] +fn test_cases(function_name: &str) { + compare_inputless_function(function_name) +} diff --git a/tests/alexandria/.gitignore b/tests/alexandria/.gitignore new file mode 100644 index 000000000..eb5a316cb --- /dev/null +++ b/tests/alexandria/.gitignore @@ -0,0 +1 @@ +target diff --git a/tests/alexandria/Scarb.lock b/tests/alexandria/Scarb.lock new file mode 100644 index 000000000..d5bf57ffb --- /dev/null +++ b/tests/alexandria/Scarb.lock @@ -0,0 +1,35 @@ +# Code generated by scarb DO NOT EDIT. +version = 1 + +[[package]] +name = "alexandria" +version = "0.1.0" +dependencies = [ + "alexandria_data_structures", + "alexandria_encoding", + "alexandria_math", +] + +[[package]] +name = "alexandria_data_structures" +version = "0.1.0" +source = "git+https://github.com/keep-starknet-strange/alexandria.git?tag=cairo-v2.3.0-rc0#ae1d5149ff601a7ac5b39edc867d33ebd83d7f4f" +dependencies = [ + "alexandria_encoding", +] + +[[package]] +name = "alexandria_encoding" +version = "0.1.0" +source = "git+https://github.com/keep-starknet-strange/alexandria.git?tag=cairo-v2.3.0-rc0#ae1d5149ff601a7ac5b39edc867d33ebd83d7f4f" +dependencies = [ + "alexandria_math", +] + +[[package]] +name = "alexandria_math" +version = "0.2.0" +source = "git+https://github.com/keep-starknet-strange/alexandria.git?tag=cairo-v2.3.0-rc0#ae1d5149ff601a7ac5b39edc867d33ebd83d7f4f" +dependencies = [ + "alexandria_data_structures", +] diff --git a/tests/alexandria/Scarb.toml b/tests/alexandria/Scarb.toml new file mode 100644 index 000000000..6fb1cf5a4 --- /dev/null +++ b/tests/alexandria/Scarb.toml @@ -0,0 +1,11 @@ +[package] +name = "alexandria" +version = "0.1.0" +edition = "2023_10" + +# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html + +[dependencies] +alexandria_math = { tag = "cairo-v2.3.0-rc0", git = "https://github.com/keep-starknet-strange/alexandria.git" } +alexandria_data_structures = { tag = "cairo-v2.3.0-rc0", git = "https://github.com/keep-starknet-strange/alexandria.git" } +alexandria_encoding = { tag = "cairo-v2.3.0-rc0", git = "https://github.com/keep-starknet-strange/alexandria.git" } diff --git a/tests/alexandria/src/lib.cairo b/tests/alexandria/src/lib.cairo new file mode 100644 index 000000000..22a5f91a2 --- /dev/null +++ b/tests/alexandria/src/lib.cairo @@ -0,0 +1,109 @@ +mod alexandria { + // Alexandria Math + + fn fib() -> felt252 { + alexandria_math::fibonacci::fib(16, 10, 1) + } + + fn karatsuba() -> u128 { + alexandria_math::karatsuba::multiply(3754192357923759273591, 18492875) + } + + fn armstrong_number() -> bool { + alexandria_math::armstrong_number::is_armstrong_number(472587892687682) + } + + fn aliquot_sum() -> u128 { + alexandria_math::aliquot_sum::aliquot_sum(67472587892687682) + } + + fn collatz_sequence() -> Array { + alexandria_math::collatz_sequence::sequence(4332490568290368) + } + + fn extended_euclidean_algorithm() -> (u128, u128, u128) { + alexandria_math::extended_euclidean_algorithm::extended_euclidean_algorithm( + 384292543952858, 158915958590 + ) + } + + // Alexandria Data Structures + + use alexandria_data_structures::vec::{Felt252Vec, VecTrait}; + fn vec() -> (felt252, felt252, felt252) { + let mut vec = VecTrait::::new(); + vec.push(12); + vec.push(99); + vec.set(1, 67); + (vec.at(0), vec.at(1), vec.at(2)) + } + + use alexandria_data_structures::stack::{Felt252Stack, StackTrait}; + fn stack() -> (Option, Option, Option, bool) { + let mut stack = StackTrait::::new(); + stack.push(1); + stack.push(2); + stack.push(17); + let top = stack.peek(); + stack.pop(); + (top, stack.pop(), stack.pop(), stack.is_empty()) + } + + use alexandria_data_structures::queue::{Queue, QueueTrait}; + fn queue() -> (Option, Option, Option, bool) { + let mut queue = QueueTrait::::new(); + queue.enqueue(3); + queue.enqueue(31); + queue.enqueue(13); + (queue.dequeue(), queue.dequeue(), queue.dequeue(), queue.is_empty()) + } + + use alexandria_data_structures::bit_array::{BitArray, BitArrayTrait}; + fn bit_array() -> Option { + let mut bit_array: BitArray = Default::default(); + bit_array.write_word_be(340282366920938463463374607431768211455, 128); + bit_array.pop_front(); + bit_array.append_bit(true); + bit_array.append_bit(false); + bit_array.append_bit(true); + bit_array.read_word_le(bit_array.len()) + } + + // Alexandria Encoding + use alexandria_encoding::base64::{Encoder, Decoder, Base64Encoder, Base64Decoder}; + use core::array::ArrayTrait; + fn base64_encode() -> (Array, Array) { + let mut input = ArrayTrait::::new(); + input.append('C'); + input.append('a'); + input.append('i'); + input.append('r'); + input.append('o'); + let encoded = Base64Encoder::encode(input); + let decoded = Base64Decoder::decode(encoded.clone()); + (encoded, decoded) + } +// Compiling the following functions generates invalid MLIR, please uncomment once the bug is fixed + +// use alexandria_encoding::reversible::{U16ReversibleBits, U32ReversibleBits, U64ReversibleBits, U128ReversibleBits, U256ReversibleBits}; +// fn reverse_bits() -> (u16, u32, u64, u128, u256) { +// ( +// U16ReversibleBits::reverse_bits(@333), +// U32ReversibleBits::reverse_bits(@3333333), +// U64ReversibleBits::reverse_bits(@3333333333333), +// U128ReversibleBits::reverse_bits(@333333333333333333333), +// U256ReversibleBits::reverse_bits(@33333333333333333333333333), +// ) +// } + +// use alexandria_encoding::reversible::{U16Reversible, U32Reversible, U64Reversible, U128Reversible, U256Reversible}; +// fn reverse_bytes() -> (u16, u32, u64, u128, u256) { +// ( +// U16Reversible::reverse_bytes(@333), +// U32Reversible::reverse_bytes(@3333333), +// U64Reversible::reverse_bytes(@3333333333333), +// U128Reversible::reverse_bytes(@333333333333333333333), +// U256Reversible::reverse_bytes(@33333333333333333333333333), +// ) +// } +} diff --git a/tests/common.rs b/tests/common.rs index 07a483f76..039f761d8 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -251,12 +251,7 @@ pub fn run_native_program( cairo_native::execute( &engine, ®istry, - &program - .funcs - .iter() - .find(|x| x.id.debug_name.as_deref() == Some(&entry_point)) - .expect("Test program entry point not found.") - .id, + entry_point_id, args, required_initial_gas, Some(u64::MAX.into()),