Integrate Airbender (#175)

This commit is contained in:
Han
2025-10-21 11:21:26 +08:00
committed by GitHub
parent 577f97165e
commit 11020823a6
29 changed files with 1901 additions and 29 deletions

View File

@@ -0,0 +1,18 @@
name: Test and clippy Airbender
on:
push:
branches:
- master
pull_request:
jobs:
test:
uses: ./.github/workflows/test-zkvm.yml
permissions:
contents: read
packages: write
with:
zkvm: airbender
toolchain: nightly
skip_prove_test: true

View File

@@ -173,7 +173,8 @@ jobs:
--rm \
--interactive \
--volume ${{ github.workspace }}:/ere \
--volume $HOME/.cargo:/root/.cargo \
--volume $HOME/.cargo/registry:/usr/local/cargo/registry \
--volume $HOME/.cargo/git:/usr/local/cargo/git \
--workdir /ere \
${{ needs.build_image.outputs.base_zkvm_image }} \
/bin/bash"
@@ -186,8 +187,9 @@ jobs:
cargo clippy --package ere-compiler --features ${{ inputs.zkvm }} \$OPTIONS
cargo clippy --package ere-server --features ${{ inputs.zkvm }} \$OPTIONS
chown -R $(id -u):$(id -g) ~/.cargo
chown -R $(id -u):$(id -g) target
time chown -R $(id -u):$(id -g) /usr/local/cargo/registry
time chown -R $(id -u):$(id -g) /usr/local/cargo/git
time chown -R $(id -u):$(id -g) target
EOF
test_via_docker:
@@ -221,7 +223,8 @@ jobs:
--rm \
--interactive \
--volume ${{ github.workspace }}:/ere \
--volume $HOME/.cargo:/root/.cargo \
--volume $HOME/.cargo/registry:/usr/local/cargo/registry \
--volume $HOME/.cargo/git:/usr/local/cargo/git \
--workdir /ere \
${{ needs.build_image.outputs.base_zkvm_image }} \
/bin/bash"
@@ -232,8 +235,9 @@ jobs:
cargo test --release --package ere-${{ inputs.zkvm }} \
-- ${{ inputs.skip_prove_test && '--skip prove' || '' }}
chown -R $(id -u):$(id -g) ~/.cargo
chown -R $(id -u):$(id -g) target
time chown -R $(id -u):$(id -g) /usr/local/cargo/registry
time chown -R $(id -u):$(id -g) /usr/local/cargo/git
time chown -R $(id -u):$(id -g) target
EOF
test_ere_dockerized:

570
Cargo.lock generated
View File

@@ -23,6 +23,21 @@ dependencies = [
"num-traits",
]
[[package]]
name = "addr2line"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678"
dependencies = [
"cpp_demangle",
"fallible-iterator",
"gimli 0.29.0",
"memmap2",
"object 0.35.0",
"rustc-demangle",
"smallvec",
]
[[package]]
name = "addr2line"
version = "0.24.2"
@@ -31,9 +46,9 @@ checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
dependencies = [
"cpp_demangle",
"fallible-iterator",
"gimli",
"gimli 0.31.1",
"memmap2",
"object",
"object 0.36.7",
"rustc-demangle",
"smallvec",
"typed-arena",
@@ -63,6 +78,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
dependencies = [
"cfg-if",
"const-random",
"getrandom 0.3.3",
"once_cell",
"version_check",
"zerocopy",
@@ -1851,11 +1868,11 @@ version = "0.3.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
dependencies = [
"addr2line",
"addr2line 0.24.2",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"object 0.36.7",
"rustc-demangle",
"serde",
"windows-targets 0.52.6",
@@ -1928,6 +1945,17 @@ dependencies = [
"serde",
]
[[package]]
name = "bigint_with_control"
version = "0.1.0"
source = "git+https://github.com/matter-labs/zksync-airbender?tag=v0.5.0#a3c74040d2f994608f451550232392333400bbf1"
dependencies = [
"prover",
"serde",
"serde_json",
"verifier_generator",
]
[[package]]
name = "bincode"
version = "1.3.3"
@@ -2103,6 +2131,17 @@ dependencies = [
"digest 0.10.7",
]
[[package]]
name = "blake2_with_compression"
version = "0.1.0"
source = "git+https://github.com/matter-labs/zksync-airbender?tag=v0.5.0#a3c74040d2f994608f451550232392333400bbf1"
dependencies = [
"prover",
"serde",
"serde_json",
"verifier_generator",
]
[[package]]
name = "blake2b_simd"
version = "1.0.3"
@@ -2114,6 +2153,14 @@ dependencies = [
"constant_time_eq 0.3.1",
]
[[package]]
name = "blake2s_u32"
version = "0.1.0"
source = "git+https://github.com/matter-labs/zksync-airbender?tag=v0.5.0#a3c74040d2f994608f451550232392333400bbf1"
dependencies = [
"unroll",
]
[[package]]
name = "blake3"
version = "1.8.2"
@@ -2443,6 +2490,16 @@ dependencies = [
"inout",
]
[[package]]
name = "circuit_common"
version = "0.1.0"
source = "git+https://github.com/matter-labs/zksync-airbender?tag=v0.5.0#a3c74040d2f994608f451550232392333400bbf1"
dependencies = [
"fft",
"field",
"worker",
]
[[package]]
name = "clang-sys"
version = "1.8.1"
@@ -2612,6 +2669,26 @@ version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
[[package]]
name = "const-random"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359"
dependencies = [
"const-random-macro",
]
[[package]]
name = "const-random-macro"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e"
dependencies = [
"getrandom 0.2.16",
"once_cell",
"tiny-keccak",
]
[[package]]
name = "const_format"
version = "0.2.34"
@@ -2859,6 +2936,31 @@ dependencies = [
"typenum",
]
[[package]]
name = "cs"
version = "0.1.0"
source = "git+https://github.com/matter-labs/zksync-airbender?tag=v0.5.0#a3c74040d2f994608f451550232392333400bbf1"
dependencies = [
"arrayvec",
"bincode 1.3.3",
"blake2s_u32",
"derivative",
"divrem",
"field",
"itertools 0.14.0",
"poseidon2",
"proc-macro2",
"quote",
"rayon",
"seq-macro",
"serde",
"serde_json",
"smallvec",
"super-seq-macro",
"syn 2.0.101",
"type-map",
]
[[package]]
name = "csv"
version = "1.3.1"
@@ -3347,6 +3449,12 @@ version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8975ffdaa0ef3661bfe02dbdcc06c9f829dfafe6a3c474de366a8d5e44276921"
[[package]]
name = "divrem"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69dde51e8fef5e12c1d65e0929b03d66e4c0c18282bc30ed2ca050ad6f44dd82"
[[package]]
name = "docker-generate"
version = "0.1.3"
@@ -3619,6 +3727,21 @@ dependencies = [
"typeid",
]
[[package]]
name = "ere-airbender"
version = "0.0.14"
dependencies = [
"bincode 2.0.1",
"ere-build-utils",
"ere-compile-utils",
"ere-test-utils",
"ere-zkvm-interface",
"execution_utils",
"serde_json",
"tempfile",
"thiserror 2.0.12",
]
[[package]]
name = "ere-build-utils"
version = "0.0.14"
@@ -3644,6 +3767,7 @@ dependencies = [
"anyhow",
"bincode 2.0.1",
"clap",
"ere-airbender",
"ere-jolt",
"ere-miden",
"ere-nexus",
@@ -3796,6 +3920,7 @@ dependencies = [
"anyhow",
"bincode 2.0.1",
"clap",
"ere-airbender",
"ere-jolt",
"ere-miden",
"ere-nexus",
@@ -4227,6 +4352,19 @@ dependencies = [
"pin-project-lite",
]
[[package]]
name = "execution_utils"
version = "0.1.0"
source = "git+https://github.com/matter-labs/zksync-airbender?tag=v0.5.0#a3c74040d2f994608f451550232392333400bbf1"
dependencies = [
"clap",
"risc_v_simulator",
"serde",
"serde_json",
"trace_and_split",
"verifier_common",
]
[[package]]
name = "eyre"
version = "0.6.12"
@@ -4310,12 +4448,48 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "fft"
version = "0.1.0"
source = "git+https://github.com/matter-labs/zksync-airbender?tag=v0.5.0#a3c74040d2f994608f451550232392333400bbf1"
dependencies = [
"field",
"itertools 0.14.0",
"rayon",
"seq-macro",
"trace_holder",
"unroll",
"worker",
]
[[package]]
name = "fiat-crypto"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
[[package]]
name = "field"
version = "0.1.0"
source = "git+https://github.com/matter-labs/zksync-airbender?tag=v0.5.0#a3c74040d2f994608f451550232392333400bbf1"
dependencies = [
"rand 0.9.2",
"seq-macro",
"serde",
]
[[package]]
name = "final_reduced_risc_v_machine"
version = "0.1.0"
source = "git+https://github.com/matter-labs/zksync-airbender?tag=v0.5.0#a3c74040d2f994608f451550232392333400bbf1"
dependencies = [
"circuit_common",
"prover",
"serde",
"serde_json",
"verifier_generator",
]
[[package]]
name = "findshlibs"
version = "0.10.2"
@@ -4692,6 +4866,16 @@ dependencies = [
"syn 2.0.101",
]
[[package]]
name = "gimli"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
dependencies = [
"fallible-iterator",
"stable_deref_trait",
]
[[package]]
name = "gimli"
version = "0.31.1"
@@ -5549,6 +5733,28 @@ dependencies = [
"web-time",
]
[[package]]
name = "inferno"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e96d2465363ed2d81857759fc864cf6bb7997f79327aec028d65bd7989393685"
dependencies = [
"ahash",
"clap",
"crossbeam-channel",
"crossbeam-utils",
"dashmap",
"env_logger",
"indexmap 2.10.0",
"itoa",
"log",
"num-format",
"once_cell",
"quick-xml",
"rgb",
"str_stack",
]
[[package]]
name = "inout"
version = "0.1.4"
@@ -5969,6 +6175,20 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "lib-rv32-asm"
version = "0.2.0"
source = "git+https://github.com/shamatar/lib-rv32.git#869f82795f82314f17014a04fb6f2c31808e6b42"
dependencies = [
"lib-rv32-common",
"log",
]
[[package]]
name = "lib-rv32-common"
version = "0.2.0"
source = "git+https://github.com/shamatar/lib-rv32.git#869f82795f82314f17014a04fb6f2c31808e6b42"
[[package]]
name = "libc"
version = "0.2.175"
@@ -6128,6 +6348,18 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
[[package]]
name = "machine_without_signed_mul_div"
version = "0.1.0"
source = "git+https://github.com/matter-labs/zksync-airbender?tag=v0.5.0#a3c74040d2f994608f451550232392333400bbf1"
dependencies = [
"circuit_common",
"prover",
"serde",
"serde_json",
"verifier_generator",
]
[[package]]
name = "macro-string"
version = "0.1.4"
@@ -6868,6 +7100,11 @@ dependencies = [
"minimal-lexical",
]
[[package]]
name = "non_determinism_source"
version = "0.1.0"
source = "git+https://github.com/matter-labs/zksync-airbender?tag=v0.5.0#a3c74040d2f994608f451550232392333400bbf1"
[[package]]
name = "ntapi"
version = "0.4.1"
@@ -6967,6 +7204,16 @@ dependencies = [
"syn 2.0.101",
]
[[package]]
name = "num-format"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3"
dependencies = [
"arrayvec",
"itoa",
]
[[package]]
name = "num-integer"
version = "0.1.46"
@@ -7161,6 +7408,17 @@ dependencies = [
"malloc_buf",
]
[[package]]
name = "object"
version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e"
dependencies = [
"flate2",
"memchr",
"ruzstd 0.6.0",
]
[[package]]
name = "object"
version = "0.36.7"
@@ -7172,7 +7430,7 @@ dependencies = [
"hashbrown 0.15.3",
"indexmap 2.10.0",
"memchr",
"ruzstd",
"ruzstd 0.7.3",
]
[[package]]
@@ -7180,6 +7438,9 @@ name = "once_cell"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
dependencies = [
"portable-atomic",
]
[[package]]
name = "open-fastrlp"
@@ -9919,6 +10180,17 @@ dependencies = [
"portable-atomic",
]
[[package]]
name = "poseidon2"
version = "0.1.0"
source = "git+https://github.com/matter-labs/zksync-airbender?tag=v0.5.0#a3c74040d2f994608f451550232392333400bbf1"
dependencies = [
"field",
"non_determinism_source",
"rand 0.9.2",
"unroll",
]
[[package]]
name = "postcard"
version = "1.1.1"
@@ -10282,6 +10554,34 @@ dependencies = [
"prost 0.13.5",
]
[[package]]
name = "prover"
version = "0.1.0"
source = "git+https://github.com/matter-labs/zksync-airbender?tag=v0.5.0#a3c74040d2f994608f451550232392333400bbf1"
dependencies = [
"bit-set 0.8.0",
"blake2s_u32",
"cs",
"fft",
"field",
"itertools 0.14.0",
"lib-rv32-asm",
"non_determinism_source",
"poseidon2",
"proc-macro2",
"quote",
"rayon",
"risc_v_simulator",
"seq-macro",
"serde",
"serde_json",
"syn 2.0.101",
"trace_holder",
"transcript",
"unroll",
"worker",
]
[[package]]
name = "puffin"
version = "0.19.1"
@@ -10317,6 +10617,15 @@ version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quick-xml"
version = "0.37.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb"
dependencies = [
"memchr",
]
[[package]]
name = "quinn"
version = "0.11.8"
@@ -10621,6 +10930,30 @@ dependencies = [
"thiserror 2.0.12",
]
[[package]]
name = "reduced_risc_v_log_23_machine"
version = "0.1.0"
source = "git+https://github.com/matter-labs/zksync-airbender?tag=v0.5.0#a3c74040d2f994608f451550232392333400bbf1"
dependencies = [
"circuit_common",
"prover",
"serde",
"serde_json",
"verifier_generator",
]
[[package]]
name = "reduced_risc_v_machine"
version = "0.1.0"
source = "git+https://github.com/matter-labs/zksync-airbender?tag=v0.5.0#a3c74040d2f994608f451550232392333400bbf1"
dependencies = [
"circuit_common",
"prover",
"serde",
"serde_json",
"verifier_generator",
]
[[package]]
name = "regex"
version = "1.11.1"
@@ -10798,6 +11131,43 @@ dependencies = [
"subtle",
]
[[package]]
name = "rgb"
version = "0.8.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce"
dependencies = [
"bytemuck",
]
[[package]]
name = "rhai"
version = "1.23.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "527390cc333a8d2cd8237890e15c36518c26f8b54c903d86fc59f42f08d25594"
dependencies = [
"ahash",
"bitflags 2.9.0",
"instant",
"num-traits",
"once_cell",
"rhai_codegen",
"smallvec",
"smartstring",
"thin-vec",
]
[[package]]
name = "rhai_codegen"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4322a2a4e8cf30771dd9f27f7f37ca9ac8fe812dddd811096a98483080dabe6"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
]
[[package]]
name = "ring"
version = "0.16.20"
@@ -11120,7 +11490,7 @@ version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fcce11648a9ff60b8e7af2f0ce7fbf8d25275ab6d414cc91b9da69ee75bc978"
dependencies = [
"addr2line",
"addr2line 0.24.2",
"anyhow",
"bincode 1.3.3",
"borsh",
@@ -11131,13 +11501,13 @@ dependencies = [
"enum-map",
"gdbstub",
"gdbstub_arch",
"gimli",
"gimli 0.31.1",
"hex",
"keccak",
"lazy-regex",
"num-bigint 0.4.6",
"num-traits",
"object",
"object 0.36.7",
"prost 0.13.5",
"rand 0.9.2",
"rayon",
@@ -11179,6 +11549,37 @@ dependencies = [
"stability",
]
[[package]]
name = "risc_v_cycles"
version = "0.1.0"
source = "git+https://github.com/matter-labs/zksync-airbender?tag=v0.5.0#a3c74040d2f994608f451550232392333400bbf1"
dependencies = [
"circuit_common",
"prover",
"serde",
"serde_json",
"verifier_generator",
]
[[package]]
name = "risc_v_simulator"
version = "0.1.0"
source = "git+https://github.com/matter-labs/zksync-airbender?tag=v0.5.0#a3c74040d2f994608f451550232392333400bbf1"
dependencies = [
"addr2line 0.22.0",
"blake2s_u32",
"cs",
"field",
"inferno",
"memmap2",
"object 0.36.7",
"poseidon2",
"rand 0.9.2",
"ringbuffer",
"ruint",
"serde",
]
[[package]]
name = "rlp"
version = "0.5.2"
@@ -11517,6 +11918,17 @@ dependencies = [
"wait-timeout",
]
[[package]]
name = "ruzstd"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5174a470eeb535a721ae9fdd6e291c2411a906b96592182d05217591d5c5cf7b"
dependencies = [
"byteorder",
"derive_more 0.99.20",
"twox-hash",
]
[[package]]
name = "ruzstd"
version = "0.7.3"
@@ -11786,6 +12198,12 @@ version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73"
[[package]]
name = "seq-macro"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc"
[[package]]
name = "serde"
version = "1.0.221"
@@ -11960,6 +12378,27 @@ dependencies = [
"syn 2.0.101",
]
[[package]]
name = "setups"
version = "0.1.0"
source = "git+https://github.com/matter-labs/zksync-airbender?tag=v0.5.0#a3c74040d2f994608f451550232392333400bbf1"
dependencies = [
"bigint_with_control",
"blake2_with_compression",
"final_reduced_risc_v_machine",
"machine_without_signed_mul_div",
"proc-macro2",
"prover",
"quote",
"reduced_risc_v_log_23_machine",
"reduced_risc_v_machine",
"risc_v_cycles",
"serde",
"serde_json",
"syn 2.0.101",
"verifier_common",
]
[[package]]
name = "sha1"
version = "0.10.6"
@@ -12090,6 +12529,17 @@ dependencies = [
"serde",
]
[[package]]
name = "smartstring"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29"
dependencies = [
"autocfg",
"static_assertions",
"version_check",
]
[[package]]
name = "smawk"
version = "0.3.2"
@@ -12688,6 +13138,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "str_stack"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9091b6114800a5f2141aee1d1b9d6ca3592ac062dc5decb3764ec5895a47b4eb"
[[package]]
name = "strength_reduce"
version = "0.2.4"
@@ -12803,6 +13259,17 @@ version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "super-seq-macro"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a7568b180699c2138a4bfaadda9a33a7419251489e58760d3b4e74d6588da3d"
dependencies = [
"proc-macro2",
"rhai",
"syn 2.0.101",
]
[[package]]
name = "supports-color"
version = "3.0.2"
@@ -13087,6 +13554,12 @@ dependencies = [
"unicode-width 0.2.0",
]
[[package]]
name = "thin-vec"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "144f754d318415ac792f9d69fc87abbbfc043ce2ef041c60f16ad828f638717d"
[[package]]
name = "thiserror"
version = "1.0.69"
@@ -13592,6 +14065,27 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
[[package]]
name = "trace_and_split"
version = "0.1.0"
source = "git+https://github.com/matter-labs/zksync-airbender?tag=v0.5.0#a3c74040d2f994608f451550232392333400bbf1"
dependencies = [
"prover",
"serde",
"serde_json",
"setups",
"worker",
]
[[package]]
name = "trace_holder"
version = "0.1.0"
source = "git+https://github.com/matter-labs/zksync-airbender?tag=v0.5.0#a3c74040d2f994608f451550232392333400bbf1"
dependencies = [
"field",
"worker",
]
[[package]]
name = "tracer"
version = "0.2.0"
@@ -13600,7 +14094,7 @@ dependencies = [
"clap",
"common",
"fnv",
"object",
"object 0.36.7",
"tracing",
"tracing-subscriber 0.3.19",
]
@@ -13743,6 +14237,16 @@ dependencies = [
"syn 2.0.101",
]
[[package]]
name = "transcript"
version = "0.1.0"
source = "git+https://github.com/matter-labs/zksync-airbender?tag=v0.5.0#a3c74040d2f994608f451550232392333400bbf1"
dependencies = [
"blake2s_u32",
"unroll",
"worker",
]
[[package]]
name = "transpose"
version = "0.2.3"
@@ -13863,6 +14367,15 @@ dependencies = [
"static_assertions",
]
[[package]]
name = "type-map"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb30dbbd9036155e74adad6812e9898d03ec374946234fbcebd5dfc7b9187b90"
dependencies = [
"rustc-hash 2.1.1",
]
[[package]]
name = "typed-arena"
version = "2.0.2"
@@ -14107,6 +14620,36 @@ dependencies = [
"time",
]
[[package]]
name = "verifier_common"
version = "0.1.0"
source = "git+https://github.com/matter-labs/zksync-airbender?tag=v0.5.0#a3c74040d2f994608f451550232392333400bbf1"
dependencies = [
"blake2s_u32",
"cs",
"field",
"non_determinism_source",
"poseidon2",
"prover",
"serde",
"transcript",
"unroll",
]
[[package]]
name = "verifier_generator"
version = "0.1.0"
source = "git+https://github.com/matter-labs/zksync-airbender?tag=v0.5.0#a3c74040d2f994608f451550232392333400bbf1"
dependencies = [
"proc-macro-error2",
"proc-macro2",
"prover",
"quote",
"serde",
"serde_json",
"syn 2.0.101",
]
[[package]]
name = "version_check"
version = "0.9.5"
@@ -14859,6 +15402,15 @@ dependencies = [
"bitflags 2.9.0",
]
[[package]]
name = "worker"
version = "0.1.0"
source = "git+https://github.com/matter-labs/zksync-airbender?tag=v0.5.0#a3c74040d2f994608f451550232392333400bbf1"
dependencies = [
"num_cpus",
"rayon",
]
[[package]]
name = "writeable"
version = "0.6.1"

View File

@@ -3,6 +3,7 @@ members = [
# zkVM interface
"crates/zkvm-interface",
# zkVMs
"crates/zkvm/airbender",
"crates/zkvm/jolt",
"crates/zkvm/miden",
"crates/zkvm/nexus",
@@ -62,6 +63,9 @@ tracing-subscriber = "0.3.19"
twirp = "0.9.1"
twirp-build = "0.9.0"
# Airbender dependencies
airbender_execution_utils = { git = "https://github.com/matter-labs/zksync-airbender", package = "execution_utils", tag = "v0.5.0" }
# Jolt dependencies
ark-serialize = "0.5.0"
common = { git = "https://github.com/a16z/jolt.git", rev = "55b9830a3944dde55d33a55c42522b81dd49f87a" }
@@ -106,6 +110,7 @@ zkm-sdk = { git = "https://github.com/ProjectZKM/Ziren.git", tag = "v1.1.4" }
# Local dependencies
ere-zkvm-interface = { path = "crates/zkvm-interface" }
ere-airbender = { path = "crates/zkvm/airbender", default-features = false }
ere-jolt = { path = "crates/zkvm/jolt", default-features = false }
ere-miden = { path = "crates/zkvm/miden", default-features = false }
ere-nexus = { path = "crates/zkvm/nexus", default-features = false }

View File

@@ -13,6 +13,7 @@ serde.workspace = true
tracing-subscriber = { workspace = true, features = ["env-filter"] }
# Local dependencies
ere-airbender = { workspace = true, optional = true }
ere-jolt = { workspace = true, optional = true }
ere-miden = { workspace = true, optional = true }
ere-nexus = { workspace = true, optional = true }
@@ -30,6 +31,7 @@ ere-zkvm-interface.workspace = true
default = []
# zkVM
airbender = ["dep:ere-airbender"]
jolt = ["dep:ere-jolt"]
miden = ["dep:ere-miden"]
nexus = ["dep:ere-nexus"]

View File

@@ -8,7 +8,8 @@ use tracing_subscriber::EnvFilter;
// Compile-time check to ensure exactly one zkVM feature is enabled for `ere-compiler`
const _: () = {
assert!(
(cfg!(feature = "jolt") as u8
(cfg!(feature = "airbender") as u8
+ cfg!(feature = "jolt") as u8
+ cfg!(feature = "miden") as u8
+ cfg!(feature = "nexus") as u8
+ cfg!(feature = "openvm") as u8
@@ -50,6 +51,9 @@ fn main() -> Result<(), Error> {
}
fn compile(guest_path: PathBuf) -> Result<impl Serialize, Error> {
#[cfg(feature = "airbender")]
let result = ere_airbender::compiler::RustRv32ima.compile(&guest_path);
#[cfg(feature = "jolt")]
let result = if use_stock_rust() {
ere_jolt::compiler::RustRv32ima.compile(&guest_path)

View File

@@ -20,6 +20,7 @@ fn generate_crate_version() {
fn generate_zkvm_sdk_version_impl() {
let [
airbender_version,
jolt_version,
miden_version,
nexus_version,
@@ -29,6 +30,7 @@ fn generate_zkvm_sdk_version_impl() {
sp1_version,
ziren_version,
] = [
"execution_utils",
"jolt-sdk",
"miden-core",
"nexus-sdk",
@@ -51,6 +53,7 @@ fn generate_zkvm_sdk_version_impl() {
r#"impl crate::ErezkVM {{
pub fn sdk_version(&self) -> &'static str {{
match self {{
Self::Airbender => "{airbender_version}",
Self::Jolt => "{jolt_version}",
Self::Miden => "{miden_version}",
Self::Nexus => "{nexus_version}",

View File

@@ -95,6 +95,7 @@ const ERE_SERVER_PORT_OFFSET: u16 = 4174;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ErezkVM {
Airbender,
Jolt,
Miden,
Nexus,
@@ -109,6 +110,7 @@ pub enum ErezkVM {
impl ErezkVM {
pub fn as_str(&self) -> &'static str {
match self {
Self::Airbender => "airbender",
Self::Jolt => "jolt",
Self::Miden => "miden",
Self::Nexus => "nexus",
@@ -126,7 +128,7 @@ impl ErezkVM {
let suffix = match (gpu, self) {
// Only the following zkVMs requires CUDA setup in the base image
// when GPU support is required.
(true, Self::OpenVM | Self::Risc0 | Self::Zisk) => "-cuda",
(true, Self::Airbender | Self::OpenVM | Self::Risc0 | Self::Zisk) => "-cuda",
_ => "",
};
format!("{version}{suffix}")
@@ -195,7 +197,7 @@ impl ErezkVM {
let cuda_arch = cuda_arch();
match self {
ErezkVM::OpenVM | ErezkVM::Risc0 | ErezkVM::Zisk => {
Self::Airbender | Self::OpenVM | Self::Risc0 | Self::Zisk => {
if let Some(cuda_arch) = cuda_arch {
cmd = cmd.build_arg("CUDA_ARCH", cuda_arch)
}
@@ -259,12 +261,12 @@ impl ErezkVM {
// zkVM specific options
cmd = match self {
ErezkVM::Risc0 => cmd
Self::Risc0 => cmd
.inherit_env("RISC0_SEGMENT_PO2")
.inherit_env("RISC0_KECCAK_PO2"),
// ZisK uses shared memory to exchange data between processes, it
// requires at least 8G shared memory, here we set 16G for safety.
ErezkVM::Zisk => cmd
Self::Zisk => cmd
.option("shm-size", "16G")
.option("ulimit", "memlock=-1:-1")
.inherit_env("ZISK_PORT")
@@ -282,13 +284,14 @@ impl ErezkVM {
// zkVM specific options when using GPU
if gpu {
cmd = match self {
ErezkVM::OpenVM => cmd.gpus("all"),
Self::Airbender => cmd.gpus("all"),
Self::OpenVM => cmd.gpus("all"),
// SP1 runs docker command to spin up the server to do GPU
// proving, to give the client access to the prover service, we
// need to use the host networking driver.
ErezkVM::SP1 => cmd.mount_docker_socket().network("host"),
ErezkVM::Risc0 => cmd.gpus("all").inherit_env("RISC0_DEFAULT_PROVER_NUM_GPUS"),
ErezkVM::Zisk => cmd.gpus("all"),
Self::SP1 => cmd.mount_docker_socket().network("host"),
Self::Risc0 => cmd.gpus("all").inherit_env("RISC0_DEFAULT_PROVER_NUM_GPUS"),
Self::Zisk => cmd.gpus("all"),
_ => cmd,
}
}
@@ -303,11 +306,11 @@ impl ErezkVM {
// create a temporary directory and mount it on the top level, so
// the volume could be shared, and override `TMPDIR` so we don't
// need to mount the whole `/tmp`.
ErezkVM::Risc0 => cmd
Self::Risc0 => cmd
.mount_docker_socket()
.env("TMPDIR", tempdir.path().to_string_lossy())
.volume(tempdir.path(), tempdir.path()),
ErezkVM::SP1 => {
Self::SP1 => {
let groth16_circuit_path = home_dir().join(".sp1").join("circuits").join("groth16");
cmd.mount_docker_socket()
.env(
@@ -336,6 +339,7 @@ impl FromStr for ErezkVM {
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"airbender" => Self::Airbender,
"jolt" => Self::Jolt,
"miden" => Self::Miden,
"nexus" => Self::Nexus,
@@ -610,6 +614,12 @@ mod test {
};
}
mod airbender {
test_compile!(Airbender, "basic");
test_execute!(Airbender, BasicProgramInput::valid().into_output_sha256());
test_prove!(Airbender, BasicProgramInput::valid().into_output_sha256());
}
mod jolt {
test_compile!(Jolt, "basic");
}

View File

@@ -20,6 +20,7 @@ tracing = { workspace = true, optional = true }
tracing-subscriber = { workspace = true, features = ["env-filter"], optional = true }
# Local dependencies
ere-airbender = { workspace = true, optional = true }
ere-jolt = { workspace = true, optional = true }
ere-miden = { workspace = true, optional = true }
ere-nexus = { workspace = true, optional = true }
@@ -45,6 +46,7 @@ default = []
server = ["dep:clap", "dep:tower-http", "dep:tracing", "dep:tracing-subscriber", "tokio/macros", "tokio/rt-multi-thread", "tokio/signal"]
# zkVM
airbender = ["dep:ere-airbender", "server"]
jolt = ["dep:ere-jolt", "server"]
miden = ["dep:ere-miden", "server"]
nexus = ["dep:ere-nexus", "server"]

View File

@@ -21,7 +21,8 @@ use twirp::{
const _: () = {
if cfg!(feature = "server") {
assert!(
(cfg!(feature = "jolt") as u8
(cfg!(feature = "airbender") as u8
+ cfg!(feature = "jolt") as u8
+ cfg!(feature = "miden") as u8
+ cfg!(feature = "nexus") as u8
+ cfg!(feature = "openvm") as u8
@@ -111,6 +112,9 @@ fn construct_zkvm(program: Vec<u8>, resource: ProverResourceType) -> Result<impl
let (program, _) = bincode::serde::decode_from_slice(&program, bincode::config::legacy())
.with_context(|| "Failed to deserialize program")?;
#[cfg(feature = "airbender")]
let zkvm = Ok::<_, Error>(ere_airbender::EreAirbender::new(program, resource));
#[cfg(feature = "jolt")]
let zkvm = ere_jolt::EreJolt::new(program, resource);

View File

@@ -0,0 +1,28 @@
[package]
name = "ere-airbender"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
[dependencies]
bincode = { workspace = true, features = ["alloc", "serde"] }
serde_json.workspace = true
tempfile.workspace = true
thiserror.workspace = true
# Airbender dependencies
airbender_execution_utils.workspace = true
# Local dependencies
ere-compile-utils.workspace = true
ere-zkvm-interface.workspace = true
[dev-dependencies]
ere-test-utils = { workspace = true, features = ["host"] }
[build-dependencies]
ere-build-utils.workspace = true
[lints]
workspace = true

View File

@@ -0,0 +1,5 @@
use ere_build_utils::detect_and_generate_name_and_sdk_version;
fn main() {
detect_and_generate_name_and_sdk_version("airbender", "execution_utils");
}

View File

@@ -0,0 +1,244 @@
use crate::error::AirbenderError;
use airbender_execution_utils::{
Machine, ProgramProof, compute_chain_encoding, generate_params_for_binary,
universal_circuit_verifier_vk, verify_recursion_log_23_layer,
};
use ere_zkvm_interface::PublicValues;
use std::{array, fs, io::BufRead, iter, process::Command};
use tempfile::tempdir;
/// Verification key hash chain.
///
/// For recursive verifier program, it exposes the chaining hash of verification
/// keys of programs that it verifies, which is computed as
/// `blake(blake(blake(0 || base_vk)|| verifier_0_vk) || verifier_1_vk)...`.
///
/// For a base program, the VK is computed as `blake(PC || setup_caps)`, where
/// `PC` is the program counter value at the end of execution, and `setup_caps`
/// is the merkle tree caps derived from the program.
pub type VkHashChain = [u32; 8];
pub struct AirbenderSdk {
bin: Vec<u8>,
vk_hash_chain: VkHashChain,
gpu: bool,
}
impl AirbenderSdk {
pub fn new(bin: &[u8], gpu: bool) -> Self {
let vk_hash_chain = {
// Compute base VK as `blake(PC || setup_caps)`.
let base_vk = generate_params_for_binary(bin, Machine::Standard);
// The 1st recursion layer VK
let verifier_vk = universal_circuit_verifier_vk().params;
// Compute hash chain as `blake(blake(0 || guest_vk) || verifier_vk)`,
// that is expected to be exposed by second layer recursion program.
compute_chain_encoding(vec![[0; 8], base_vk, verifier_vk])
};
Self {
bin: bin.to_vec(),
vk_hash_chain,
gpu,
}
}
pub fn execute(&self, input: &[u8]) -> Result<(PublicValues, u64), AirbenderError> {
let tempdir = tempdir().map_err(AirbenderError::TempDir)?;
let bin_path = tempdir.path().join("guest.bin");
fs::write(&bin_path, &self.bin)
.map_err(|err| AirbenderError::write_file(err, "guest.bin", &bin_path))?;
let input_path = tempdir.path().join("input.hex");
fs::write(&input_path, encode_input(input))
.map_err(|err| AirbenderError::write_file(err, "input.hex", &input_path))?;
let output = Command::new("airbender-cli")
.arg("run")
.arg("--bin")
.arg(&bin_path)
.arg("--input-file")
.arg(&input_path)
.args(["--cycles", &u64::MAX.to_string()])
.output()
.map_err(AirbenderError::AirbenderRun)?;
if !output.status.success() {
return Err(AirbenderError::AirbenderRunFailed {
status: output.status,
});
}
// Parse public values 8 u32 words (32 bytes) from stdout in format of:
// `Result: {v0}, {v1}, {v2}, {v3}, {v4}, {v5}, {v6}, {v7}`
let public_values = output
.stdout
.lines()
.find_map(|line| {
let line = line.ok()?;
let line = line.split_once("Result:")?.1;
let mut words = line.split(',');
let mut bytes = Vec::with_capacity(32);
for _ in 0..8 {
bytes.extend(words.next()?.trim().parse::<u32>().ok()?.to_le_bytes())
}
Some(bytes)
})
.ok_or_else(|| {
AirbenderError::ParsePublicValue(
String::from_utf8_lossy(&output.stdout).to_string(),
)
})?;
// Parse cycles from stdout in format of:
// `Took {cycles} cycles to finish`
let cycles = output
.stdout
.lines()
.find_map(|line| {
let line = line.ok()?;
let line = line.split_once("Took ")?.1;
let cycle = line.split_once(" cycles")?.0;
cycle.parse().ok()
})
.ok_or_else(|| {
AirbenderError::ParseCycles(String::from_utf8_lossy(&output.stdout).to_string())
})?;
Ok((public_values, cycles))
}
pub fn prove(&self, input: &[u8]) -> Result<(PublicValues, ProgramProof), AirbenderError> {
let tempdir = tempdir().map_err(AirbenderError::TempDir)?;
let bin_path = tempdir.path().join("guest.bin");
fs::write(&bin_path, &self.bin)
.map_err(|err| AirbenderError::write_file(err, "guest.bin", &bin_path))?;
let input_path = tempdir.path().join("input.hex");
fs::write(&input_path, encode_input(input))
.map_err(|err| AirbenderError::write_file(err, "input.hex", &input_path))?;
let output_dir = tempdir.path().join("output");
fs::create_dir_all(&output_dir)
.map_err(|err| AirbenderError::create_dir(err, "output", &output_dir))?;
// Prove guest program + 1st recursion layer (tree of recursive proofs until root).
let output = Command::new("airbender-cli")
.arg("prove")
.arg("--bin")
.arg(&bin_path)
.arg("--output-dir")
.arg(&output_dir)
.arg("--input-file")
.arg(&input_path)
.args(["--until", "final-recursion"])
.args(["--cycles", &u64::MAX.to_string()])
.args(self.gpu.then_some("--gpu"))
.output()
.map_err(AirbenderError::AirbenderProve)?;
if !output.status.success() {
return Err(AirbenderError::AirbenderProveFailed {
status: output.status,
stderr: String::from_utf8_lossy(&output.stderr).into_owned(),
});
}
let proof_path = output_dir.join("recursion_program_proof.json");
if !proof_path.exists() {
return Err(AirbenderError::RecursionProofNotFound { path: proof_path });
}
// Prove 2nd recursion layer (wrapping root of 1st recursion layer)
let output = Command::new("airbender-cli")
.arg("prove-final")
.arg("--input-file")
.arg(&proof_path)
.arg("--output-dir")
.arg(&output_dir)
.args(self.gpu.then_some("--gpu"))
.output()
.map_err(AirbenderError::AirbenderProveFinal)?;
if !output.status.success() {
return Err(AirbenderError::AirbenderProveFinalFailed {
status: output.status,
stderr: String::from_utf8_lossy(&output.stderr).into_owned(),
});
}
let proof_path = output_dir.join("final_program_proof.json");
if !proof_path.exists() {
return Err(AirbenderError::FinalProofNotFound { path: proof_path });
}
let proof_bytes = fs::read(&proof_path).map_err(|err| {
AirbenderError::read_file(err, "final_program_proof.json", &proof_path)
})?;
let proof: ProgramProof =
serde_json::from_slice(&proof_bytes).map_err(AirbenderError::JsonDeserialize)?;
let (public_values, vk_hash_chain) = extract_public_values_and_vk_hash_chain(&proof)?;
if self.vk_hash_chain != vk_hash_chain {
return Err(AirbenderError::UnexpectedVkHashChain {
preprocessed: self.vk_hash_chain,
proved: vk_hash_chain,
});
}
Ok((public_values, proof))
}
pub fn verify(&self, proof: &ProgramProof) -> Result<PublicValues, AirbenderError> {
let is_valid = verify_recursion_log_23_layer(proof);
if !is_valid {
return Err(AirbenderError::ProofVerificationFailed);
}
let (public_values, vk_hash_chain) = extract_public_values_and_vk_hash_chain(proof)?;
if self.vk_hash_chain != vk_hash_chain {
return Err(AirbenderError::UnexpectedVkHashChain {
preprocessed: self.vk_hash_chain,
proved: vk_hash_chain,
});
}
Ok(public_values)
}
}
/// Encode input with length prefixed to hex string for `airbender-cli`.
fn encode_input(input: &[u8]) -> String {
iter::once((input.len() as u32).to_le_bytes().as_slice())
.chain(input.chunks(4))
.map(|chunk| {
let mut bytes = [0u8; 4];
bytes[..chunk.len()].copy_from_slice(chunk);
format!("{:08x}", u32::from_le_bytes(bytes))
})
.collect()
}
// Extract public values and VK hash chain from register values.
fn extract_public_values_and_vk_hash_chain(
proof: &ProgramProof,
) -> Result<(PublicValues, VkHashChain), AirbenderError> {
if proof.register_final_values.len() != 32 {
return Err(AirbenderError::InvalidRegisterCount(
proof.register_final_values.len(),
));
}
let public_values = proof.register_final_values[10..18]
.iter()
.flat_map(|value| value.value.to_le_bytes())
.collect();
let vk_chain_hash = array::from_fn(|i| proof.register_final_values[18 + i].value);
Ok((public_values, vk_chain_hash))
}

View File

@@ -0,0 +1,5 @@
mod rust_rv32ima;
pub use rust_rv32ima::RustRv32ima;
pub type AirbenderProgram = Vec<u8>;

View File

@@ -0,0 +1,97 @@
use crate::{compiler::AirbenderProgram, error::AirbenderError};
use ere_compile_utils::CargoBuildCmd;
use ere_zkvm_interface::Compiler;
use std::{
env,
io::Write,
path::Path,
process::{Command, Stdio},
};
const TARGET_TRIPLE: &str = "riscv32ima-unknown-none-elf";
// Rust flags according to https://github.com/matter-labs/zksync-airbender/blob/v0.5.0/examples/dynamic_fibonacci/.cargo/config.toml.
const RUSTFLAGS: &[&str] = &[
// Replace atomic ops with nonatomic versions since the guest is single threaded.
"-C",
"passes=lower-atomic",
"-C",
"target-feature=-unaligned-scalar-mem,+relax",
"-C",
"link-arg=--save-temps",
"-C",
"force-frame-pointers",
];
const CARGO_BUILD_OPTIONS: &[&str] = &[
// For bare metal we have to build core and alloc
"-Zbuild-std=core,alloc",
];
const LINKER_SCRIPT: &str = concat!(
include_str!("rust_rv32ima/memory.x"),
include_str!("rust_rv32ima/link.x"),
);
/// Compiler for Rust guest program to RV32IMA architecture.
pub struct RustRv32ima;
impl Compiler for RustRv32ima {
type Error = AirbenderError;
type Program = AirbenderProgram;
fn compile(&self, guest_directory: &Path) -> Result<Self::Program, Self::Error> {
let toolchain = env::var("ERE_RUST_TOOLCHAIN").unwrap_or_else(|_| "nightly".into());
let elf = CargoBuildCmd::new()
.linker_script(Some(LINKER_SCRIPT))
.toolchain(toolchain)
.build_options(CARGO_BUILD_OPTIONS)
.rustflags(RUSTFLAGS)
.exec(guest_directory, TARGET_TRIPLE)?;
let bin = objcopy_binary(&elf)?;
Ok(bin)
}
}
fn objcopy_binary(elf: &[u8]) -> Result<Vec<u8>, AirbenderError> {
let mut child = Command::new("rust-objcopy")
.args(["-O", "binary", "-", "-"])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.map_err(AirbenderError::RustObjcopy)?;
child
.stdin
.as_mut()
.unwrap()
.write_all(elf)
.map_err(AirbenderError::RustObjcopyStdin)?;
let output = child
.wait_with_output()
.map_err(AirbenderError::RustObjcopy)?;
if !output.status.success() {
return Err(AirbenderError::RustObjcopyFailed {
status: output.status,
stderr: String::from_utf8_lossy(&output.stderr).into_owned(),
});
}
Ok(output.stdout)
}
#[cfg(test)]
mod tests {
use crate::compiler::RustRv32ima;
use ere_test_utils::host::testing_guest_directory;
use ere_zkvm_interface::Compiler;
#[test]
fn test_compile() {
let guest_directory = testing_guest_directory("airbender", "basic");
let bin = RustRv32ima.compile(&guest_directory).unwrap();
assert!(!bin.is_empty(), "Binary should not be empty.");
}
}

View File

@@ -0,0 +1,193 @@
/* Copied from https://github.com/matter-labs/zksync-airbender/blob/v0.5.0/examples/scripts/lds/link.x */
PROVIDE(_stext = ORIGIN(REGION_TEXT));
PROVIDE(_max_hart_id = 0);
PROVIDE(_hart_stack_size = 64M);
PROVIDE(_heap_size = 768M);
/*
PROVIDE(UserSoft = DefaultHandler);
PROVIDE(SupervisorSoft = DefaultHandler);
PROVIDE(MachineSoft = DefaultHandler);
PROVIDE(UserTimer = DefaultHandler);
PROVIDE(SupervisorTimer = DefaultHandler);
PROVIDE(MachineTimer = DefaultHandler);
PROVIDE(UserExternal = DefaultHandler);
PROVIDE(SupervisorExternal = DefaultHandler);
PROVIDE(MachineExternal = DefaultHandler);
PROVIDE(DefaultHandler = DefaultInterruptHandler);
PROVIDE(ExceptionHandler = DefaultExceptionHandler);
*/
/* # Pre-initialization function */
/* If the user overrides this using the `#[pre_init]` attribute or by creating a `__pre_init` function,
then the function this points to will be called before the RAM is initialized. */
PROVIDE(__pre_init = default_pre_init);
/* A PAC/HAL defined routine that should initialize custom interrupt controller if needed. */
/*
PROVIDE(_setup_interrupts = default_setup_interrupts);
*/
/* # Start trap function override
By default uses the riscv crates default trap handler
but by providing the `_start_trap` symbol external crates can override.
*/
PROVIDE(_start_trap = default_start_trap);
PROVIDE(_machine_start_trap = machine_default_start_trap);
PHDRS
{
text PT_LOAD;
data PT_LOAD;
bss PT_LOAD;
}
SECTIONS
{
.text.dummy (NOLOAD) :
{
/* This section is intended to make _stext address work */
. = ABSOLUTE(_stext);
} > REGION_TEXT AT > REGION_TEXT :text
.text _stext : ALIGN(4096)
{
/* Put reset handler first in .text section so it ends up as the entry */
/* point of the program. */
KEEP(*(.init));
KEEP(*(.init.rust));
. = ALIGN(4);
*(.trap);
*(.trap.rust);
*(.text .text.*);
} > REGION_TEXT AT > REGION_TEXT :text
.rodata : ALIGN(4)
{
*(.srodata .srodata.*);
*(.rodata .rodata.*);
/* 4-byte align the end (VMA) of this section.
This is required by LLD to ensure the LMA of the following
section will have the correct alignment. */
. = ALIGN(4);
} > REGION_RODATA AT > REGION_RODATA :text
/* fictitious region that represents the memory available for the stack */
.stack ORIGIN(REGION_STACK) (NOLOAD) : ALIGN(4096)
{
_estack = .;
. += (_max_hart_id + 1) * _hart_stack_size;
. = ALIGN(4);
_sstack = .;
} > REGION_STACK
.data : ALIGN(4096)
{
_sidata = LOADADDR(.data);
_sdata = .;
/* Must be called __global_pointer$ for linker relaxations to work. */
PROVIDE(__global_pointer$ = . + 0x800);
*(.sdata .sdata.* .sdata2 .sdata2.*);
*(.data .data.*);
. = ALIGN(4);
_edata = .;
} > REGION_DATA AT > REGION_DATAINIT :data
.bss (NOLOAD) : ALIGN(4096)
{
_sbss = .;
*(.sbss .sbss.* .bss .bss.*);
. = ALIGN(4);
_ebss = .;
} > REGION_BSS AT > REGION_BSS :bss
/* fictitious region that represents the memory available for the heap */
.heap (NOLOAD) : ALIGN(2097152)
{
_sheap = .;
. += _heap_size;
. = ALIGN(2097152);
_eheap = .;
} > REGION_HEAP
/* fake output .got section */
/* Dynamic relocations are unsupported. This section is only used to detect
relocatable code in the input files and raise an error if relocatable code
is found */
.got (INFO) :
{
KEEP(*(.got .got.*));
}
.eh_frame (INFO) : { KEEP(*(.eh_frame)) }
.eh_frame_hdr (INFO) : { *(.eh_frame_hdr) }
}
/* Do not exceed this mark in the error messages above | */
ASSERT(ORIGIN(REGION_TEXT) % 4 == 0, "
ERROR(riscv-rt): the start of the REGION_TEXT must be 4-byte aligned");
ASSERT(ORIGIN(REGION_RODATA) % 4 == 0, "
ERROR(riscv-rt): the start of the REGION_RODATA must be 4-byte aligned");
ASSERT(ORIGIN(REGION_DATAINIT) % 4 == 0, "
ERROR(riscv-rt): the start of the REGION_DATAINIT must be 4-byte aligned");
ASSERT(ORIGIN(REGION_DATA) % 4 == 0, "
ERROR(riscv-rt): the start of the REGION_DATA must be 4-byte aligned");
ASSERT(ORIGIN(REGION_HEAP) % 4 == 0, "
ERROR(riscv-rt): the start of the REGION_HEAP must be 4-byte aligned");
ASSERT(ORIGIN(REGION_TEXT) % 4 == 0, "
ERROR(riscv-rt): the start of the REGION_TEXT must be 4-byte aligned");
ASSERT(ORIGIN(REGION_STACK) % 4 == 0, "
ERROR(riscv-rt): the start of the REGION_STACK must be 4-byte aligned");
ASSERT(_stext % 4 == 0, "
ERROR(riscv-rt): `_stext` must be 4-byte aligned");
ASSERT(_sdata % 4 == 0 && _edata % 4 == 0, "
BUG(riscv-rt): .data is not 4-byte aligned");
ASSERT(_sidata % 4 == 0, "
BUG(riscv-rt): the LMA of .data is not 4-byte aligned");
ASSERT(_sbss % 4 == 0 && _ebss % 4 == 0, "
BUG(riscv-rt): .bss is not 4-byte aligned");
ASSERT(_sheap % 4 == 0, "
BUG(riscv-rt): start of .heap is not 4-byte aligned");
ASSERT(_stext + SIZEOF(.text) < ORIGIN(REGION_TEXT) + LENGTH(REGION_TEXT), "
ERROR(riscv-rt): The .text section must be placed inside the REGION_TEXT region.
Set _stext to an address smaller than 'ORIGIN(REGION_TEXT) + LENGTH(REGION_TEXT)'");
ASSERT(_sidata + SIZEOF(.data) < ORIGIN(REGION_DATAINIT) + LENGTH(REGION_DATAINIT), "
ERROR(riscv-rt): The init data for .data section must be placed inside the REGION_DATAINIT region.
Set _sidata to an address smaller than 'ORIGIN(REGION_DATAINIT) + LENGTH(REGION_DATAINIT)'");
ASSERT(SIZEOF(.stack) >= (_max_hart_id + 1) * _hart_stack_size, "
ERROR(riscv-rt): .stack section is too small for allocating stacks for all the harts.
Consider changing `_max_hart_id` or `_hart_stack_size`.");
ASSERT(SIZEOF(.got) == 0, "
.got section detected in the input files. Dynamic relocations are not
supported. If you are linking to C code compiled using the `gcc` crate
then modify your build script to compile the C code _without_ the
-fPIC flag. See the documentation of the `gcc::Config.fpic` method for
details.");
ASSERT(SIZEOF(.data) == 0, "
.data section detected in the input files. Global variables with non-trivial
initialization are not supported yet. Variables with zero-initialization can be
linked to .bss section instead, as the platform guarantees zero-initialization
of all RAM space.");
/* Do not exceed this mark in the error messages above | */

View File

@@ -0,0 +1,15 @@
/* Copied from https://github.com/matter-labs/zksync-airbender/blob/v0.5.0/examples/scripts/lds/memory.x */
MEMORY
{
ROM (rx): ORIGIN = 0, LENGTH = 2M
RAM (rwa!x) : ORIGIN = 2M, LENGTH = 1022M
}
REGION_ALIAS("REGION_TEXT", ROM);
REGION_ALIAS("REGION_RODATA", ROM);
REGION_ALIAS("REGION_DATAINIT", ROM);
REGION_ALIAS("REGION_STACK", RAM);
REGION_ALIAS("REGION_DATA", RAM);
REGION_ALIAS("REGION_BSS", RAM);
REGION_ALIAS("REGION_HEAP", RAM);

View File

@@ -0,0 +1,99 @@
use crate::client::VkHashChain;
use ere_zkvm_interface::zkVMError;
use std::{
io,
path::{Path, PathBuf},
process::ExitStatus,
};
use thiserror::Error;
impl From<AirbenderError> for zkVMError {
fn from(value: AirbenderError) -> Self {
zkVMError::Other(Box::new(value))
}
}
#[derive(Debug, Error)]
pub enum AirbenderError {
// Compilation
#[error(transparent)]
CompileError(#[from] ere_compile_utils::CompileError),
#[error("Failed to execute `rust-objcopy`: {0}")]
RustObjcopy(#[source] io::Error),
#[error("`rust-objcopy` failed with status: {status}\nstderr: {stderr}")]
RustObjcopyFailed { status: ExitStatus, stderr: String },
#[error("Failed to write ELF to `rust-objcopy` stdin: {0}")]
RustObjcopyStdin(#[source] io::Error),
// IO and file system
#[error("IO failure: {0}")]
Io(#[from] io::Error),
#[error("IO failure in temporary directory: {0}")]
TempDir(io::Error),
// Serialization
#[error("Bincode encode failed: {0}")]
BincodeEncode(#[from] bincode::error::EncodeError),
#[error("Bincode decode failed: {0}")]
BincodeDecode(#[from] bincode::error::DecodeError),
#[error("JSON deserialization failed: {0}")]
JsonDeserialize(#[from] serde_json::Error),
// Execution
#[error("Failed to execute `airbender-cli run`: {0}")]
AirbenderRun(#[source] io::Error),
#[error("`airbender-cli run` failed with status: {status}")]
AirbenderRunFailed { status: ExitStatus },
#[error("Failed to parse public value from stdout: {0}")]
ParsePublicValue(String),
#[error("Failed to parse cycles from stdout: {0}")]
ParseCycles(String),
// Proving
#[error("Failed to execute `airbender-cli prove`: {0}")]
AirbenderProve(#[source] io::Error),
#[error("`airbender-cli prove` failed with status: {status}\nstderr: {stderr}")]
AirbenderProveFailed { status: ExitStatus, stderr: String },
#[error("Failed to execute `airbender-cli prove-final`: {0}")]
AirbenderProveFinal(#[source] io::Error),
#[error("`airbender-cli prove-final` failed with status: {status}\nstderr: {stderr}")]
AirbenderProveFinalFailed { status: ExitStatus, stderr: String },
#[error("Recursion proof not found at {path}")]
RecursionProofNotFound { path: PathBuf },
#[error("Final proof not found at {path}")]
FinalProofNotFound { path: PathBuf },
// Verification
#[error("Proof verification failed")]
ProofVerificationFailed,
#[error("Invalid final register count, expected 32 but got {0}")]
InvalidRegisterCount(usize),
#[error(
"Unexpected verification key hash chain - preprocessed: {preprocessed:?}, proved: {proved:?}"
)]
UnexpectedVkHashChain {
preprocessed: VkHashChain,
proved: VkHashChain,
},
}
impl AirbenderError {
pub fn io(err: io::Error, context: impl Into<String>) -> Self {
Self::Io(io::Error::other(format!("{}: {}", context.into(), err)))
}
pub fn create_dir(err: io::Error, id: &str, path: impl AsRef<Path>) -> Self {
let ctx = format!("Failed to create dir {id} at {}", path.as_ref().display());
Self::io(err, ctx)
}
pub fn write_file(err: io::Error, id: &str, path: impl AsRef<Path>) -> Self {
let ctx = format!("Failed to write {id} to {}", path.as_ref().display());
Self::io(err, ctx)
}
pub fn read_file(err: io::Error, id: &str, path: impl AsRef<Path>) -> Self {
let ctx = format!("Failed to read {id} from {}", path.as_ref().display());
Self::io(err, ctx)
}
}

View File

@@ -0,0 +1,153 @@
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
use crate::{client::AirbenderSdk, compiler::AirbenderProgram, error::AirbenderError};
use airbender_execution_utils::ProgramProof;
use ere_zkvm_interface::{
ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind, ProverResourceType,
PublicValues, zkVM, zkVMError,
};
use std::time::Instant;
mod client;
pub mod compiler;
pub mod error;
include!(concat!(env!("OUT_DIR"), "/name_and_sdk_version.rs"));
pub struct EreAirbender {
sdk: AirbenderSdk,
}
impl EreAirbender {
pub fn new(bin: AirbenderProgram, resource: ProverResourceType) -> Self {
let gpu = matches!(resource, ProverResourceType::Gpu);
let sdk = AirbenderSdk::new(&bin, gpu);
Self { sdk }
}
}
impl zkVM for EreAirbender {
fn execute(&self, input: &[u8]) -> Result<(PublicValues, ProgramExecutionReport), zkVMError> {
let start = Instant::now();
let (public_values, cycles) = self.sdk.execute(input)?;
let execution_duration = start.elapsed();
Ok((
public_values,
ProgramExecutionReport {
total_num_cycles: cycles,
execution_duration,
..Default::default()
},
))
}
fn prove(
&self,
input: &[u8],
proof_kind: ProofKind,
) -> Result<(PublicValues, Proof, ProgramProvingReport), zkVMError> {
if proof_kind != ProofKind::Compressed {
panic!("Only Compressed proof kind is supported.");
}
let start = Instant::now();
let (public_values, proof) = self.sdk.prove(input)?;
let proving_time = start.elapsed();
let proof_bytes = bincode::serde::encode_to_vec(&proof, bincode::config::legacy())
.map_err(AirbenderError::BincodeEncode)?;
Ok((
public_values,
Proof::Compressed(proof_bytes),
ProgramProvingReport::new(proving_time),
))
}
fn verify(&self, proof: &Proof) -> Result<PublicValues, zkVMError> {
let Proof::Compressed(proof) = proof else {
return Err(zkVMError::other("Only Compressed proof kind is supported."));
};
let (proof, _): (ProgramProof, _) =
bincode::serde::decode_from_slice(proof, bincode::config::legacy())
.map_err(AirbenderError::BincodeDecode)?;
let public_values = self.sdk.verify(&proof)?;
Ok(public_values)
}
fn name(&self) -> &'static str {
NAME
}
fn sdk_version(&self) -> &'static str {
SDK_VERSION
}
}
#[cfg(test)]
mod tests {
use crate::{
EreAirbender,
compiler::{AirbenderProgram, RustRv32ima},
};
use ere_test_utils::{
host::{TestCase, run_zkvm_execute, run_zkvm_prove, testing_guest_directory},
program::basic::BasicProgramInput,
};
use ere_zkvm_interface::{Compiler, ProofKind, ProverResourceType, zkVM};
use std::sync::OnceLock;
fn basic_program() -> AirbenderProgram {
static PROGRAM: OnceLock<AirbenderProgram> = OnceLock::new();
PROGRAM
.get_or_init(|| {
RustRv32ima
.compile(&testing_guest_directory("airbender", "basic"))
.unwrap()
})
.clone()
}
#[test]
fn test_execute() {
let program = basic_program();
let zkvm = EreAirbender::new(program, ProverResourceType::Cpu);
let test_case = BasicProgramInput::valid().into_output_sha256();
run_zkvm_execute(&zkvm, &test_case);
}
#[test]
fn test_execute_invalid_input() {
let program = basic_program();
let zkvm = EreAirbender::new(program, ProverResourceType::Cpu);
for input in [Vec::new(), BasicProgramInput::invalid().serialized_input()] {
zkvm.execute(&input).unwrap_err();
}
}
#[test]
fn test_prove() {
let program = basic_program();
let zkvm = EreAirbender::new(program, ProverResourceType::Cpu);
let test_case = BasicProgramInput::valid().into_output_sha256();
run_zkvm_execute(&zkvm, &test_case);
run_zkvm_prove(&zkvm, &test_case);
}
#[test]
fn test_prove_invalid_input() {
let program = basic_program();
let zkvm = EreAirbender::new(program, ProverResourceType::Cpu);
for input in [Vec::new(), BasicProgramInput::invalid().serialized_input()] {
zkvm.prove(&input, ProofKind::default()).unwrap_err();
}
}
}

View File

@@ -0,0 +1,35 @@
ARG BASE_IMAGE=ere-base:latest
ARG BASE_CUDA_IMAGE=ere-base:latest-cuda
# Whether to enable CUDA feature or not.
ARG CUDA
FROM $BASE_IMAGE AS base
FROM $BASE_CUDA_IMAGE AS base_cuda
FROM base${CUDA:+_cuda}
# Set default toolchain to nightly
RUN rustup default nightly
ARG CUDA
# Default to build for RTX 50 series
ARG CUDA_ARCH=sm_120
# Env read by Airbender crate `gpu_prover`, only taking the numeric part
ARG CUDAARCHS=${CUDA_ARCH#sm_}
# Copy the Airbender SDK installer script from the workspace context
COPY --chmod=755 scripts/sdk_installers/install_airbender_sdk.sh /tmp/install_airbender_sdk.sh
# Run the Airbender SDK installation script.
# This script installs airbender-cli.
RUN /tmp/install_airbender_sdk.sh && rm /tmp/install_airbender_sdk.sh
# Verify airbender-cli is accessible with the correct toolchain
RUN airbender-cli --version
# Add `rust-src` component to enable std build for nightly rust.
RUN rustup component add rust-src
CMD ["/bin/bash"]

View File

@@ -0,0 +1,34 @@
ARG BASE_ZKVM_IMAGE=ere-base-airbender:latest
ARG RUNTIME_IMAGE=ubuntu:24.04
FROM $BASE_ZKVM_IMAGE AS build_stage
COPY . /ere
WORKDIR /ere
RUN cargo build --release --package ere-compiler --bin ere-compiler --features airbender \
&& mkdir bin && mv target/release/ere-compiler bin/ere-compiler \
&& cargo clean && rm -rf $CARGO_HOME/registry/src $CARGO_HOME/registry/cache
FROM $RUNTIME_IMAGE AS runtime_stage
# Install common dependencies and build tools
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
ca-certificates \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
# Copy Rust
COPY --from=build_stage /usr/local/cargo /usr/local/cargo
COPY --from=build_stage /usr/local/rustup /usr/local/rustup
# Add Rust to path
ENV RUSTUP_HOME=/usr/local/rustup \
CARGO_HOME=/usr/local/cargo \
PATH=/usr/local/cargo/bin:$PATH
# Copy ere-compiler
COPY --from=build_stage /ere/bin/ere-compiler /ere/bin/ere-compiler
ENTRYPOINT ["/ere/bin/ere-compiler"]

View File

@@ -0,0 +1,37 @@
ARG BASE_ZKVM_IMAGE=ere-base-airbender:latest
ARG BASE_ZKVM_CUDA_IMAGE=ere-base-airbender:latest-cuda
ARG RUNTIME_IMAGE=ubuntu:24.04
ARG RUNTIME_CUDA_IMAGE=nvidia/cuda:12.9.1-runtime-ubuntu24.04
# Whether to enable CUDA feature or not.
ARG CUDA
FROM $BASE_ZKVM_IMAGE AS base
FROM $BASE_ZKVM_CUDA_IMAGE AS base_cuda
FROM base${CUDA:+_cuda} AS build_stage
COPY . /ere
WORKDIR /ere
ARG CUDA
ARG RUSTFLAGS="-Ctarget-cpu=native"
RUN cargo build --release --package ere-server --bin ere-server --features airbender${CUDA:+,cuda} \
&& mkdir bin && mv target/release/ere-server bin/ere-server \
&& cargo clean && rm -rf $CARGO_HOME/registry/src $CARGO_HOME/registry/cache
FROM $RUNTIME_IMAGE AS runtime
FROM $RUNTIME_CUDA_IMAGE AS runtime_cuda
FROM runtime${CUDA:+_cuda} AS runtime_stage
# Copy airbender-cli
COPY --from=build_stage /usr/local/cargo/bin/airbender-cli /usr/local/cargo/bin/airbender-cli
# Add airbender-cli to path
ENV PATH=/usr/local/cargo/bin:$PATH
# Copy ere-server
COPY --from=build_stage /ere/bin/ere-server /ere/bin/ere-server
ENTRYPOINT ["/ere/bin/ere-server"]

View File

@@ -17,7 +17,7 @@ COPY --chmod=755 scripts/sdk_installers/install_nexus_sdk.sh /tmp/install_nexus_
# Run the Nexus SDK installation script.
# This script installs the specific Rust toolchain (nightly-2025-04-06)
# and installs cargo-nexus
# The CARGO_HOME from ere-base (e.g., /root/.cargo) will be used, and cargo-nexus will be in its bin.
# The CARGO_HOME from ere-base (e.g., /usr/local/cargo) will be used, and cargo-nexus will be in its bin.
RUN /tmp/install_nexus_sdk.sh && rm /tmp/install_nexus_sdk.sh # Clean up the script
# Verify Nexus installation

View File

@@ -17,7 +17,7 @@ COPY --chmod=755 scripts/sdk_installers/install_pico_sdk.sh /tmp/install_pico_sd
# Run the Pico SDK installation script.
# This script installs the specific Rust toolchain (nightly-2025-08-04)
# and installs pico-cli (as cargo-pico).
# The CARGO_HOME from ere-base (e.g., /root/.cargo) will be used, and cargo-pico will be in its bin.
# The CARGO_HOME from ere-base (e.g., /usr/local/cargo) will be used, and cargo-pico will be in its bin.
RUN /tmp/install_pico_sdk.sh && rm /tmp/install_pico_sdk.sh # Clean up the script
# Verify Pico installation

View File

@@ -0,0 +1,51 @@
#!/bin/bash
set -e
# --- Utility functions (duplicated) ---
# Checks if a tool is installed and available in PATH.
is_tool_installed() {
command -v "$1" &> /dev/null
}
# Ensures a tool is installed. Exits with an error if not.
ensure_tool_installed() {
local tool_name="$1"
local purpose_message="$2"
if ! is_tool_installed "${tool_name}"; then
echo "Error: Required tool '${tool_name}' could not be found." >&2
if [ -n "${purpose_message}" ]; then
echo " It is needed ${purpose_message}." >&2
fi
echo " Please install it first and ensure it is in your PATH." >&2
exit 1
fi
}
# --- End of Utility functions ---
ensure_tool_installed "rustup" "to manage Rust toolchains"
ensure_tool_installed "git" "to install airbender-cli from a git repository"
ensure_tool_installed "cargo" "to build and install Rust packages"
AIRBENDER_CLI_VERSION_TAG="v0.5.0"
# Install airbender-cli using the specified toolchain and version tag
echo "Installing airbender-cli (version ${AIRBENDER_CLI_VERSION_TAG}) from GitHub repository (matter-labs/zksync-airbender)..."
cargo +nightly install --locked --git https://github.com/matter-labs/zksync-airbender.git --tag "${AIRBENDER_CLI_VERSION_TAG}" ${CUDA:+-F gpu} cli
# Rename cli to airbender-cli
CARGO_HOME=${CARGO_HOME:-$HOME/.cargo}
mv $CARGO_HOME/bin/cli $CARGO_HOME/bin/airbender-cli
# Verify airbender-cli installation
echo "Verifying airbender-cli installation..."
if airbender-cli --version; then
echo "airbender-cli installation verified successfully."
else
echo "Error: 'airbender-cli --version' failed. airbender-cli might not have installed correctly." >&2
echo " Ensure $CARGO_HOME/bin is in your PATH for new shells." >&2
exit 1
fi
# Install cargo-binutils to objcopy ELF to binary file
rustup +nightly component add llvm-tools
cargo install cargo-binutils

View File

@@ -0,0 +1,10 @@
[package]
name = "ere-test-airbender-guest"
version = "0.1.0"
edition = "2021"
[dependencies]
riscv_common = { git = "https://github.com/matter-labs/zksync-airbender", features = ["custom_allocator"], tag = "v0.5.0" }
ere-test-utils = { path = "../../../crates/test-utils" }
[workspace]

View File

@@ -0,0 +1,60 @@
use core::alloc::{GlobalAlloc, Layout};
core::arch::global_asm!(include_str!("./asm_reduced.S"));
#[link_section = ".init.rust"]
#[export_name = "_start_rust"]
unsafe extern "C" fn start_rust() -> ! {
crate::main();
unsafe { core::hint::unreachable_unchecked() }
}
/// A simple heap allocator.
///
/// Allocates memory from left to right, without any deallocation.
struct SimpleAlloc;
unsafe impl GlobalAlloc for SimpleAlloc {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
unsafe { sys_alloc_aligned(layout.size(), layout.align()) }
}
unsafe fn dealloc(&self, _: *mut u8, _: Layout) {}
}
#[global_allocator]
static HEAP: SimpleAlloc = SimpleAlloc;
static mut HEAP_POS: usize = 0;
#[unsafe(no_mangle)]
pub unsafe extern "C" fn sys_alloc_aligned(bytes: usize, align: usize) -> *mut u8 {
unsafe extern "C" {
// start/end of heap defined in `link.x`.
unsafe static _sheap: u8;
unsafe static _eheap: u8;
}
// SAFETY: Single threaded, so nothing else can touch this while we're working.
let mut heap_pos = unsafe { HEAP_POS };
if heap_pos == 0 {
heap_pos = unsafe { (&_sheap) as *const u8 as usize };
}
let offset = heap_pos & (align - 1);
if offset != 0 {
heap_pos += align - offset;
}
let ptr = heap_pos as *mut u8;
let (heap_pos, overflowed) = heap_pos.overflowing_add(bytes);
let eheap = unsafe { (&_eheap) as *const u8 as usize };
if overflowed || heap_pos > eheap {
panic!("Heap exhausted");
}
unsafe { HEAP_POS = heap_pos };
ptr
}

View File

@@ -0,0 +1,160 @@
/* Copied from https://github.com/matter-labs/zksync-airbender/blob/v0.5.0/examples/scripts/asm/asm_reduced.S */
/*
Entry point of all programs (_start).
It initializes DWARF call frame information, the stack pointer, the
frame pointer (needed for closures to work in start_rust) and the global
pointer. Then it calls _start_rust.
*/
.section .init, "ax"
.global _start
_start:
/* Jump to the absolute address defined by the linker script. */
// for 32bit
# lui ra, %hi(_abs_start)
# jr %lo(_abs_start)(ra)
la ra, _abs_start
jr ra
_abs_start:
.cfi_startproc
.cfi_undefined ra
.option push
.option norelax
la gp, __global_pointer$
.option pop
// Assume single core, and put SP to the very top address of the stack region
la sp, _sstack
// Set frame pointer
add s0, sp, zero
jal zero, _start_rust
.cfi_endproc
/*
Machine trap entry point (_machine_start_trap)
*/
.section .trap, "ax"
.global machine_default_start_trap
.align 4
machine_default_start_trap:
// We assume that exception stack is always saved to MSCRATCH
// so we swap it with x31
csrrw x31, mscratch, x31
// write to exception stack
# sw x31, -4(sp)
sw x30, -8(x31)
sw x29, -12(x31)
sw x28, -16(x31)
sw x27, -20(x31)
sw x26, -24(x31)
sw x25, -28(x31)
sw x24, -32(x31)
sw x23, -36(x31)
sw x22, -40(x31)
sw x21, -44(x31)
sw x20, -48(x31)
sw x19, -52(x31)
sw x18, -56(x31)
sw x17, -60(x31)
sw x16, -64(x31)
sw x15, -68(x31)
sw x14, -72(x31)
sw x13, -76(x31)
sw x12, -80(x31)
sw x11, -84(x31)
sw x10, -88(x31)
sw x9, -92(x31)
sw x8, -96(x31)
sw x7, -100(x31)
sw x6, -104(x31)
sw x5, -108(x31)
sw x4, -112(x31)
sw x3, -116(x31)
sw x2, -120(x31)
sw x1, -124(x31)
// we will not restore it, so we are ok to avoid write
# sw x0, -128(x31)
// move valid sp into a0,
mv a0, x31
csrrw x31, mscratch, x0
sw x31, -4(a0)
// restore sp
mv sp, a0
// sp is valid now
addi sp, sp, -128
// pass pointer as first argument
add a0, sp, zero
jal ra, _machine_start_trap_rust
// set return address into mepc
csrw mepc, a0
// save original SP to mscratch for now
lw a0, 8(sp) // it's original sp that we saved in the stack
csrw mscratch, a0 // save it for now
// restore everything we saved
// it's illegal instruction, so we skip. Anyway can not overwrite x0
# lw x0, 0(sp)
lw x1, 4(sp)
# lw x2, 8(sp) // do not overwrite SP yet
lw x3, 12(sp)
lw x4, 16(sp)
lw x5, 20(sp)
lw x6, 24(sp)
lw x7, 28(sp)
lw x8, 32(sp)
lw x9, 36(sp)
lw x10, 40(sp)
lw x11, 44(sp)
lw x12, 48(sp)
lw x13, 52(sp)
lw x14, 56(sp)
lw x15, 60(sp)
lw x16, 64(sp)
lw x17, 68(sp)
lw x18, 72(sp)
lw x19, 76(sp)
lw x20, 80(sp)
lw x21, 84(sp)
lw x22, 88(sp)
lw x23, 92(sp)
lw x24, 96(sp)
lw x25, 100(sp)
lw x26, 104(sp)
lw x27, 108(sp)
lw x28, 112(sp)
lw x29, 116(sp)
lw x30, 120(sp)
lw x31, 124(sp)
addi sp, sp, 128
// we popped everything from the stack
// now save current exception SP to mscratch,
// and put original SP back
csrrw sp, mscratch, sp
mret
/* Make sure there is an abort when linking */
.section .text.abort
.globl abort
abort:
j abort

View File

@@ -0,0 +1,42 @@
#![no_std]
#![no_main]
#![no_builtins]
#![allow(incomplete_features)]
#![feature(allocator_api)]
#![feature(generic_const_exprs)]
extern crate alloc;
use alloc::vec::Vec;
use core::{array, iter::repeat_with};
use ere_test_utils::{
guest::{Digest, Platform, Sha256},
program::{basic::BasicProgram, Program},
};
use riscv_common::{csr_read_word, zksync_os_finish_success};
mod airbender_rt;
struct AirbenderPlatform;
impl Platform for AirbenderPlatform {
fn read_input() -> Vec<u8> {
let len = csr_read_word() as usize;
repeat_with(csr_read_word)
.take(len.div_ceil(4))
.flat_map(|word| word.to_le_bytes())
.take(len)
.collect()
}
fn write_output(output: &[u8]) {
let digest = Sha256::digest(output);
let words = array::from_fn(|i| u32::from_le_bytes(array::from_fn(|j| digest[4 * i + j])));
zksync_os_finish_success(&words);
}
}
#[inline(never)]
fn main() {
BasicProgram::run::<AirbenderPlatform>();
}