From f2852856ba63fa14d31750270e6d4c967b2ab2b0 Mon Sep 17 00:00:00 2001 From: TomerStarkware Date: Sun, 5 Apr 2026 11:04:16 +0300 Subject: [PATCH] added e2e tests from cairo repo --- .github/workflows/bench-hyperfine.yml | 4 ++ .github/workflows/ci.yml | 10 +++ .github/workflows/release.yml | 2 + .github/workflows/starknet-blocks.yml | 11 ++- .gitignore | 5 +- .gitmodules | 4 ++ Cargo.toml | 1 + Makefile | 6 +- scripts/extract_e2e_sierra.py | 98 +++++++++++++++++++++++++++ src/libfuncs/circuit.rs | 4 ++ src/types/circuit.rs | 6 ++ tests/tests/e2e_libfuncs.rs | 60 ++++++++++++++++ tests/tests/mod.rs | 1 + vendor/cairo | 1 + 14 files changed, 204 insertions(+), 9 deletions(-) create mode 100644 .gitmodules create mode 100755 scripts/extract_e2e_sierra.py create mode 100644 tests/tests/e2e_libfuncs.rs create mode 160000 vendor/cairo diff --git a/.github/workflows/bench-hyperfine.yml b/.github/workflows/bench-hyperfine.yml index 585c7422f6..5cee142d97 100644 --- a/.github/workflows/bench-hyperfine.yml +++ b/.github/workflows/bench-hyperfine.yml @@ -24,6 +24,8 @@ jobs: LLVM_SYS_191_PREFIX: /usr/lib/llvm-19/ steps: - uses: actions/checkout@v4 + with: + submodules: true - name: check and free hdd space left run: | echo "Listing 20 largest packages" @@ -129,6 +131,7 @@ jobs: uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request[matrix.branch].sha }} + submodules: true - name: Fetch Rust cache uses: Swatinem/rust-cache@v2 @@ -162,6 +165,7 @@ jobs: uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request[matrix.branch].sha }} + submodules: true - name: Install Hyperfine uses: taiki-e/install-action@v2 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 974fa6aaeb..4d9af8cebd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,6 +21,8 @@ jobs: TABLEGEN_190_PREFIX: /usr/lib/llvm-19/ steps: - uses: actions/checkout@v4 + with: + submodules: true - uses: dtolnay/rust-toolchain@1.84.1 with: components: clippy, rustfmt @@ -116,6 +118,8 @@ jobs: RUST_LOG: cairo_native=debug,cairo_native_test=debug steps: - uses: actions/checkout@v4 + with: + submodules: true - name: Install testing tools uses: taiki-e/install-action@v2 with: @@ -171,6 +175,8 @@ jobs: RUST_LOG: cairo_native=debug,cairo_native_test=debug steps: - uses: actions/checkout@v4 + with: + submodules: true - name: Install testing tools uses: taiki-e/install-action@v2 with: @@ -194,6 +200,8 @@ jobs: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 + with: + submodules: true - name: check and free hdd space left run: | echo "Listing 20 largest packages" @@ -222,6 +230,8 @@ jobs: steps: - uses: actions/checkout@v4 + with: + submodules: true - name: Retreive cached dependecies uses: Swatinem/rust-cache@v2 - name: Install dependencies diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f6795dac18..153984ae90 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -33,6 +33,8 @@ jobs: runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 + with: + submodules: true - name: Set env run: | echo "MLIR_SYS_190_PREFIX=${{ matrix.dep_base_dir }}" >> $GITHUB_ENV diff --git a/.github/workflows/starknet-blocks.yml b/.github/workflows/starknet-blocks.yml index 0aa901ceb0..591325c9f7 100644 --- a/.github/workflows/starknet-blocks.yml +++ b/.github/workflows/starknet-blocks.yml @@ -8,7 +8,6 @@ on: jobs: run-blocks: - if: false name: Run Blocks runs-on: ubuntu-latest env: @@ -33,7 +32,7 @@ jobs: with: repository: starkware-libs/starknet-replay path: starknet-replay - ref: 5e75e33d67de77fe68aba06e05571175c0976733 + ref: 4b896e523bd33da66b95c8b2afdb7423785e99d4 # We need native to use the linux deps ci action - name: Checkout Native @@ -46,8 +45,8 @@ jobs: repository: starkware-libs/sequencer path: sequencer # branch main-v0.14.2 - ref: 77a1a32b7b2c52edd9b634276e3bb28f5fbd750c - + ref: efa9bd7d9e33e44d0cea08bf67c7b413fb20c7cb + - name: Restore RPC Calls id: restore-rpc-calls uses: actions/cache/restore@v4.2.0 @@ -108,14 +107,14 @@ jobs: with: name: dump-${{matrix.block}}-${{matrix.runner}} path: starknet-replay/state_dumps/${{matrix.runner}} - + - name: Save RPC Calls uses: actions/cache/save@v4.2.0 if: ${{ always() && matrix.runner == 'vm' }} with: path: starknet-replay/cache key: ${{ steps.restore-rpc-calls.outputs.cache-primary-key }} - + compare-dumps: name: Compare Dumps needs: [run-blocks] diff --git a/.gitignore b/.gitignore index fcd6b7f290..3cd0f84ba4 100644 --- a/.gitignore +++ b/.gitignore @@ -34,11 +34,12 @@ cairo-*.tar *.ipynb cairo-native-stress-logs.jsonl - + .DS_Store bench_starknet_compilation_data -cairo +/cairo +test_data/e2e_sierra/ test_data_artifacts/programs test_data_artifacts/contracts test_data_artifacts/tests diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..40243cb3fa --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "vendor/cairo"] + path = vendor/cairo + url = https://github.com/starkware-libs/cairo.git + branch = v2.16.0 diff --git a/Cargo.toml b/Cargo.toml index b09eab30a5..bcf0d3fe32 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -106,6 +106,7 @@ description = "A compiler to convert Cairo's IR Sierra code to MLIR and execute readme = "README.md" keywords = ["starknet", "cairo", "compiler", "mlir"] categories = ["compilers"] +exclude = ["vendor/"] version.workspace = true edition.workspace = true license.workspace = true diff --git a/Makefile b/Makefile index 13e86b6249..40ccde3b24 100644 --- a/Makefile +++ b/Makefile @@ -192,6 +192,10 @@ install-scarb-macos: pull-external-projects: python3 ./scripts/pull_external_projects.py v${CAIRO_2_VERSION} +test_data/e2e_sierra: vendor/cairo + python3 ./scripts/extract_e2e_sierra.py + + .PHONY: compile-test-data -compile-test-data: +compile-test-data: test_data/e2e_sierra python3 ./scripts/compile_test_data.py diff --git a/scripts/extract_e2e_sierra.py b/scripts/extract_e2e_sierra.py new file mode 100755 index 0000000000..d9ec33b452 --- /dev/null +++ b/scripts/extract_e2e_sierra.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 +""" +Extract sierra_code sections from cairo e2e test data files. + +Input: test_data/e2e_libfuncs_raw/ (files in //! > format) +Output: test_data/e2e_sierra/ (individual .sierra files) +""" + +import os +import re +import shutil +import sys + +SRC_DIR = "vendor/cairo/tests/e2e_test_data/libfuncs" +DST_DIR = "test_data/e2e_sierra" + + +def sanitize_name(name): + """Convert test name to a safe filename.""" + return re.sub(r"[^\w\-]", "_", name.strip()).strip("_") + + +def parse_test_cases(text): + """Parse //! > format into list of test-case dicts mapping section_key -> content.""" + cases = re.split(r"^//! > =+\s*$", text, flags=re.MULTILINE) + results = [] + for case in cases: + sections = {} + section_order = [] + current_section = None + lines = [] + for line in case.splitlines(): + m = re.match(r"^//! > (.+)$", line) + if m: + if current_section is not None: + sections[current_section] = "\n".join(lines).strip() + current_section = m.group(1).strip() + section_order.append(current_section) + lines = [] + else: + lines.append(line) + if current_section is not None: + sections[current_section] = "\n".join(lines).strip() + if sections: + sections["_order"] = section_order + results.append(sections) + return results + + +def main(): + if not os.path.isdir(SRC_DIR): + print( + f"Source directory {SRC_DIR} not found. " + "Run 'git submodule update --init' first." + ) + sys.exit(1) + + shutil.rmtree(DST_DIR, ignore_errors=True) + + count = 0 + for dirpath, _, filenames in sorted(os.walk(SRC_DIR)): + for filename in sorted(filenames): + filepath = os.path.join(dirpath, filename) + + with open(filepath, "r") as f: + content = f.read() + + # Preserve subdirectory structure (e.g. starknet/syscalls) + rel_dir = os.path.relpath(dirpath, SRC_DIR) + test_cases = parse_test_cases(content) + for case in test_cases: + sierra = case.get("sierra_code", "").strip() + if not sierra: + continue + + # The test name is the first section key (not a section called "test_name") + order = case.get("_order", []) + if not order: + continue + test_name = order[0] + + safe_name = sanitize_name(test_name) + if not safe_name: + continue + + out_dir = os.path.join(DST_DIR, rel_dir, filename) + os.makedirs(out_dir, exist_ok=True) + out_path = os.path.join(out_dir, f"{safe_name}.sierra") + + with open(out_path, "w") as f: + f.write(sierra + "\n") + count += 1 + + print(f"Extracted {count} sierra files into {DST_DIR}/") + + +if __name__ == "__main__": + main() diff --git a/src/libfuncs/circuit.rs b/src/libfuncs/circuit.rs index 3b6abe6e83..66becdf0ac 100644 --- a/src/libfuncs/circuit.rs +++ b/src/libfuncs/circuit.rs @@ -102,6 +102,8 @@ fn build_init_circuit_data<'ctx, 'this>( metadata: &mut MetadataStorage, info: &SignatureAndTypeConcreteLibfunc, ) -> Result<()> { + metadata.get_or_insert_with(|| ReallocBindingsMeta::new(context, helper)); + let circuit_info = match registry.get_type(&info.ty)? { CoreTypeConcrete::Circuit(CircuitTypeConcrete::Circuit(info)) => &info.circuit_info, _ => return Err(SierraAssertError::BadTypeInfo.into()), @@ -307,6 +309,8 @@ fn build_eval<'ctx, 'this>( metadata: &mut MetadataStorage, info: &SignatureAndTypeConcreteLibfunc, ) -> Result<()> { + metadata.get_or_insert_with(|| ReallocBindingsMeta::new(context, helper)); + let circuit_info = match registry.get_type(&info.ty)? { CoreTypeConcrete::Circuit(CircuitTypeConcrete::Circuit(info)) => &info.circuit_info, _ => return Err(SierraAssertError::BadTypeInfo.into()), diff --git a/src/types/circuit.rs b/src/types/circuit.rs index 5cb6163349..b7dd985998 100644 --- a/src/types/circuit.rs +++ b/src/types/circuit.rs @@ -111,6 +111,8 @@ pub fn build_circuit_accumulator<'ctx>( metadata: &mut MetadataStorage, info: WithSelf, ) -> Result> { + metadata.get_or_insert_with(|| ReallocBindingsMeta::new(context, module)); + let Some(GenericArg::Type(circuit_type_id)) = info.info.long_id.generic_args.first() else { return Err(SierraAssertError::BadTypeInfo.into()); }; @@ -211,6 +213,8 @@ pub fn build_circuit_data<'ctx>( metadata: &mut MetadataStorage, info: WithSelf, ) -> Result> { + metadata.get_or_insert_with(|| ReallocBindingsMeta::new(context, module)); + let Some(GenericArg::Type(circuit_type_id)) = info.info.long_id.generic_args.first() else { return Err(SierraAssertError::BadTypeInfo.into()); }; @@ -302,6 +306,8 @@ pub fn build_circuit_outputs<'ctx>( metadata: &mut MetadataStorage, info: WithSelf, ) -> Result> { + metadata.get_or_insert_with(|| ReallocBindingsMeta::new(context, module)); + let Some(GenericArg::Type(circuit_type_id)) = info.info.long_id.generic_args.first() else { return Err(SierraAssertError::BadTypeInfo.into()); }; diff --git a/tests/tests/e2e_libfuncs.rs b/tests/tests/e2e_libfuncs.rs new file mode 100644 index 0000000000..822a40432c --- /dev/null +++ b/tests/tests/e2e_libfuncs.rs @@ -0,0 +1,60 @@ +use cairo_lang_sierra::ProgramParser; +use cairo_native::context::NativeContext; +use std::fs; +use std::path::Path; + +/// Collects all `.sierra` files under the e2e_sierra directory and compiles each +/// through cairo-native (Sierra → LLVM) to verify they compile without errors. +#[test] +fn compile_e2e_libfunc_sierra() { + let sierra_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("test_data/e2e_sierra"); + assert!( + sierra_dir.exists(), + "{} not found. Run 'git submodule update --init && make test_data/e2e_sierra' first.", + sierra_dir.display() + ); + + let context = NativeContext::new(); + let parser = ProgramParser::new(); + + let mut total = 0; + let mut failures = Vec::new(); + + for entry in walkdir::WalkDir::new(&sierra_dir) { + let entry = entry.unwrap(); + let path = entry.path(); + if path.extension().is_some_and(|e| e == "sierra") { + total += 1; + let source = fs::read_to_string(path).unwrap(); + let rel_path = path.strip_prefix(&sierra_dir).unwrap(); + + let program = match parser.parse(&source) { + Ok(p) => p, + Err(e) => { + failures.push(format!("{}: parse error: {e}", rel_path.display())); + continue; + } + }; + + eprintln!("compiling {}", rel_path.display()); + if let Err(e) = context.compile(&program, false, Some(Default::default()), None) { + failures.push(format!("{}: compile error: {e}", rel_path.display())); + } + } + } + + assert!( + total > 0, + "No .sierra files found in {}", + sierra_dir.display() + ); + + if !failures.is_empty() { + panic!( + "{}/{} e2e libfunc sierra files failed to compile:\n{}", + failures.len(), + total, + failures.join("\n") + ); + } +} diff --git a/tests/tests/mod.rs b/tests/tests/mod.rs index d864318e8d..9526058119 100644 --- a/tests/tests/mod.rs +++ b/tests/tests/mod.rs @@ -5,6 +5,7 @@ pub mod cases; pub mod circuit; pub mod compile_library; pub mod dict; +pub mod e2e_libfuncs; pub mod ec; pub mod felt252; pub mod libfuncs; diff --git a/vendor/cairo b/vendor/cairo new file mode 160000 index 0000000000..6a43d7c941 --- /dev/null +++ b/vendor/cairo @@ -0,0 +1 @@ +Subproject commit 6a43d7c9419481fcb82376447fecf85c44d8fbbf