From d8ac7b69a2bbdd094b6b3f83f74e23288679421f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s?= <47506558+MegaRedHand@users.noreply.github.com> Date: Sat, 15 Jul 2023 10:21:08 -0300 Subject: [PATCH] feat: add WASM example (#1315) * Add example to workspace * Rename example to wasm-demo, and add keywords * Update example * Update readme and links * Make example show result * Remove license, makefile, cairo contract * Add contract source * Update readme * Update main readme * Update changelog * Compile example program in CI * Build example program also in CI * Modify dependencies in Makefile * Compile example program in test_install workflow * Cache correctly * Activate venv before running make check * Set pyenv local version in install scripts * Remove venv activation from test_install * Install requirements also in pyenv * Add pyenv shims to PATH * Add pyenv shims to path (now for real) * Remove unneeded command and add instructions * Fix workflow * Merge install and compile steps * Source script * Apply Oppen's suggestions * Add keywords to cli; use check instead of build * Upload coverage from both runs and run on release * Don't run in release * Shallow clone repo before tests (+prints) * Partition tests to reduce .profraw amount * Fix: don't use '/' in the name * Fix cache path * Print only on debug * Remove fetch-depth: 1 as it's the default * Remove prints --- .github/workflows/rust.yml | 80 ++++++++++++++++++-------- .github/workflows/test_install.yml | 20 +++---- CHANGELOG.md | 2 + Cargo.lock | 72 +++++++++++++++++------ Cargo.toml | 13 ++++- Makefile | 14 +++-- README.md | 3 +- cairo-vm-cli/Cargo.toml | 1 + examples/wasm-demo/Cargo.toml | 36 ++++++++++++ examples/wasm-demo/README.md | 48 ++++++++++++++++ examples/wasm-demo/index.html | 30 ++++++++++ examples/wasm-demo/src/array_sum.cairo | 36 ++++++++++++ examples/wasm-demo/src/lib.rs | 61 ++++++++++++++++++++ examples/wasm-demo/src/utils.rs | 10 ++++ felt/Cargo.toml | 1 + install-scripts/install-debian.sh | 7 +++ install-scripts/install-macos.sh | 7 +++ install-scripts/install-ubuntu.sh | 8 ++- vm/Cargo.toml | 1 + 19 files changed, 388 insertions(+), 62 deletions(-) create mode 100644 examples/wasm-demo/Cargo.toml create mode 100644 examples/wasm-demo/README.md create mode 100644 examples/wasm-demo/index.html create mode 100644 examples/wasm-demo/src/array_sum.cairo create mode 100644 examples/wasm-demo/src/lib.rs create mode 100644 examples/wasm-demo/src/utils.rs diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index ed94638800..9cb48f7208 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -31,7 +31,8 @@ jobs: path: | cairo_programs/**/*.casm cairo_programs/**/*.json - key: ${{ matrix.program-target }}-cache-${{ hashFiles( 'cairo_programs/**/*.cairo' ) }} + examples/wasm-demo/src/array_sum.json + key: ${{ matrix.program-target }}-cache-${{ hashFiles('cairo_programs/**/*.cairo', 'examples/wasm-demo/src/array_sum.cairo') }} restore-keys: ${{ matrix.program-target }}-cache- # This is not pretty, but we need `make` to see the compiled programs are @@ -81,7 +82,8 @@ jobs: path: | cairo_programs/**/*.casm cairo_programs/**/*.json - key: cairo_test_programs-cache-${{ hashFiles( 'cairo_programs/**/*.cairo' ) }} + examples/wasm-demo/src/array_sum.json + key: cairo_test_programs-cache-${{ hashFiles('cairo_programs/**/*.cairo', 'examples/wasm-demo/src/array_sum.cairo') }} - name: Fetch proof programs uses: actions/cache/restore@v3 with: @@ -137,7 +139,8 @@ jobs: path: | cairo_programs/**/*.casm cairo_programs/**/*.json - key: cairo_test_programs-cache-${{ hashFiles( 'cairo_programs/**/*.cairo' ) }} + examples/wasm-demo/src/array_sum.json + key: cairo_test_programs-cache-${{ hashFiles('cairo_programs/**/*.cairo', 'examples/wasm-demo/src/array_sum.cairo') }} - name: Fetch proof programs uses: actions/cache/restore@v3 with: @@ -184,8 +187,8 @@ jobs: - name: Check no-std run: | cd ensure-no_std - cargo build --no-default-features - cargo build + cargo check --no-default-features + cargo check tests: needs: build-programs @@ -193,8 +196,7 @@ jobs: fail-fast: false matrix: special_features: ["", "lambdaworks-felt"] - target: [ test, test-no_std, test-wasm ] - # TODO: features + target: [ test#1, test#2, test-no_std, test-wasm ] name: Run tests runs-on: ubuntu-22.04 steps: @@ -206,12 +208,6 @@ jobs: uses: Swatinem/rust-cache@v2 - name: Checkout uses: actions/checkout@v3 - with: - fetch-depth: 0 - # This is not pretty, but we need `make` to see the compiled programs are - # actually newer than the sources, otherwise it will try to rebuild them - - name: Restore timestamps - uses: chetan/git-restore-mtime-action@v1 - name: Fetch test programs uses: actions/cache/restore@v3 @@ -219,7 +215,8 @@ jobs: path: | cairo_programs/**/*.casm cairo_programs/**/*.json - key: cairo_test_programs-cache-${{ hashFiles( 'cairo_programs/**/*.cairo' ) }} + examples/wasm-demo/src/array_sum.json + key: cairo_test_programs-cache-${{ hashFiles('cairo_programs/**/*.cairo', 'examples/wasm-demo/src/array_sum.cairo') }} - name: Fetch proof programs uses: actions/cache/restore@v3 with: @@ -239,15 +236,23 @@ jobs: uses: taiki-e/install-action@v2 with: tool: cargo-nextest,cargo-llvm-cov,wasm-pack - - name: Run + + - name: Run ${{ matrix.target }} run: | + # this splits the `test#1` into `test` and `1` + export MATRIX_TARGET=${{ matrix.target }} + export NAME=${MATRIX_TARGET%#*} + export PARTITION=${MATRIX_TARGET#*#} # FIXME: we need to update the Makefile to do this correctly - case ${{ matrix.target }} in + case ${NAME} in 'test') - cargo llvm-cov nextest --lcov --output-path lcov-${{ matrix.target }}.info --workspace --features "cairo-1-hints, test_utils, ${{ matrix.special_features }}" + cargo llvm-cov nextest --lcov --output-path lcov-${{ matrix.target }}-${{ matrix.special_features }}.info \ + --partition count:${PARTITION}/2 \ + --workspace --features "cairo-1-hints, test_utils, ${{ matrix.special_features }}" ;; 'test-no_std') - cargo llvm-cov nextest --lcov --output-path lcov-${{ matrix.target }}.info --workspace --no-default-features --features "${{ matrix.special_features }}" + cargo llvm-cov nextest --lcov --output-path lcov-${{ matrix.target }}-${{ matrix.special_features }}.info \ + --workspace --no-default-features --features "${{ matrix.special_features }}" ;; 'test-wasm') # NOTE: release mode is needed to avoid "too many locals" error @@ -259,8 +264,8 @@ jobs: if: matrix.target != 'test-wasm' uses: actions/cache/save@v3 with: - path: lcov-${{ matrix.target }}.info - key: codecov-cache-${{ matrix.target }}-${{ github.sha }} + path: lcov-${{ matrix.target }}-${{ matrix.special_features }}.info + key: codecov-cache-${{ matrix.target }}-${{ matrix.special_features }}-${{ github.sha }} build-release: @@ -399,17 +404,42 @@ jobs: - name: Checkout uses: actions/checkout@v3 - - name: Fetch results for tests with stdlib + - name: Fetch results for tests with stdlib (part. 1) + uses: actions/cache/restore@v3 + with: + path: lcov-test#1-.info + key: codecov-cache-test#1--${{ github.sha }} + fail-on-cache-miss: true + - name: Fetch results for tests with stdlib (part. 2) uses: actions/cache/restore@v3 with: - path: lcov-test.info - key: codecov-cache-test-${{ github.sha }} + path: lcov-test#2-.info + key: codecov-cache-test#2--${{ github.sha }} fail-on-cache-miss: true - name: Fetch results for tests without stdlib uses: actions/cache/restore@v3 with: - path: lcov-test-no_std.info - key: codecov-cache-test-no_std-${{ github.sha }} + path: lcov-test-no_std-.info + key: codecov-cache-test-no_std--${{ github.sha }} + fail-on-cache-miss: true + + - name: Fetch results for tests with stdlib (w/lambdaworks; part. 1) + uses: actions/cache/restore@v3 + with: + path: lcov-test#1-lambdaworks-felt.info + key: codecov-cache-test#1-lambdaworks-felt-${{ github.sha }} + fail-on-cache-miss: true + - name: Fetch results for tests with stdlib (w/lambdaworks; part. 2) + uses: actions/cache/restore@v3 + with: + path: lcov-test#2-lambdaworks-felt.info + key: codecov-cache-test#2-lambdaworks-felt-${{ github.sha }} + fail-on-cache-miss: true + - name: Fetch results for tests without stdlib (w/lambdaworks) + uses: actions/cache/restore@v3 + with: + path: lcov-test-no_std-lambdaworks-felt.info + key: codecov-cache-test-no_std-lambdaworks-felt-${{ github.sha }} fail-on-cache-miss: true - name: Upload coverage to codecov.io diff --git a/.github/workflows/test_install.yml b/.github/workflows/test_install.yml index 07ab75c63c..b167807f80 100644 --- a/.github/workflows/test_install.yml +++ b/.github/workflows/test_install.yml @@ -31,11 +31,11 @@ jobs: python-version: '3.9' cache: 'pip' - - name: Install dependencies - run: bash install.sh - - - name: Compile project - run: cargo check + # NOTE: we do it in one step because GitHub cleans the PATH between steps + - name: Install dependencies and compile project + run: | + source install.sh + make check install_debian: name: "Install on debian-11" @@ -77,10 +77,8 @@ jobs: key: ${{ runner.os }}-python-install-${{ github.sha }} restore-keys: ${{ runner.os }}-python-install- - - name: Install dependencies - run: bash install.sh - - - name: Compile project + # NOTE: we do it in one step because GitHub cleans the PATH between steps + - name: Install dependencies and compile project run: | - . "$HOME/.cargo/env" - cargo check + source install.sh + make check diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c67fa2113..66e2e0b3dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ #### Upcoming Changes +* feat: updated the old WASM example and moved it to [`examples/wasm-demo`](examples/wasm-demo/) [#1315](https://github.com/lambdaclass/cairo-vm/pull/1315) + * feat: add `arbitrary` feature to enable arbitrary derive in `Program` and `CairoRunConfig` * feat(fuzzing): add `arbitrary` feature to enable arbitrary derive in `Program` and `CairoRunConfig` [#1306](https://github.com/lambdaclass/cairo-vm/pull/1306) [#1330](https://github.com/lambdaclass/cairo-vm/pull/1330) diff --git a/Cargo.lock b/Cargo.lock index db37539056..b829bec898 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,7 +19,7 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "once_cell", "version_check", ] @@ -784,6 +784,12 @@ version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + [[package]] name = "cfg-if" version = "1.0.0" @@ -881,7 +887,7 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "wasm-bindgen", ] @@ -957,7 +963,7 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "crossbeam-utils", ] @@ -967,7 +973,7 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "crossbeam-epoch", "crossbeam-utils", ] @@ -979,7 +985,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" dependencies = [ "autocfg", - "cfg-if", + "cfg-if 1.0.0", "crossbeam-utils", "memoffset", "scopeguard", @@ -991,7 +997,7 @@ version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -1066,7 +1072,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "dirs-sys-next", ] @@ -1289,7 +1295,7 @@ version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "js-sys", "libc", "wasi", @@ -1455,7 +1461,7 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -1646,6 +1652,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "memory_units" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" + [[package]] name = "mimalloc" version = "0.1.37" @@ -1854,7 +1866,7 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "instant", "libc", "redox_syscall 0.2.16", @@ -1868,7 +1880,7 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "redox_syscall 0.3.5", "smallvec", @@ -2183,7 +2195,7 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290ca1a1c8ca7edb7c3283bd44dc35dd54fdec6253a3912e201ba1072018fca8" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "proc-macro2", "quote", "rustc_version", @@ -2386,7 +2398,7 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "cpufeatures", "digest", ] @@ -2545,9 +2557,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "subtle" -version = "2.5.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" @@ -2584,7 +2596,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" dependencies = [ "autocfg", - "cfg-if", + "cfg-if 1.0.0", "fastrand", "redox_syscall 0.3.5", "rustix 0.37.23", @@ -2775,7 +2787,7 @@ version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "wasm-bindgen-macro", ] @@ -2800,7 +2812,7 @@ version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "js-sys", "wasm-bindgen", "web-sys", @@ -2859,6 +2871,18 @@ dependencies = [ "quote", ] +[[package]] +name = "wasm-demo" +version = "0.8.2" +dependencies = [ + "cairo-vm", + "console_error_panic_hook", + "serde_json", + "wasm-bindgen", + "wasm-bindgen-test", + "wee_alloc", +] + [[package]] name = "web-sys" version = "0.3.64" @@ -2869,6 +2893,18 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "wee_alloc" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "memory_units", + "winapi", +] + [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index efb03e7e38..171c7683cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,11 @@ [workspace] -members = ["cairo-vm-cli", "felt", "vm", "hint_accountant"] +members = [ + "cairo-vm-cli", + "felt", + "vm", + "hint_accountant", + "examples/wasm-demo", +] exclude = ["ensure-no_std"] # Explicitly set the resolver to the default for edition >= 2021 @@ -12,6 +18,7 @@ edition = "2021" license = "Apache-2.0" repository = "https://github.com/lambdaclass/cairo-vm/" readme = "README.md" +keywords = ["starknet", "cairo", "vm", "wasm", "no_std"] [workspace.dependencies] felt = { package = "cairo-felt", path = "./felt", version = "0.8.2", default-features = false, features = [ @@ -66,3 +73,7 @@ arbitrary = { version = "1.3.0", features = ["derive"] } [profile.release] lto = "fat" + +[profile.release.package.wasm-demo] +# Tell `rustc` to optimize for small code size. +opt-level = "s" diff --git a/Makefile b/Makefile index 63ffd65c58..35f3f7f706 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,8 @@ STARKNET_SIERRA_COMPILE:=cairo/target/release/starknet-sierra-compile compare_trace_memory_proof compare_trace_proof compare_memory_proof \ cairo_bench_programs cairo_proof_programs cairo_test_programs \ cairo_trace cairo-vm_trace cairo_proof_trace cairo-vm_proof_trace \ - $(RELBIN) $(DBGBIN) $(STARKNET_COMPILE) $(STARKNET_SIERRA_COMPILE) + $(RELBIN) $(DBGBIN) $(STARKNET_COMPILE) $(STARKNET_SIERRA_COMPILE) \ + example_programs # Proof mode consumes too much memory with cairo-lang to execute # two instances at the same time in the CI without getting killed @@ -151,10 +152,16 @@ build: $(RELBIN) run: cargo run -p cairo-vm-cli -check: +check: example_programs cargo check -cairo_test_programs: $(COMPILED_TESTS) $(COMPILED_BAD_TESTS) $(COMPILED_NORETROCOMPAT_TESTS) +examples/wasm-demo/src/array_sum.json: examples/wasm-demo/src/array_sum.cairo + cairo-compile --no_debug_info examples/wasm-demo/src/array_sum.cairo \ + --output examples/wasm-demo/src/array_sum.json + +example_programs: examples/wasm-demo/src/array_sum.json + +cairo_test_programs: $(COMPILED_TESTS) $(COMPILED_BAD_TESTS) $(COMPILED_NORETROCOMPAT_TESTS) example_programs cairo_proof_programs: $(COMPILED_PROOF_TESTS) cairo_bench_programs: $(COMPILED_BENCHES) cairo_1_test_contracts: $(COMPILED_CASM_CONTRACTS) @@ -173,7 +180,6 @@ test-wasm: $(COMPILED_PROOF_TESTS) $(COMPILED_TESTS) $(COMPILED_BAD_TESTS) $(COM # NOTE: release mode is needed to avoid "too many locals" error wasm-pack test --release --node vm --no-default-features - check-fmt: cargo fmt --all -- --check cargo fmt --manifest-path fuzzer/Cargo.toml --all -- --check diff --git a/README.md b/README.md index 96d7edb448..f49f8fb79f 100644 --- a/README.md +++ b/README.md @@ -197,8 +197,7 @@ When using cairo-vm with the Starknet devnet there are additional parameters tha ### WebAssembly Demo -A demo on how to use `cairo-vm` with WebAssembly can be found -[here](https://github.com/lambdaclass/cairo-rs-wasm). +A demo on how to use `cairo-vm` with WebAssembly can be found in [`examples/wasm-demo`](examples/wasm-demo/) ### Testing diff --git a/cairo-vm-cli/Cargo.toml b/cairo-vm-cli/Cargo.toml index 7f69b2dd5c..149e5619e8 100644 --- a/cairo-vm-cli/Cargo.toml +++ b/cairo-vm-cli/Cargo.toml @@ -5,6 +5,7 @@ edition.workspace = true license.workspace = true repository.workspace = true readme.workspace = true +keywords.workspace = true [dependencies] bincode = { version = "2.0.0-rc.3", tag = "v2.0.0-rc.3", git = "https://github.com/bincode-org/bincode.git" } diff --git a/examples/wasm-demo/Cargo.toml b/examples/wasm-demo/Cargo.toml new file mode 100644 index 0000000000..d0ff41c5c6 --- /dev/null +++ b/examples/wasm-demo/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "wasm-demo" +description = "A demo using cairo-vm in a WASM environment" +version.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +readme = "README.md" +keywords.workspace = true +publish = false + +[lib] +crate-type = ["cdylib", "rlib"] + +[features] +default = ["console_error_panic_hook"] + +[dependencies] +serde_json.workspace = true +wasm-bindgen = "0.2.87" + +# The `console_error_panic_hook` crate provides better debugging of panics by +# logging them with `console.error`. This is great for development, but requires +# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for +# code size when deploying. +console_error_panic_hook = { version = "0.1.6", optional = true } + +# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size +# compared to the default allocator's ~10K. It is slower than the default +# allocator, however. +wee_alloc = { version = "0.4.5", optional = true } + +cairo-vm = { workspace = true } + +[dev-dependencies] +wasm-bindgen-test = "0.3.34" diff --git a/examples/wasm-demo/README.md b/examples/wasm-demo/README.md new file mode 100644 index 0000000000..e8d58c13eb --- /dev/null +++ b/examples/wasm-demo/README.md @@ -0,0 +1,48 @@ +# Demo of `cairo-rs` on WebAssembly + +While cairo-rs is compatible with WebAssembly, it doesn't implement any bindings to it. +Instead, create a new WebAssembly crate with cairo-rs as a dependency and implement the required functionality there. + +Since mimalloc is not automatically compilable to WebAssembly, the cairo-rs dependency should disable the default features, which will in turn disable mimalloc. + +A working example is provided in this repository. + +## Dependencies + +To compile and run the example you need: + +- a Cairo 0 compiler +- the _wasm-pack_ crate +- some HTTP server (for example: the `live-server` npm module) + +> **Note** +> The first two dependencies can be installed via the repository's installation script (see ["Installation script"](../../README.md#installation-script)) + +## Building + +To build the example, first compile your Cairo program: + +```sh +cairo-compile src/array_sum.cairo --no_debug_info --output src/array_sum.json +``` + +And then the WebAssembly package: + +```sh +wasm-pack build --target=web +``` + +This will generate a javascript module that is directly loadable by the browser. + +## Running + +To run the example webpage, you need to run an HTTP server. +For example, using the _live-server_ npm module: + +```sh +# while in /examples/wasm-demo +npx live-server +``` + +> **Warning** +> Trying to run `index.html` directly (i.e. URL starts with `file://`) will result in a CORS error. diff --git a/examples/wasm-demo/index.html b/examples/wasm-demo/index.html new file mode 100644 index 0000000000..5d0f1b7676 --- /dev/null +++ b/examples/wasm-demo/index.html @@ -0,0 +1,30 @@ + + + + + + + + + Cairo WebAssembly Demo + + + + + +

Result:

+ + + diff --git a/examples/wasm-demo/src/array_sum.cairo b/examples/wasm-demo/src/array_sum.cairo new file mode 100644 index 0000000000..7d524191ae --- /dev/null +++ b/examples/wasm-demo/src/array_sum.cairo @@ -0,0 +1,36 @@ +%builtins output + +from starkware.cairo.common.alloc import alloc +from starkware.cairo.common.serialize import serialize_word + +// Computes the sum of the memory elements at addresses: +// arr + 0, arr + 1, ..., arr + (size - 1). +func array_sum(arr: felt*, size) -> (sum: felt) { + if (size == 0) { + return (sum=0); + } + + // size is not zero. + let (sum_of_rest) = array_sum(arr=arr + 1, size=size - 1); + return (sum=[arr] + sum_of_rest); +} + +func main{output_ptr: felt*}() { + const ARRAY_SIZE = 3; + + // Allocate an array. + let (ptr) = alloc(); + + // Populate some values in the array. + assert [ptr] = 9; + assert [ptr + 1] = 16; + assert [ptr + 2] = 25; + + // Call array_sum to compute the sum of the elements. + let (sum) = array_sum(arr=ptr, size=ARRAY_SIZE); + + // Write the sum to the program output. + serialize_word(sum); + + return (); +} diff --git a/examples/wasm-demo/src/lib.rs b/examples/wasm-demo/src/lib.rs new file mode 100644 index 0000000000..3f2f54f8b6 --- /dev/null +++ b/examples/wasm-demo/src/lib.rs @@ -0,0 +1,61 @@ +mod utils; + +use cairo_vm::{ + cairo_run::{cairo_run, CairoRunConfig}, + hint_processor::builtin_hint_processor::builtin_hint_processor_definition::BuiltinHintProcessor, +}; +use wasm_bindgen::prelude::*; + +// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global +// allocator. +#[cfg(feature = "wee_alloc")] +#[global_allocator] +static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(js_namespace = console)] + fn log(msg: &str); +} + +#[cfg(feature = "console_error_panic_hook")] +#[wasm_bindgen(start)] +pub fn start() { + crate::utils::set_panic_hook(); +} + +// TODO: check why this is needed. Seems wasm-bindgen expects us to use +// `std::error::Error` even if it's not yet in `core` +macro_rules! wrap_error { + ($xp: expr) => { + $xp.map_err(|e| JsError::new(e.to_string().as_str())) + }; +} + +#[wasm_bindgen(js_name = runCairoProgram)] +pub fn run_cairo_program() -> Result { + const PROGRAM_JSON: &[u8] = include_bytes!("./array_sum.json"); + + let mut hint_executor = BuiltinHintProcessor::new_empty(); + + let cairo_run_config = CairoRunConfig { + layout: "all_cairo", + relocate_mem: true, + trace_enabled: true, + ..Default::default() + }; + + let (_runner, mut vm) = wrap_error!(cairo_run( + PROGRAM_JSON, + &cairo_run_config, + &mut hint_executor + ))?; + + let mut buffer = String::new(); + + wrap_error!(vm.write_output(&mut buffer))?; + + log(buffer.as_str()); + + Ok(buffer) +} diff --git a/examples/wasm-demo/src/utils.rs b/examples/wasm-demo/src/utils.rs new file mode 100644 index 0000000000..b1d7929dc9 --- /dev/null +++ b/examples/wasm-demo/src/utils.rs @@ -0,0 +1,10 @@ +pub fn set_panic_hook() { + // When the `console_error_panic_hook` feature is enabled, we can call the + // `set_panic_hook` function at least once during initialization, and then + // we will get better error messages if our code ever panics. + // + // For more details see + // https://github.com/rustwasm/console_error_panic_hook#readme + #[cfg(feature = "console_error_panic_hook")] + console_error_panic_hook::set_once(); +} diff --git a/felt/Cargo.toml b/felt/Cargo.toml index fd41f03d44..fac6101f80 100644 --- a/felt/Cargo.toml +++ b/felt/Cargo.toml @@ -6,6 +6,7 @@ edition.workspace = true license.workspace = true repository.workspace = true readme.workspace = true +keywords.workspace = true [features] default = ["std"] diff --git a/install-scripts/install-debian.sh b/install-scripts/install-debian.sh index fb050edf97..9d42d88fb1 100755 --- a/install-scripts/install-debian.sh +++ b/install-scripts/install-debian.sh @@ -31,3 +31,10 @@ eval "$(pyenv init -)" pyenv -v make deps + +pyenv local 3.9.15 + +pip install -r requirements.txt + +echo "-- You need to follow these instructions to finish installing pyenv: --" +pyenv init || true diff --git a/install-scripts/install-macos.sh b/install-scripts/install-macos.sh index 09da2b33a4..c42d7c7307 100755 --- a/install-scripts/install-macos.sh +++ b/install-scripts/install-macos.sh @@ -15,3 +15,10 @@ brew install pyenv gmp pyenv -v make deps-macos + +pyenv local 3.9.15 + +pip install -r requirements.txt + +echo "-- You need to follow these instructions to finish installing pyenv: --" +pyenv init || true diff --git a/install-scripts/install-ubuntu.sh b/install-scripts/install-ubuntu.sh index 7141928865..9db1fe2b53 100755 --- a/install-scripts/install-ubuntu.sh +++ b/install-scripts/install-ubuntu.sh @@ -24,8 +24,14 @@ export PYENV_ROOT="$HOME/.pyenv" command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH" eval "$(pyenv init -)" - # Make sure pyenv has been installed correctly pyenv -v make deps + +pyenv local 3.9.15 + +pip install -r requirements.txt + +echo "-- You need to follow these instructions to finish installing pyenv: --" +pyenv init || true diff --git a/vm/Cargo.toml b/vm/Cargo.toml index d3937a0d02..d1d5a77ab3 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -6,6 +6,7 @@ edition.workspace = true license.workspace = true repository.workspace = true readme.workspace = true +keywords.workspace = true [features] default = ["std", "with_mimalloc"]