├── .clang-format ├── .editorconfig ├── .github ├── actions │ ├── rustup │ │ └── action.yml │ └── sccache │ │ └── action.yml ├── dependabot.yml └── workflows │ ├── bench_pr.yml │ ├── bench_pr_comment.yml │ ├── bench_trends.yml │ ├── main.yml │ └── nightly.yml ├── .gitignore ├── .tool-versions ├── .vscode ├── extensions.json ├── ltex.dictionary.en-US.txt ├── ltex.hiddenFalsePositives.en-US.txt └── settings.json ├── Cargo.toml ├── LICENSE ├── README.md ├── license-check.py ├── risc0 ├── build_kernel │ ├── Cargo.toml │ ├── kernels │ │ ├── cuda │ │ │ ├── fp.h │ │ │ └── fp4.h │ │ └── metal │ │ │ ├── fp.h │ │ │ └── fp4.h │ └── src │ │ └── lib.rs ├── cargo-risczero │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── bin │ │ └── main.rs │ │ ├── commands │ │ └── new.rs │ │ └── lib.rs ├── core │ ├── Cargo.toml │ └── src │ │ ├── field │ │ ├── baby_bear.rs │ │ ├── goldilocks.rs │ │ └── mod.rs │ │ └── lib.rs ├── r0vm │ ├── Cargo.toml │ └── src │ │ └── bin │ │ └── r0vm.rs ├── sys │ ├── Cargo.toml │ ├── build.rs │ ├── kernels │ │ └── zkp │ │ │ ├── cuda │ │ │ ├── all.cu │ │ │ ├── eltwise.cu │ │ │ ├── fri.cu │ │ │ ├── mix.cu │ │ │ ├── ntt.cu │ │ │ ├── poseidon.cu │ │ │ ├── sha.cu │ │ │ ├── sha256.h │ │ │ └── zk.cu │ │ │ └── metal │ │ │ ├── eltwise.metal │ │ │ ├── fri.metal │ │ │ ├── mix.metal │ │ │ ├── ntt.metal │ │ │ ├── poseidon.metal │ │ │ ├── sha.metal │ │ │ ├── sha256.h │ │ │ └── zk.metal │ └── src │ │ └── lib.rs └── zkvm │ ├── Cargo.toml │ ├── README.md │ ├── build.rs │ ├── examples │ ├── fib.rs │ └── loop.rs │ ├── platform │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── lib.rs │ │ ├── memory.rs │ │ └── syscall.rs │ └── src │ ├── binfmt │ ├── elf.rs │ ├── image.rs │ └── mod.rs │ ├── exec │ ├── env.rs │ ├── io.rs │ ├── mod.rs │ ├── monitor.rs │ ├── profile.proto │ └── profiler.rs │ ├── guest │ ├── alloc.rs │ ├── env.rs │ ├── memcpy.s │ ├── memset.s │ ├── mod.rs │ └── sha.rs │ ├── lib.rs │ ├── opcode.rs │ ├── serde │ ├── deserializer.rs │ ├── err.rs │ ├── mod.rs │ └── serializer.rs │ ├── session.rs │ └── testdata │ └── riscv-tests.tgz ├── rust-toolchain.toml └── rustfmt.toml /.clang-format: -------------------------------------------------------------------------------- 1 | AlignOperands: true 2 | AllowShortFunctionsOnASingleLine: Inline 3 | AllowShortIfStatementsOnASingleLine: Never 4 | AllowShortLambdasOnASingleLine: true 5 | AllowShortLoopsOnASingleLine: false 6 | BinPackArguments: false 7 | BinPackParameters: false 8 | BreakConstructorInitializersBeforeComma: true 9 | ColumnLimit: 100 10 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 11 | PointerAlignment: Left 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | tab_width = 4 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | end_of_line = lf 10 | 11 | [*.rs] 12 | indent_size = 4 13 | 14 | [*.py] 15 | indent_size = 4 16 | 17 | [*.bazel] 18 | indent_size = 4 19 | 20 | [*.bzl] 21 | indent_size = 4 22 | 23 | [WORKSPACE] 24 | indent_size = 4 25 | 26 | [*.BUILD] 27 | indent_size = 4 28 | 29 | [*.s] 30 | indent_size = 4 31 | -------------------------------------------------------------------------------- /.github/actions/rustup/action.yml: -------------------------------------------------------------------------------- 1 | name: rustup toolchain install 2 | description: Install the Rust toolchain 3 | runs: 4 | using: composite 5 | steps: 6 | - run: | 7 | if ! command -v rustup &>/dev/null; then 8 | curl --proto '=https' --tlsv1.2 --retry 10 --retry-connrefused -fsSL "https://sh.rustup.rs" | sh -s -- -y 9 | echo "${CARGO_HOME:-$HOME/.cargo}/bin" >> $GITHUB_PATH 10 | fi 11 | shell: bash 12 | 13 | - run: rustup default stable 14 | shell: bash 15 | 16 | - run: echo CARGO_TERM_COLOR=always >> $GITHUB_ENV 17 | shell: bash 18 | 19 | - run: rustc --version --verbose 20 | shell: bash 21 | -------------------------------------------------------------------------------- /.github/actions/sccache/action.yml: -------------------------------------------------------------------------------- 1 | name: sccache install 2 | description: Install sccache 3 | runs: 4 | using: composite 5 | steps: 6 | - if: runner.os == 'Linux' && runner.arch == 'X64' 7 | run: | 8 | echo "SCCACHE_ARCH=x86_64-unknown-linux-musl" >> $GITHUB_ENV 9 | shell: bash 10 | 11 | - if: runner.os == 'macOS' && runner.arch == 'X64' 12 | run: | 13 | echo "SCCACHE_ARCH=x86_64-apple-darwin" >> $GITHUB_ENV 14 | shell: bash 15 | 16 | - if: runner.os == 'macOS' && runner.arch == 'ARM64' 17 | run: | 18 | echo "SCCACHE_ARCH=aarch64-apple-darwin" >> $GITHUB_ENV 19 | shell: bash 20 | 21 | - env: 22 | SCCACHE_URL: https://github.com/mozilla/sccache/releases/download 23 | SCCACHE_VERSION: v0.3.3 24 | run: | 25 | SCCACHE_FILE=sccache-$SCCACHE_VERSION-$SCCACHE_ARCH 26 | curl -L "$SCCACHE_URL/$SCCACHE_VERSION/$SCCACHE_FILE.tar.gz" | tar xz 27 | mkdir -p $HOME/.local/bin 28 | mv -f $SCCACHE_FILE/sccache $HOME/.local/bin/sccache 29 | echo "$HOME/.local/bin" >> $GITHUB_PATH 30 | shell: bash 31 | 32 | - if: runner.os != 'macOS' 33 | name: Cache rust target + HAL_CACHE 34 | uses: risc0/rust-cache@v2 35 | id: rust-cache 36 | with: 37 | key: rust-cache-${{ runner.os }}-${{ runner.arch }} 38 | # TODO: this path will not work on windows. 39 | cache-directories: ~/.cache/risc0 40 | 41 | - run: rm -f $HOME/.cargo/config 42 | shell: bash 43 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "cargo" 9 | directory: "/" 10 | schedule: 11 | interval: "daily" 12 | -------------------------------------------------------------------------------- /.github/workflows/bench_pr.yml: -------------------------------------------------------------------------------- 1 | name: Benchmark Check 2 | 3 | on: 4 | pull_request: 5 | branches: [ main ] 6 | # Only run benchmarks on changes to following paths: 7 | paths: 8 | - 'risc0/**' 9 | - '.github/workflows/bench_*' 10 | 11 | workflow_dispatch: 12 | 13 | concurrency: 14 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 15 | cancel-in-progress: true 16 | 17 | jobs: 18 | bench: 19 | runs-on: [self-hosted, prod, bench, "${{ matrix.os }}", "${{ matrix.device }}"] 20 | 21 | strategy: 22 | fail-fast: false 23 | matrix: 24 | os: [Linux, macOS] 25 | feature: [default] 26 | device: [cpu] 27 | include: 28 | - os: Linux 29 | feature: cuda 30 | device: nvidia_rtx_a5000 31 | - os: macOS 32 | feature: metal 33 | device: apple_m2_pro 34 | 35 | env: 36 | FEATURE: ${{ matrix.feature }} 37 | RUSTC_WRAPPER: sccache 38 | 39 | steps: 40 | - uses: actions/checkout@v3 41 | - uses: ./.github/actions/rustup 42 | - uses: ./.github/actions/sccache 43 | - uses: risc0/criterion-compare-action@risc0 44 | id: criterion-cmp 45 | with: 46 | benchName: fib 47 | branchName: main 48 | features: ${{ matrix.feature }} 49 | prettyName: "${{ matrix.os }}-${{ matrix.feature }}" 50 | outputMarkdown: true 51 | 52 | - name: Create output dir 53 | run: | 54 | mkdir -p ${{ runner.temp }}/pr-output/ 55 | echo ${{ github.event.number }} > ${{ runner.temp }}/pr-output/NR 56 | 57 | - name: Write compare markdown to file 58 | run: | 59 | echo "${{ steps.criterion-cmp.outputs.markdown }}" > ${{ runner.temp }}/pr-output/${{ matrix.os }}-${{ matrix.feature }}-output.md 60 | 61 | - uses: actions/upload-artifact@v3 62 | with: 63 | name: 'md-output' 64 | path: ${{ runner.temp }}/pr-output/ 65 | if-no-files-found: error 66 | 67 | # Note: we just push the comment data into artifacts, here 68 | # the bench_pr_comment.yml picks up after this workflow to make the comment 69 | # needed because: https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ 70 | -------------------------------------------------------------------------------- /.github/workflows/bench_pr_comment.yml: -------------------------------------------------------------------------------- 1 | 2 | name: Create Benchmark PR comment 3 | 4 | on: 5 | workflow_run: 6 | workflows: ["Benchmark Check"] 7 | types: 8 | - completed 9 | 10 | jobs: 11 | create-comment: 12 | runs-on: ubuntu-latest 13 | # Only run if the upstream workflow was successful. 14 | if: ${{ github.event.workflow_run.conclusion == 'success' }} 15 | steps: 16 | - name: 'Download artifact' 17 | uses: actions/github-script@v3.1.0 18 | with: 19 | script: | 20 | var artifacts = await github.actions.listWorkflowRunArtifacts({ 21 | owner: context.repo.owner, 22 | repo: context.repo.repo, 23 | run_id: ${{github.event.workflow_run.id }}, 24 | }); 25 | var matchArtifact = artifacts.data.artifacts.filter((artifact) => { 26 | return artifact.name == "md-output" 27 | })[0]; 28 | var download = await github.actions.downloadArtifact({ 29 | owner: context.repo.owner, 30 | repo: context.repo.repo, 31 | artifact_id: matchArtifact.id, 32 | archive_format: 'zip', 33 | }); 34 | var fs = require('fs'); 35 | fs.writeFileSync('${{ runner.temp }}/md-output.zip', Buffer.from(download.data)); 36 | 37 | - name: unzip benchmark markdown files 38 | run: | 39 | mkdir -p ${{ runner.temp }}/pr-output/ 40 | unzip -o ${{ runner.temp }}/md-output.zip -d ${{ runner.temp }}/pr-output/ 41 | 42 | - name: Merge output files 43 | shell: bash 44 | run: sed h ${{ runner.temp }}/pr-output/*-output.md > ${{ runner.temp }}/pr-output/merged.md 45 | id: download 46 | 47 | - uses: actions/github-script@v3 48 | name: Create PR comment 49 | with: 50 | github-token: ${{secrets.GITHUB_TOKEN}} 51 | script: | 52 | var fs = require('fs'); 53 | var body = fs.readFileSync("${{ runner.temp }}/pr-output/merged.md", 'utf8'); 54 | var issue_number = Number(fs.readFileSync("${{ runner.temp }}/pr-output/NR")); 55 | github.issues.createComment({ 56 | issue_number: issue_number, 57 | owner: context.repo.owner, 58 | repo: context.repo.repo, 59 | body: body 60 | }) 61 | -------------------------------------------------------------------------------- /.github/workflows/bench_trends.yml: -------------------------------------------------------------------------------- 1 | name: Benchmark Trends 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | # Only run benchmarks on changes to following paths: 7 | paths: 8 | - 'risc0/**' 9 | - '.github/workflows/bench_*' 10 | 11 | # uncomment for only for testing changes to this workflow while in a PR 12 | # pull_request: 13 | # branches: [ main ] 14 | 15 | concurrency: 16 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 17 | cancel-in-progress: true 18 | 19 | jobs: 20 | bench: 21 | runs-on: [self-hosted, prod, bench, "${{ matrix.os }}", "${{ matrix.device }}"] 22 | 23 | strategy: 24 | fail-fast: false 25 | matrix: 26 | os: [Linux, macOS] 27 | feature: [default] 28 | device: [cpu] 29 | include: 30 | - os: Linux 31 | feature: cuda 32 | device: nvidia_rtx_a5000 33 | - os: macOS 34 | feature: metal 35 | device: apple_m2_pro 36 | 37 | env: 38 | FEATURE: ${{ matrix.feature }} 39 | RUSTC_WRAPPER: sccache 40 | 41 | steps: 42 | - uses: actions/checkout@v3 43 | - uses: ./.github/actions/rustup 44 | - uses: ./.github/actions/sccache 45 | - run: cargo bench -F $FEATURE --bench fib -- --output-format=bencher | tee output.txt 46 | # Delete existing benchmark repo if it exists, hardcoded path: 47 | # https://github.com/risc0/github-action-benchmark/blob/7eeba8924d4ed651010d72d7319b064fc0c10355/src/write.ts#L392 48 | - run: rm -rf ./benchmark-data-repository || true 49 | shell: bash 50 | - name: Store benchmark result 51 | uses: risc0/github-action-benchmark@v1 52 | with: 53 | name: "${{ matrix.os }}-${{ matrix.device }}" 54 | tool: 'cargo' 55 | github-token: ${{ secrets.BENCHMARK_TOKEN }} 56 | gh-repository: 'github.com/risc0/benchmarks' 57 | gh-pages-branch: main 58 | output-file-path: output.txt 59 | comment-always: false 60 | comment-on-alert: false 61 | auto-push: true 62 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | workflow_dispatch: 10 | 11 | concurrency: 12 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 13 | cancel-in-progress: true 14 | 15 | jobs: 16 | check: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v3 20 | with: 21 | # Full history is required by license-check.py 22 | fetch-depth: 0 23 | - uses: ./.github/actions/rustup 24 | - name: Install cargo-sort 25 | uses: risc0/cargo-install@2cfb0024938d23011106cbf127b393bc83fddba1 26 | with: 27 | crate: cargo-sort 28 | version: "1.0" 29 | - uses: actions/setup-node@v3 30 | with: 31 | node-version: 18 32 | - run: cargo fmt --all --check 33 | - run: cargo fmt --all --check --manifest-path examples/Cargo.toml 34 | - run: cargo fmt --all --check --manifest-path templates/bonsai/Cargo.toml 35 | - run: cargo sort --workspace --check 36 | - run: cargo sort --workspace --check examples 37 | - run: cargo sort --workspace --check templates/bonsai 38 | - run: npm install 39 | working-directory: templates/bonsai 40 | - run: npm run ci 41 | working-directory: templates/bonsai 42 | - uses: actions/setup-python@v4 43 | with: 44 | python-version: '3.10' 45 | - run: python license-check.py 46 | 47 | test: 48 | runs-on: [self-hosted, prod, "${{ matrix.os }}", "${{ matrix.device }}"] 49 | strategy: 50 | fail-fast: false 51 | matrix: 52 | os: [Linux, macOS] 53 | feature: [default] 54 | device: [cpu] 55 | include: 56 | - os: Linux 57 | feature: cuda 58 | device: nvidia_rtx_a5000 59 | - os: macOS 60 | feature: metal 61 | device: apple_m2_pro 62 | env: 63 | FEATURE: ${{ matrix.feature }} 64 | RUST_BACKTRACE: full 65 | RUSTC_WRAPPER: sccache 66 | steps: 67 | - uses: actions/checkout@v3 68 | - uses: ./.github/actions/rustup 69 | - uses: actions/setup-node@v3 70 | with: 71 | node-version: 18 72 | - uses: ./.github/actions/sccache 73 | - run: npm install 74 | working-directory: templates/bonsai 75 | - run: cargo test -F $FEATURE 76 | - run: cargo test -F $FEATURE --tests -- --ignored 77 | - run: cargo test -F $FEATURE --manifest-path examples/Cargo.toml 78 | - run: PATH=$PWD/node_modules/.bin:$PATH cargo test -F $FEATURE 79 | working-directory: templates/bonsai 80 | - run: cargo build --manifest-path risc0/wasm/Cargo.toml --target wasm32-unknown-unknown 81 | if: matrix.device == 'cpu' 82 | - run: cargo check -F $FEATURE --benches 83 | - run: cargo check -p bootstrap 84 | if: matrix.device == 'cpu' 85 | - run: cargo check -p risc0-build 86 | if: matrix.device == 'cpu' 87 | - run: cargo check -F $FEATURE -p risc0-circuit-rv32im 88 | - run: cargo check -p risc0-core 89 | if: matrix.device == 'cpu' 90 | - run: cargo check -F $FEATURE -p risc0-r0vm 91 | - run: cargo check -F $FEATURE -p risc0-sys 92 | - run: cargo check -F $FEATURE -p risc0-zkp 93 | - run: cargo check -F $FEATURE -p risc0-zkvm 94 | - run: sccache --show-stats 95 | 96 | doc: 97 | runs-on: [self-hosted, prod, Linux, cpu] 98 | steps: 99 | - uses: actions/checkout@v3 100 | - uses: ./.github/actions/rustup 101 | - uses: ./.github/actions/sccache 102 | - run: RISC0_SKIP_BUILD=1 cargo doc --no-deps --exclude=risc0-zkvm-methods --workspace 103 | -------------------------------------------------------------------------------- /.github/workflows/nightly.yml: -------------------------------------------------------------------------------- 1 | name: Nightly Tasks 2 | 3 | on: 4 | schedule: 5 | - cron: '7 0 * * *' # Nightly (ish) Pacific 6 | workflow_dispatch: 7 | inputs: 8 | crate_count: 9 | description: 'How many crates (sorted by downloads) to test' 10 | default: 100 11 | 12 | jobs: 13 | crates_validate: 14 | runs-on: [self-hosted, prod, Linux, cpu] 15 | env: 16 | RUST_BACKTRACE: full 17 | RUST_LOG: info 18 | steps: 19 | - uses: actions/checkout@v3 20 | - uses: ./.github/actions/rustup 21 | - name: Set the variables 22 | env: 23 | DEFAULT_CRATE_COUNT: 1000 24 | run: echo "TOP_N_CRATES=${{ github.event.inputs.crate_count || env.DEFAULT_CRATE_COUNT }}" >> $GITHUB_ENV 25 | 26 | - run: cargo install --force --path risc0/cargo-risczero 27 | 28 | - run: cargo build --release 29 | working-directory: tools/crates-validator/ 30 | 31 | - name: Create working directory 32 | run: mkdir -p ${{ runner.temp }}/crate-validation/ 33 | 34 | - name: Generate the profile 35 | run: | 36 | target/release/gen-profiles \ 37 | -r ${{ github.workspace }} \ 38 | -o ${{ runner.temp }}/crate-validation/profiles.json \ 39 | -c $TOP_N_CRATES 40 | working-directory: tools/crates-validator/ 41 | 42 | - name: Validate profiles 43 | run: | 44 | target/release/main \ 45 | -p ${{ runner.temp }}/crate-validation/profiles.json \ 46 | -j ${{ runner.temp }}/crate-validation/profile-results.json &> ${{ runner.temp }}/crate-validation/crate-results.log 47 | # &> ${{ runner.temp }}/crate-validation/crate-results.log 48 | shell: bash 49 | working-directory: tools/crates-validator/ 50 | 51 | - name: Delete input profile 52 | run: rm ${{ runner.temp }}/crate-validation/profiles.json 53 | 54 | - run: tail -n 1 ${{ runner.temp }}/crate-validation/crate-results.log 55 | 56 | - name: Upload artifact results 57 | uses: actions/upload-artifact@v3 58 | with: 59 | name: crates-test-results 60 | path: ${{ runner.temp }}/crate-validation/ 61 | retention-days: 2 62 | 63 | - name: Commit results to risc0/benchmarks 64 | run: | 65 | git clone "https://x-access-token:${{ secrets.BENCHMARK_TOKEN }}@github.com/risc0/benchmarks.git" ${{ runner.temp }}/benchmarks/ 66 | cd ${{ runner.temp }}/benchmarks/ 67 | cp ${{ runner.temp }}/crate-validation/profile-results.json ./dev/crate-validation/profiles.json 68 | git add . 69 | git -c "user.name=nightly-action" -c "user.email=github@users.noreply.github.com" commit -m "Added update at '$(date)'" 70 | git push origin main 71 | shell: bash 72 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.swp 3 | *.swo 4 | .bazelrc.local 5 | .cache 6 | .DS_Store 7 | .idea/ 8 | bazel-* 9 | Cargo.lock 10 | dist/ 11 | rust-project.json 12 | target/ 13 | tmp/ 14 | -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | nodejs lts 2 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "bazelbuild.vscode-bazel", 4 | "EditorConfig.EditorConfig", 5 | "james-yu.latex-workshop", 6 | "kriegalex.vscode-cudacpp", 7 | "rust-lang.rust-analyzer", 8 | "ms-vscode.cpptools", 9 | "stkb.rewrap", 10 | "twxs.cmake", 11 | "valentjn.vscode-ltex" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /.vscode/ltex.dictionary.en-US.txt: -------------------------------------------------------------------------------- 1 | toolchain 2 | bazelisk 3 | cpp 4 | prover 5 | RISC 6 | zk-STARKs 7 | zk-STARK 8 | ZKVM 9 | zkVM 10 | -------------------------------------------------------------------------------- /.vscode/ltex.hiddenFalsePositives.en-US.txt: -------------------------------------------------------------------------------- 1 | {"rule":"A_INSTALL","sentence":"^\\QRust development on MacOS seems to require a full install of xcode.\\E$"} 2 | {"rule":"REPEATED_VERBS","sentence":"^\\Qcrate crates.io docs.rs\\E$"} 3 | {"rule":"REPEATED_VERBS","sentence":"^\\Qcrate crates.io\\E$"} 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "[rust]": { 3 | "editor.formatOnSave": true 4 | }, 5 | "[starlark]": { 6 | "editor.formatOnSave": true 7 | }, 8 | "emmet.includeLanguages": { 9 | "rust": "html" 10 | }, 11 | "rust-analyzer.cargo.extraEnv": { 12 | "RISC0_SKIP_BUILD": "1" 13 | }, 14 | "files.eol": "\n", 15 | "git.ignoreLimitWarning": true, 16 | "files.associations": { 17 | "*.sh": "shellscript", 18 | "*.py": "python", 19 | "*.cl": "c", 20 | "*.rs": "rust", 21 | "*.BUILD": "starlark", 22 | "meson.build": "meson", 23 | "*.mlir": "mlir", 24 | "*.pbtxt": "prototxt", 25 | "*.cfg": "python", 26 | "*.scr": "tcl", 27 | "cstddef": "cpp", 28 | "*.bazel": "starlark", 29 | "fstream": "cpp", 30 | "ios": "cpp", 31 | "iosfwd": "cpp", 32 | "vector": "cpp", 33 | "charconv": "cpp", 34 | "stdexcept": "cpp", 35 | "array": "cpp" 36 | }, 37 | "rust-analyzer.showUnlinkedFileNotification": false 38 | } -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "risc0/build_kernel", 5 | "risc0/core", 6 | "risc0/r0vm", 7 | "risc0/sys", 8 | "risc0/zkvm", 9 | "risc0/zkvm/platform", 10 | ] 11 | exclude = [ 12 | "tools/crates-validator" 13 | ] 14 | 15 | [patch.crates-io] 16 | rrs-lib = { path = "../rrs/rrs-lib/"} 17 | 18 | [workspace.package] 19 | version = "0.14.0" 20 | edition = "2021" 21 | license = "Apache-2.0" 22 | homepage = "https://risczero.com/" 23 | repository = "https://github.com/risc0/risc0/" 24 | 25 | [workspace.dependencies] 26 | risc0-build-kernel = { version = "0.14.0", default-features = false, path = "risc0/build_kernel" } 27 | risc0-core = { version = "0.14.0", default-features = false, path = "risc0/core" } 28 | risc0-sys = { version = "0.14.0", default-features = false, path = "risc0/sys" } 29 | risc0-zkvm = { version = "0.14.0", default-features = false, path = "risc0/zkvm" } 30 | risc0-zkvm-platform = { version = "0.14.0", default-features = false, path = "risc0/zkvm/platform" } 31 | 32 | [profile.bench] 33 | lto = true 34 | 35 | # Always optimize; otherwise tests take excessively long. 36 | [profile.dev] 37 | opt-level = 3 38 | 39 | [profile.dev.build-override] 40 | opt-level = 3 41 | 42 | [profile.release] 43 | debug = 1 44 | lto = true 45 | 46 | [profile.release.build-override] 47 | opt-level = 3 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 |

5 | 6 |

RISC Zero

7 | 8 | [![Crates.io][crates-badge]][crates-url] 9 | [![MIT licensed][licence-badge]][licence-url] 10 | [![Build Status][actions-badge]][actions-url] 11 | [![Discord chat][discord-badge]][discord-url] 12 | [![Twitter][twitter-badge]][twitter-url] 13 | 14 | [crates-badge]: https://img.shields.io/badge/crates.io-v0.14-orange 15 | [crates-url]: https://crates.io/crates/risc0-zkvm 16 | [licence-badge]: https://img.shields.io/github/license/risc0/risc0?color=blue 17 | [licence-url]: https://github.com/risc0/risc0/blob/main/LICENSE 18 | [actions-badge]: https://img.shields.io/github/actions/workflow/status/risc0/risc0/main.yml?branch=main 19 | [actions-url]: https://github.com/risc0/risc0/actions?query=workflow%3ACI+branch%3Amain 20 | [discord-badge]: https://img.shields.io/discord/953703904086994974.svg?logo=discord&style=flat-square 21 | [discord-url]: https://discord.gg/risczero 22 | [twitter-badge]: https://img.shields.io/twitter/follow/risczero 23 | [twitter-url]: https://twitter.com/risczero 24 | 25 | [zk-proof]: https://en.wikipedia.org/wiki/Non-interactive_zero-knowledge_proof 26 | [risc-v]: https://en.wikipedia.org/wiki/RISC-V 27 | [crates.io]: https://crates.io 28 | [cargo-risczero-readme]: https://github.com/risc0/risc0/blob/main/risc0/cargo-risczero/README.md 29 | [website-getting-started]: https://www.risczero.com/docs 30 | [examples]: https://github.com/risc0/risc0/tree/main/examples 31 | [install-rust]: https://doc.rust-lang.org/cargo/getting-started/installation.html 32 | 33 | > WARNING: This software is still experimental, we do not recommend it for 34 | > production use (see Security section). 35 | 36 | RISC Zero is a zero-knowledge verifiable general computing platform based on 37 | [zk-STARKs][zk-proof] and the [RISC-V] microarchitecture. 38 | 39 | A [zero knowledge proof][zk-proof] allows one party (the prover) to convince 40 | another party (the verifier) that something is true without revealing all the 41 | details. In the case of RISC Zero, the prover can show they correctly executed 42 | some code (known to both parties), while only revealing to the verifier the 43 | output of the code, not any of its inputs or any state during execution. 44 | 45 | The code runs in a special virtual machine, called a *zkVM*. The RISC Zero zkVM 46 | emulates a small [RISC-V] computer, allowing it to run arbitrary code in any 47 | language, so long as a compiler toolchain exists that targets RISC-V. Currently, 48 | SDK support exists for Rust, C, and C++. 49 | 50 | ## Protocol overview and terminology 51 | 52 | First, the code to be proven must be compiled from its implementation language 53 | into a *method*. A method is represented by a RISC-V ELF file with a special 54 | entry point that runs the code of the method. Additionally, one can compute for 55 | a given method its *image ID* which is a special type of cryptographic hash of 56 | the ELF file, and is required for verification. 57 | 58 | Next, the prover runs the method inside the zkVM. The logical RISC-V machine 59 | running inside the zkVM is called the *guest* and the prover running the zkVM is 60 | called the *host*. The guest and the host can communicate with each other 61 | during the execution of the method, but the host cannot modify the execution of 62 | the guest in any way, or the proof being generated will be invalid. During 63 | execution, the guest code can write to a special append-only log called the 64 | *journal* that represents the official output of the computation. 65 | 66 | Presuming the method terminated correctly, a *receipt* is produced, which 67 | provides the proof of correct execution. This receipt consists of 2 parts: the 68 | journal written during execution and a blob of opaque cryptographic data called 69 | the *seal*. 70 | 71 | The verifier can then verify the receipt and examine the log. If any tampering 72 | was done to the journal or the seal, the receipt will fail to verify. 73 | Additionally, it is cryptographically infeasible to generate a valid receipt 74 | unless the output of the journal is the exactly correct output for some valid 75 | execution of the method whose image ID matches the receipt. In summary, the 76 | receipt acts as a zero knowledge proof of correct execution. 77 | 78 | Because the protocol is zero knowledge, the verifier cannot infer anything about 79 | the details of the execution or any data passed between the host and the guest 80 | (aside from what is implied by the data written to the journal and the correct 81 | execution of the code). 82 | 83 | ## Security 84 | 85 | This code is based on the well studied zk-STARK protocol, which has been proven 86 | secure under the random oracle model, with the only assumption being the 87 | security of the cryptographic hash used. Our implementation uses SHA-256 for 88 | that hash and targets 100 bits of security. 89 | 90 | That said, this code is still under heavy development and has not been audited. 91 | There may be bugs in the zk-STARK implementation, the arithmetic circuit used to 92 | instantiate the RISC-V zkVM, or any other element of the code's implementation. 93 | Such bugs may impact the security of receipts, leak information, or cause any 94 | other manner of problems. Caveat emptor. 95 | 96 | ## Getting Started 97 | 98 | To start your own project, you can use our `cargo risczero` tool to write the 99 | initial boilerplate and set up a standard directory structure. 100 | First, [install Rust][install-rust] if you don't already have it, then install the `cargo risczero` tool: 101 | 102 | ``` 103 | cargo install cargo-risczero 104 | ``` 105 | 106 | Then, create a new project (named `my_project` in this example): 107 | 108 | ``` 109 | cargo risczero new my_project 110 | ``` 111 | 112 | More details and options for `cargo risczero` are given in 113 | [its README][cargo-risczero-readme]. 114 | 115 | For more guidance on how to use RISC Zero, how RISC Zero projects are typically 116 | structured, and other resources useful to developers new to RISC Zero, see our 117 | [Getting Started page][website-getting-started]. 118 | 119 | ## Rust Binaries 120 | 121 | | crate | [crates.io] | 122 | | -------------- | ---------------------------------------------------------------------------------------------------- | 123 | | cargo-risczero | [![x](https://img.shields.io/badge/crates.io-v0.14-orange)](https://crates.io/crates/cargo-risczero) | 124 | | risc0-r0vm | [![x](https://img.shields.io/badge/crates.io-v0.14-orange)](https://crates.io/crates/risc0-r0vm) | 125 | | risc0-tools | [![x](https://img.shields.io/badge/crates.io-v0.14-orange)](https://crates.io/crates/risc0-tools) | 126 | 127 | ## Rust Libraries 128 | 129 | | crate | [crates.io] | [docs.rs](https://docs.rs) | 130 | | ------------------------ | -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | 131 | | risc0-build | [![x](https://img.shields.io/badge/crates.io-v0.14-orange)](https://crates.io/crates/risc0-build) | [![](https://img.shields.io/docsrs/risc0-build)](https://docs.rs/risc0-build) | 132 | | risc0-build-kernel | [![x](https://img.shields.io/badge/crates.io-v0.14-orange)](https://crates.io/crates/risc0-build-kernel) | [![](https://img.shields.io/docsrs/risc0-build-kernel)](https://docs.rs/risc0-build-kernel) | 133 | | risc0-circuit-rv32im | [![x](https://img.shields.io/badge/crates.io-v0.14-orange)](https://crates.io/crates/risc0-circuit-rv32im) | [![](https://img.shields.io/docsrs/risc0-circuit-rv32im)](https://docs.rs/risc0-circuit-rv32im) | 134 | | risc0-circuit-rv32im-sys | [![x](https://img.shields.io/badge/crates.io-v0.14-orange)](https://crates.io/crates/risc0-circuit-rv32im-sys) | [![](https://img.shields.io/docsrs/risc0-circuit-rv32im-sys)](https://docs.rs/risc0-circuit-rv32im-sys) | 135 | | risc0-core | [![x](https://img.shields.io/badge/crates.io-v0.14-orange)](https://crates.io/crates/risc0-core) | [![](https://img.shields.io/docsrs/risc0-core)](https://docs.rs/risc0-core) | 136 | | risc0-sys | [![x](https://img.shields.io/badge/crates.io-v0.14-orange)](https://crates.io/crates/risc0-sys) | [![](https://img.shields.io/docsrs/risc0-sys)](https://docs.rs/risc0-sys) | 137 | | risc0-zkp | [![x](https://img.shields.io/badge/crates.io-v0.14-orange)](https://crates.io/crates/risc0-zkp) | [![](https://img.shields.io/docsrs/risc0-zkp)](https://docs.rs/risc0-zkp) | 138 | | risc0-zkvm | [![x](https://img.shields.io/badge/crates.io-v0.14-orange)](https://crates.io/crates/risc0-zkvm) | [![](https://img.shields.io/docsrs/risc0-zkvm)](https://docs.rs/risc0-zkvm) | 139 | | risc0-zkvm-platform | [![x](https://img.shields.io/badge/crates.io-v0.14-orange)](https://crates.io/crates/risc0-zkvm-platform) | [![](https://img.shields.io/docsrs/risc0-zkvm-platform)](https://docs.rs/risc0-zkvm-platform) | 140 | 141 | ## `cargo risczero` tool 142 | 143 | Included is a tool to manage RISC Zero project directories. 144 | 145 | ```bash 146 | # Installing from local source 147 | cargo install --path risc0/cargo-risczero 148 | 149 | # Install from crates.io 150 | cargo install cargo-risczero 151 | ``` 152 | 153 | ## License 154 | 155 | This project is licensed under the Apache2 license. See [LICENSE](LICENSE). 156 | -------------------------------------------------------------------------------- /license-check.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import os 5 | from pathlib import Path 6 | import subprocess 7 | 8 | PUBLIC_HEADER = ''' 9 | // Copyright {YEAR} RISC Zero, Inc. 10 | // 11 | // Licensed under the Apache License, Version 2.0 (the "License"); 12 | // you may not use this file except in compliance with the License. 13 | // You may obtain a copy of the License at 14 | // 15 | // http://www.apache.org/licenses/LICENSE-2.0 16 | // 17 | // Unless required by applicable law or agreed to in writing, software 18 | // distributed under the License is distributed on an "AS IS" BASIS, 19 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | // See the License for the specific language governing permissions and 21 | // limitations under the License. 22 | '''.strip().splitlines() 23 | 24 | EXTENSIONS = [ 25 | '.cpp', 26 | '.h', 27 | '.rs', 28 | '.sol', 29 | ] 30 | 31 | SKIP_DIRS = [ 32 | str(Path.cwd()) + "/templates/rust-starter", 33 | ] 34 | 35 | def check_header(expected_year, lines_actual): 36 | for (expected, actual) in zip(PUBLIC_HEADER, lines_actual): 37 | expected = expected.replace('{YEAR}', expected_year) 38 | if expected != actual: 39 | return (expected, actual) 40 | return None 41 | 42 | 43 | def check_file(root, file): 44 | cmd = ['git', 'log', '-1', '--format=%ad', '--date=format:%Y', file] 45 | expected_year = subprocess.check_output(cmd, encoding='UTF-8').strip() 46 | rel_path = file.relative_to(root) 47 | lines = file.read_text().splitlines() 48 | result = check_header(expected_year, lines) 49 | if result: 50 | print(f'{rel_path}: invalid header!') 51 | print(f' expected: {result[0]}') 52 | print(f' actual: {result[1]}') 53 | return 1 54 | return 0 55 | 56 | 57 | def repo_root(): 58 | """Return an absolute Path to the repo root""" 59 | cmd = ["git", "rev-parse", "--show-toplevel"] 60 | return Path(subprocess.check_output(cmd, encoding='UTF-8').strip()) 61 | 62 | 63 | def tracked_files(): 64 | """Yield all file paths tracked by git""" 65 | cmd = ["git", "ls-tree", "--full-tree", "--name-only", "-r", "HEAD"] 66 | tree = subprocess.check_output(cmd, encoding='UTF-8').strip() 67 | for path in tree.splitlines(): 68 | yield (repo_root() / Path(path)).absolute() 69 | 70 | 71 | def main(): 72 | root = repo_root() 73 | ret = 0 74 | for path in tracked_files(): 75 | if path.suffix in EXTENSIONS: 76 | skip = False 77 | for path_start in SKIP_DIRS: 78 | if str(path).startswith(path_start): 79 | skip = True 80 | break 81 | if skip: 82 | continue 83 | 84 | ret |= check_file(root, path) 85 | sys.exit(ret) 86 | 87 | 88 | if __name__ == "__main__": 89 | main() 90 | -------------------------------------------------------------------------------- /risc0/build_kernel/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "risc0-build-kernel" 3 | description = "RISC Zero tool for building kernels" 4 | version = { workspace = true } 5 | edition = { workspace = true } 6 | license = { workspace = true } 7 | homepage = { workspace = true } 8 | repository = { workspace = true } 9 | 10 | [dependencies] 11 | cc = { version = "1.0", features = ["parallel"] } 12 | directories = "5.0" 13 | glob = "0.3" 14 | hex = "0.4" 15 | sha2 = "0.10" 16 | tempfile = "3.3" 17 | -------------------------------------------------------------------------------- /risc0/build_kernel/kernels/cuda/fp.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 RISC Zero, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #pragma once 16 | 17 | /// \file 18 | /// Defines the core finite field data type, Fp, and some free functions on the type. 19 | 20 | #include 21 | #include 22 | 23 | /// The Fp class is an element of the finite field F_p, where P is the prime number 15*2^27 + 1. 24 | /// Put another way, Fp is basically integer arithmetic modulo P. 25 | /// 26 | /// The 'Fp' datatype is the core type of all of the operations done within the zero knowledge 27 | /// proofs, and is smallest 'addressable' datatype, and the base type of which all composite types 28 | /// are built. In many ways, one can imagine it as the word size of a very strange architecture. 29 | /// 30 | /// This specific prime P was chosen to: 31 | /// - Be less than 2^31 so that it fits within a 32 bit word and doesn't overflow on addition. 32 | /// - Otherwise have as large a power of 2 in the factors of P-1 as possible. 33 | /// 34 | /// This last property is useful for number theoretical transforms (the fast fourier transform 35 | /// equivelant on finite fields). See NTT.h for details. 36 | /// 37 | /// The Fp class wraps all the standard arithmatic operations to make the finite field elements look 38 | /// basically like ordinary numbers (which they mostly are). 39 | class Fp { 40 | public: 41 | /// The value of P, the modulus of Fp. 42 | static constexpr uint32_t P = 15 * (uint32_t(1) << 27) + 1; 43 | static constexpr uint32_t M = 0x88000001; 44 | static constexpr uint32_t R2 = 1172168163; 45 | 46 | private: 47 | // The actual value, always < P. 48 | uint32_t val; 49 | 50 | // We make 'impls' of the core ops which all the other uses call. This is done to allow for 51 | // tweaking of the implementation later, for example switching to montgomery representation or 52 | // doing inline assembly or some crazy CUDA stuff. 53 | 54 | // Add two numbers 55 | static __device__ constexpr uint32_t add(uint32_t a, uint32_t b) { 56 | uint32_t r = a + b; 57 | return (r >= P ? r - P : r); 58 | } 59 | 60 | // Subtract two numbers 61 | static __device__ constexpr uint32_t sub(uint32_t a, uint32_t b) { 62 | uint32_t r = a - b; 63 | return (r > P ? r + P : r); 64 | } 65 | 66 | // Multiply two numbers 67 | static __device__ constexpr uint32_t mul(uint32_t a, uint32_t b) { 68 | uint64_t o64 = uint64_t(a) * uint64_t(b); 69 | uint32_t low = -uint32_t(o64); 70 | uint32_t red = M * low; 71 | o64 += uint64_t(red) * uint64_t(P); 72 | uint32_t ret = o64 >> 32; 73 | return (ret >= P ? ret - P : ret); 74 | } 75 | 76 | // Encode / Decode 77 | static __device__ constexpr uint32_t encode(uint32_t a) { return mul(R2, a); } 78 | 79 | static __device__ constexpr uint32_t decode(uint32_t a) { return mul(1, a); } 80 | 81 | // A private constructor that take the 'interal' form. 82 | __device__ constexpr Fp(uint32_t val, bool ignore) : val(val) {} 83 | 84 | public: 85 | /// Default constructor, sets value to 0. 86 | __device__ constexpr Fp() : val(0) {} 87 | 88 | /// Construct an FP from a uint32_t, wrap if needed 89 | __device__ constexpr Fp(uint32_t val) : val(encode(val)) {} 90 | 91 | /// Convert to a uint32_t 92 | __device__ constexpr uint32_t asUInt32() const { return decode(val); } 93 | 94 | /// Return the raw underlying word 95 | __device__ constexpr uint32_t asRaw() const { return val; } 96 | 97 | /// Get the largest value, basically P - 1. 98 | __device__ static constexpr Fp maxVal() { return P - 1; } 99 | 100 | /// Get an 'invalid' Fp value 101 | __device__ static constexpr Fp invalid() { return Fp(0xfffffffful, true); } 102 | 103 | // Implement all the various overloads 104 | __device__ constexpr void operator=(uint32_t rhs) { val = encode(rhs); } 105 | 106 | __device__ constexpr Fp operator+(Fp rhs) const { return Fp(add(val, rhs.val), true); } 107 | 108 | __device__ constexpr Fp operator-() const { return Fp(sub(0, val), true); } 109 | 110 | __device__ constexpr Fp operator-(Fp rhs) const { return Fp(sub(val, rhs.val), true); } 111 | 112 | __device__ constexpr Fp operator*(Fp rhs) const { return Fp(mul(val, rhs.val), true); } 113 | 114 | __device__ constexpr Fp operator+=(Fp rhs) { 115 | val = add(val, rhs.val); 116 | return *this; 117 | } 118 | 119 | __device__ constexpr Fp operator-=(Fp rhs) { 120 | val = sub(val, rhs.val); 121 | return *this; 122 | } 123 | 124 | __device__ constexpr Fp operator*=(Fp rhs) { 125 | val = mul(val, rhs.val); 126 | return *this; 127 | } 128 | 129 | __device__ constexpr bool operator==(Fp rhs) const { return val == rhs.val; } 130 | 131 | __device__ constexpr bool operator!=(Fp rhs) const { return val != rhs.val; } 132 | 133 | __device__ constexpr bool operator<(Fp rhs) const { return decode(val) < decode(rhs.val); } 134 | 135 | __device__ constexpr bool operator<=(Fp rhs) const { return decode(val) <= decode(rhs.val); } 136 | 137 | __device__ constexpr bool operator>(Fp rhs) const { return decode(val) > decode(rhs.val); } 138 | 139 | __device__ constexpr bool operator>=(Fp rhs) const { return decode(val) >= decode(rhs.val); } 140 | 141 | // Post-inc/dec 142 | __device__ constexpr Fp operator++(int) { 143 | Fp r = *this; 144 | val = add(val, encode(1)); 145 | return r; 146 | } 147 | 148 | __device__ constexpr Fp operator--(int) { 149 | Fp r = *this; 150 | val = sub(val, encode(1)); 151 | return r; 152 | } 153 | 154 | // Pre-inc/dec 155 | __device__ constexpr Fp operator++() { 156 | val = add(val, encode(1)); 157 | return *this; 158 | } 159 | 160 | __device__ constexpr Fp operator--() { 161 | val = sub(val, encode(1)); 162 | return *this; 163 | } 164 | }; 165 | 166 | /// Raise an value to a power 167 | __device__ constexpr inline Fp pow(Fp x, size_t n) { 168 | Fp tot = 1; 169 | while (n != 0) { 170 | if (n % 2 == 1) { 171 | tot *= x; 172 | } 173 | n = n / 2; 174 | x *= x; 175 | } 176 | return tot; 177 | } 178 | 179 | /// Compute the multiplicative inverse of x, or `1/x` in finite field terms. Since `x^(P-1) == 1 180 | /// (mod P)` for any x != 0 (as a consequence of Fermat's little therorm), it follows that `x * 181 | /// x^(P-2) == 1 (mod P)` for x != 0. That is, `x^(P-2)` is the multiplicative inverse of x. 182 | /// Computed this way, the 'inverse' of zero comes out as zero, which is convient in many cases, so 183 | /// we leave it. 184 | __device__ constexpr inline Fp inv(Fp x) { 185 | return pow(x, Fp::P - 2); 186 | } 187 | -------------------------------------------------------------------------------- /risc0/build_kernel/kernels/cuda/fp4.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 RISC Zero, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #pragma once 16 | 17 | /// \file 18 | /// Defines Fp4, a finite field F_p^4, based on Fp via the irreducable polynomial x^4 - 11. 19 | 20 | #include "fp.h" 21 | 22 | // Defines instead of constexpr to appease CUDAs limitations around constants. 23 | // undef'd at the end of this file. 24 | #define BETA Fp(11) 25 | #define NBETA Fp(Fp::P - 11) 26 | 27 | /// Intstances of Fp4 are element of a finite field F_p^4. They are represented as elements of 28 | /// F_p[X] / (X^4 - 11). Basically, this is a 'big' finite field (about 2^128 elements), which is 29 | /// used when the security of various operations depends on the size of the field. It has the field 30 | /// Fp as a subfield, which means operations by the two are compatable, which is important. The 31 | /// irreducible polynomial was choosen to be the simpilest possible one, x^4 - B, where 11 is the 32 | /// smallest B which makes the polynomial irreducable. 33 | struct Fp4 { 34 | /// The elements of Fp4, elems[0] + elems[1]*X + elems[2]*X^2 + elems[3]*x^4 35 | Fp elems[4]; 36 | 37 | /// Default constructor makes the zero elements 38 | __device__ constexpr Fp4() {} 39 | 40 | /// Initialize from uint32_t 41 | __device__ explicit constexpr Fp4(uint32_t x) { 42 | elems[0] = x; 43 | elems[1] = 0; 44 | elems[2] = 0; 45 | elems[3] = 0; 46 | } 47 | 48 | /// Convert from Fp to Fp4. 49 | __device__ explicit constexpr Fp4(Fp x) { 50 | elems[0] = x; 51 | elems[1] = 0; 52 | elems[2] = 0; 53 | elems[3] = 0; 54 | } 55 | 56 | /// Explicitly construct an Fp4 from parts 57 | __device__ constexpr Fp4(Fp a, Fp b, Fp c, Fp d) { 58 | elems[0] = a; 59 | elems[1] = b; 60 | elems[2] = c; 61 | elems[3] = d; 62 | } 63 | 64 | // Implement the addition/subtraction overloads 65 | __device__ constexpr Fp4 operator+=(Fp4 rhs) { 66 | for (uint32_t i = 0; i < 4; i++) { 67 | elems[i] += rhs.elems[i]; 68 | } 69 | return *this; 70 | } 71 | 72 | __device__ constexpr Fp4 operator-=(Fp4 rhs) { 73 | for (uint32_t i = 0; i < 4; i++) { 74 | elems[i] -= rhs.elems[i]; 75 | } 76 | return *this; 77 | } 78 | 79 | __device__ constexpr Fp4 operator+(Fp4 rhs) const { 80 | Fp4 result = *this; 81 | result += rhs; 82 | return result; 83 | } 84 | 85 | __device__ constexpr Fp4 operator-(Fp4 rhs) const { 86 | Fp4 result = *this; 87 | result -= rhs; 88 | return result; 89 | } 90 | 91 | __device__ constexpr Fp4 operator-() const { return Fp4() - *this; } 92 | 93 | // Implement the simple multiplication case by the subfield Fp 94 | // Fp * Fp4 is done as a free function due to C++'s operator overloading rules. 95 | __device__ constexpr Fp4 operator*=(Fp rhs) { 96 | for (uint32_t i = 0; i < 4; i++) { 97 | elems[i] *= rhs; 98 | } 99 | return *this; 100 | } 101 | 102 | __device__ constexpr Fp4 operator*(Fp rhs) const { 103 | Fp4 result = *this; 104 | result *= rhs; 105 | return result; 106 | } 107 | 108 | // Now we get to the interesting case of multiplication. Basically, multiply out the polynomial 109 | // representations, and then reduce module x^4 - B, which means powers >= 4 get shifted back 4 and 110 | // multiplied by -beta. We could write this as a double loops with some if's and hope it gets 111 | // unrolled properly, but it'a small enough to just hand write. 112 | __device__ constexpr Fp4 operator*(Fp4 rhs) const { 113 | // Rename the element arrays to something small for readability 114 | #define a elems 115 | #define b rhs.elems 116 | return Fp4(a[0] * b[0] + NBETA * (a[1] * b[3] + a[2] * b[2] + a[3] * b[1]), 117 | a[0] * b[1] + a[1] * b[0] + NBETA * (a[2] * b[3] + a[3] * b[2]), 118 | a[0] * b[2] + a[1] * b[1] + a[2] * b[0] + NBETA * (a[3] * b[3]), 119 | a[0] * b[3] + a[1] * b[2] + a[2] * b[1] + a[3] * b[0]); 120 | #undef a 121 | #undef b 122 | } 123 | __device__ constexpr Fp4 operator*=(Fp4 rhs) { 124 | *this = *this * rhs; 125 | return *this; 126 | } 127 | 128 | // Equality 129 | __device__ constexpr bool operator==(Fp4 rhs) const { 130 | for (uint32_t i = 0; i < 4; i++) { 131 | if (elems[i] != rhs.elems[i]) { 132 | return false; 133 | } 134 | } 135 | return true; 136 | } 137 | 138 | __device__ constexpr bool operator!=(Fp4 rhs) const { 139 | return !(*this == rhs); 140 | } 141 | 142 | __device__ constexpr Fp constPart() const { 143 | return elems[0]; 144 | } 145 | }; 146 | 147 | /// Overload for case where LHS is Fp (RHS case is handled as a method) 148 | __device__ constexpr inline Fp4 operator*(Fp a, Fp4 b) { 149 | return b * a; 150 | } 151 | 152 | /// Raise an Fp4 to a power 153 | __device__ constexpr inline Fp4 pow(Fp4 x, size_t n) { 154 | Fp4 tot(1); 155 | while (n != 0) { 156 | if (n % 2 == 1) { 157 | tot *= x; 158 | } 159 | n = n / 2; 160 | x *= x; 161 | } 162 | return tot; 163 | } 164 | 165 | /// Compute the multiplicative inverse of an Fp4. 166 | __device__ constexpr inline Fp4 inv(Fp4 in) { 167 | #define a in.elems 168 | // Compute the multiplicative inverse by basicly looking at Fp4 as a composite field and using the 169 | // same basic methods used to invert complex numbers. We imagine that initially we have a 170 | // numerator of 1, and an denominator of a. i.e out = 1 / a; We set a' to be a with the first and 171 | // third components negated. We then multiply the numerator and the denominator by a', producing 172 | // out = a' / (a * a'). By construction (a * a') has 0's in it's first and third elements. We 173 | // call this number, 'b' and compute it as follows. 174 | Fp b0 = a[0] * a[0] + BETA * (a[1] * (a[3] + a[3]) - a[2] * a[2]); 175 | Fp b2 = a[0] * (a[2] + a[2]) - a[1] * a[1] + BETA * (a[3] * a[3]); 176 | // Now, we make b' by inverting b2. When we muliply both sizes by b', we get out = (a' * b') / 177 | // (b * b'). But by construcion b * b' is in fact an element of Fp, call it c. 178 | Fp c = b0 * b0 + BETA * b2 * b2; 179 | // But we can now invert C direcly, and multiply by a'*b', out = a'*b'*inv(c) 180 | Fp ic = inv(c); 181 | // Note: if c == 0 (really should only happen if in == 0), our 'safe' version of inverse results 182 | // in ic == 0, and thus out = 0, so we have the same 'safe' behavior for Fp4. Oh, and since we 183 | // want to multiply everything by ic, it's slightly faster to premultiply the two parts of b by ic 184 | // (2 multiplies instead of 4) 185 | b0 *= ic; 186 | b2 *= ic; 187 | return Fp4(a[0] * b0 + BETA * a[2] * b2, 188 | -a[1] * b0 + NBETA * a[3] * b2, 189 | -a[0] * b2 + a[2] * b0, 190 | a[1] * b2 - a[3] * b0); 191 | #undef a 192 | } 193 | 194 | #undef BETA 195 | #undef NBETA 196 | -------------------------------------------------------------------------------- /risc0/build_kernel/kernels/metal/fp.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 RISC Zero, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #pragma once 16 | 17 | /// \file 18 | /// Defines the core finite field data type, Fp, and some free functions on the type. 19 | 20 | #include 21 | 22 | /// The Fp class is an element of the finite field F_p, where P is the prime number 15*2^27 + 1. 23 | /// Put another way, Fp is basically integer arithmetic modulo P. 24 | /// 25 | /// The 'Fp' datatype is the core type of all of the operations done within the zero knowledge 26 | /// proofs, and is smallest 'addressable' datatype, and the base type of which all composite types 27 | /// are built. In many ways, one can imagine it as the word size of a very strange architecture. 28 | /// 29 | /// This specific prime P was chosen to: 30 | /// - Be less than 2^31 so that it fits within a 32 bit word and doesn't overflow on addition. 31 | /// - Otherwise have as large a power of 2 in the factors of P-1 as possible. 32 | /// 33 | /// This last property is useful for number theoretical transforms (the fast fourier transform 34 | /// equivelant on finite fields). See NTT.h for details. 35 | /// 36 | /// The Fp class wraps all the standard arithmatic operations to make the finite field elements look 37 | /// basically like ordinary numbers (which they mostly are). 38 | class Fp { 39 | public: 40 | /// The value of P, the modulus of Fp. 41 | static constant uint32_t P = 15 * (uint32_t(1) << 27) + 1; 42 | static constant uint32_t M = 0x88000001; 43 | static constant uint32_t R2 = 1172168163; 44 | 45 | private: 46 | // The actual value, always < P. 47 | uint32_t val; 48 | 49 | // We make 'impls' of the core ops which all the other uses call. This is done to allow for 50 | // tweaking of the implementation later, for example switching to montgomery representation or 51 | // doing inline assembly or some crazy CUDA stuff. 52 | 53 | // Add two numbers 54 | static constexpr uint32_t add(uint32_t a, uint32_t b) { 55 | uint32_t r = a + b; 56 | return (r >= P ? r - P : r); 57 | } 58 | 59 | // Subtract two numbers 60 | static constexpr uint32_t sub(uint32_t a, uint32_t b) { 61 | uint32_t r = a - b; 62 | return (r > P ? r + P : r); 63 | } 64 | 65 | // Multiply two numbers 66 | static constexpr uint32_t mul(uint32_t a, uint32_t b) { 67 | uint64_t o64 = uint64_t(a) * uint64_t(b); 68 | uint32_t low = -uint32_t(o64); 69 | uint32_t red = M * low; 70 | o64 += uint64_t(red) * uint64_t(P); 71 | uint32_t ret = o64 >> 32; 72 | return (ret >= P ? ret - P : ret); 73 | } 74 | 75 | // Encode / Decode 76 | static constexpr uint32_t encode(uint32_t a) { return mul(R2, a); } 77 | 78 | static constexpr uint32_t decode(uint32_t a) { return mul(1, a); } 79 | 80 | // A private constructor that take the 'interal' form. 81 | constexpr Fp(uint32_t val, bool ignore) : val(val) {} 82 | 83 | public: 84 | /// Default constructor, sets value to 0. 85 | constexpr Fp() : val(0) {} 86 | 87 | /// Construct an FP from a uint32_t, wrap if needed 88 | constexpr Fp(uint32_t val) : val(encode(val)) {} 89 | 90 | /// Convert to a uint32_t 91 | constexpr uint32_t asUInt32() const { return decode(val); } 92 | 93 | constexpr uint32_t asUInt32() device const { return decode(val); } 94 | 95 | /// Return the raw underlying word 96 | constexpr uint32_t asRaw() const { return val; } 97 | 98 | /// Get the largest value, basically P - 1. 99 | static constexpr Fp maxVal() { return P - 1; } 100 | 101 | /// Get an 'invalid' Fp value 102 | static constexpr Fp invalid() { return Fp(0xfffffffful, true); } 103 | 104 | // Implement all the various overloads 105 | constexpr void operator=(uint32_t rhs) { val = encode(rhs); } 106 | 107 | constexpr void operator=(uint32_t rhs) device { val = encode(rhs); } 108 | 109 | constexpr Fp operator+(Fp rhs) const { return Fp(add(val, rhs.val), true); } 110 | 111 | constexpr Fp operator-() const { return Fp(sub(0, val), true); } 112 | 113 | constexpr Fp operator-(Fp rhs) const { return Fp(sub(val, rhs.val), true); } 114 | 115 | constexpr Fp operator*(Fp rhs) const { return Fp(mul(val, rhs.val), true); } 116 | 117 | constexpr Fp operator+(Fp rhs) device const { return Fp(add(val, rhs.val), true); } 118 | 119 | constexpr Fp operator-() device const { return Fp(sub(0, val), true); } 120 | 121 | constexpr Fp operator-(Fp rhs) device const { return Fp(sub(val, rhs.val), true); } 122 | 123 | constexpr Fp operator*(Fp rhs) device const { return Fp(mul(val, rhs.val), true); } 124 | 125 | constexpr Fp operator+=(Fp rhs) { 126 | val = add(val, rhs.val); 127 | return *this; 128 | } 129 | 130 | constexpr Fp operator+=(Fp rhs) device { 131 | val = add(val, rhs.val); 132 | return *this; 133 | } 134 | 135 | constexpr Fp operator-=(Fp rhs) { 136 | val = sub(val, rhs.val); 137 | return *this; 138 | } 139 | 140 | constexpr Fp operator*=(Fp rhs) { 141 | val = mul(val, rhs.val); 142 | return *this; 143 | } 144 | 145 | constexpr bool operator==(Fp rhs) const { return val == rhs.val; } 146 | 147 | constexpr bool operator!=(Fp rhs) const { return val != rhs.val; } 148 | 149 | constexpr bool operator<(Fp rhs) const { return decode(val) < decode(rhs.val); } 150 | 151 | constexpr bool operator<=(Fp rhs) const { return decode(val) <= decode(rhs.val); } 152 | 153 | constexpr bool operator>(Fp rhs) const { return decode(val) > decode(rhs.val); } 154 | 155 | constexpr bool operator>=(Fp rhs) const { return decode(val) >= decode(rhs.val); } 156 | 157 | constexpr bool operator==(Fp rhs) device const { return val == rhs.val; } 158 | 159 | constexpr bool operator!=(Fp rhs) device const { return val != rhs.val; } 160 | 161 | constexpr bool operator<(Fp rhs) device const { return decode(val) < decode(rhs.val); } 162 | 163 | constexpr bool operator<=(Fp rhs) device const { return decode(val) <= decode(rhs.val); } 164 | 165 | constexpr bool operator>(Fp rhs) device const { return decode(val) > decode(rhs.val); } 166 | 167 | constexpr bool operator>=(Fp rhs) device const { return decode(val) >= decode(rhs.val); } 168 | 169 | // Post-inc/dec 170 | constexpr Fp operator++(int) { 171 | Fp r = *this; 172 | val = add(val, encode(1)); 173 | return r; 174 | } 175 | 176 | constexpr Fp operator--(int) { 177 | Fp r = *this; 178 | val = sub(val, encode(1)); 179 | return r; 180 | } 181 | 182 | // Pre-inc/dec 183 | constexpr Fp operator++() { 184 | val = add(val, encode(1)); 185 | return *this; 186 | } 187 | 188 | constexpr Fp operator--() { 189 | val = sub(val, encode(1)); 190 | return *this; 191 | } 192 | }; 193 | 194 | /// Raise an value to a power 195 | constexpr inline Fp pow(Fp x, size_t n) { 196 | Fp tot = 1; 197 | while (n != 0) { 198 | if (n % 2 == 1) { 199 | tot *= x; 200 | } 201 | n = n / 2; 202 | x *= x; 203 | } 204 | return tot; 205 | } 206 | 207 | /// Compute the multiplicative inverse of x, or `1/x` in finite field terms. Since `x^(P-1) == 1 208 | /// (mod P)` for any x != 0 (as a consequence of Fermat's little therorm), it follows that `x * 209 | /// x^(P-2) == 1 (mod P)` for x != 0. That is, `x^(P-2)` is the multiplicative inverse of x. 210 | /// Computed this way, the 'inverse' of zero comes out as zero, which is convient in many cases, so 211 | /// we leave it. 212 | constexpr inline Fp inv(Fp x) { 213 | return pow(x, Fp::P - 2); 214 | } 215 | -------------------------------------------------------------------------------- /risc0/build_kernel/kernels/metal/fp4.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 RISC Zero, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #pragma once 16 | 17 | /// \file 18 | /// Defines Fp4, a finite field F_p^4, based on Fp via the irreducable polynomial x^4 - 11. 19 | 20 | #include 21 | 22 | #include "fp.h" 23 | 24 | // Defines instead of constexpr to appease CUDAs limitations around constants. 25 | // undef'd at the end of this file. 26 | #define BETA Fp(11) 27 | #define NBETA Fp(Fp::P - 11) 28 | 29 | /// Intstances of Fp4 are element of a finite field F_p^4. They are represented as elements of 30 | /// F_p[X] / (X^4 - 11). Basically, this is a 'big' finite field (about 2^128 elements), which is 31 | /// used when the security of various operations depends on the size of the field. It has the field 32 | /// Fp as a subfield, which means operations by the two are compatable, which is important. The 33 | /// irreducible polynomial was choosen to be the simpilest possible one, x^4 - B, where 11 is the 34 | /// smallest B which makes the polynomial irreducable. 35 | struct Fp4 { 36 | /// The elements of Fp4, elems[0] + elems[1]*X + elems[2]*X^2 + elems[3]*x^4 37 | Fp elems[4]; 38 | 39 | /// Default constructor makes the zero elements 40 | constexpr Fp4() {} 41 | 42 | /// Initialize from uint32_t 43 | explicit constexpr Fp4(uint32_t x) { 44 | elems[0] = x; 45 | elems[1] = 0; 46 | elems[2] = 0; 47 | elems[3] = 0; 48 | } 49 | 50 | /// Convert from Fp to Fp4. 51 | explicit constexpr Fp4(Fp x) { 52 | elems[0] = x; 53 | elems[1] = 0; 54 | elems[2] = 0; 55 | elems[3] = 0; 56 | } 57 | 58 | /// Explicitly construct an Fp4 from parts 59 | constexpr Fp4(Fp a, Fp b, Fp c, Fp d) { 60 | elems[0] = a; 61 | elems[1] = b; 62 | elems[2] = c; 63 | elems[3] = d; 64 | } 65 | 66 | // Implement the addition/subtraction overloads 67 | constexpr Fp4 operator+=(Fp4 rhs) { 68 | for (uint32_t i = 0; i < 4; i++) { 69 | elems[i] += rhs.elems[i]; 70 | } 71 | return *this; 72 | } 73 | 74 | constexpr Fp4 operator-=(Fp4 rhs) { 75 | for (uint32_t i = 0; i < 4; i++) { 76 | elems[i] -= rhs.elems[i]; 77 | } 78 | return *this; 79 | } 80 | 81 | constexpr Fp4 operator+(Fp4 rhs) const { 82 | Fp4 result = *this; 83 | result += rhs; 84 | return result; 85 | } 86 | 87 | constexpr Fp4 operator-(Fp4 rhs) const { 88 | Fp4 result = *this; 89 | result -= rhs; 90 | return result; 91 | } 92 | 93 | constexpr Fp4 operator-() const { return Fp4() - *this; } 94 | 95 | // Implement the simple multiplication case by the subfield Fp 96 | // Fp * Fp4 is done as a free function due to C++'s operator overloading rules. 97 | constexpr Fp4 operator*=(Fp rhs) { 98 | for (uint32_t i = 0; i < 4; i++) { 99 | elems[i] *= rhs; 100 | } 101 | return *this; 102 | } 103 | 104 | constexpr Fp4 operator*(Fp rhs) const { 105 | Fp4 result = *this; 106 | result *= rhs; 107 | return result; 108 | } 109 | 110 | // Now we get to the interesting case of multiplication. Basically, multiply out the polynomial 111 | // representations, and then reduce module x^4 - B, which means powers >= 4 get shifted back 4 and 112 | // multiplied by -beta. We could write this as a double loops with some if's and hope it gets 113 | // unrolled properly, but it'a small enough to just hand write. 114 | constexpr Fp4 operator*(Fp4 rhs) const { 115 | // Rename the element arrays to something small for readability 116 | #define a elems 117 | #define b rhs.elems 118 | return Fp4(a[0] * b[0] + NBETA * (a[1] * b[3] + a[2] * b[2] + a[3] * b[1]), 119 | a[0] * b[1] + a[1] * b[0] + NBETA * (a[2] * b[3] + a[3] * b[2]), 120 | a[0] * b[2] + a[1] * b[1] + a[2] * b[0] + NBETA * (a[3] * b[3]), 121 | a[0] * b[3] + a[1] * b[2] + a[2] * b[1] + a[3] * b[0]); 122 | #undef a 123 | #undef b 124 | } 125 | constexpr Fp4 operator*=(Fp4 rhs) { 126 | *this = *this * rhs; 127 | return *this; 128 | } 129 | 130 | // Equality 131 | constexpr bool operator==(Fp4 rhs) const { 132 | for (uint32_t i = 0; i < 4; i++) { 133 | if (elems[i] != rhs.elems[i]) { 134 | return false; 135 | } 136 | } 137 | return true; 138 | } 139 | 140 | constexpr bool operator!=(Fp4 rhs) const { 141 | return !(*this == rhs); 142 | } 143 | 144 | constexpr Fp constPart() const { 145 | return elems[0]; 146 | } 147 | 148 | constexpr Fp4 operator+=(Fp4 rhs) device { 149 | for (uint32_t i = 0; i < 4; i++) { 150 | elems[i] += rhs.elems[i]; 151 | } 152 | return *this; 153 | } 154 | 155 | constexpr Fp4 operator+(Fp4 rhs) device const { 156 | return Fp4(*this) + rhs; 157 | } 158 | 159 | constexpr Fp4 operator-(Fp4 rhs) device const { 160 | return Fp4(*this) - rhs; 161 | } 162 | 163 | constexpr Fp4 operator-() device const { 164 | return -Fp4(*this); 165 | } 166 | 167 | constexpr Fp4 operator*(Fp4 rhs) device const { 168 | return Fp4(*this) * rhs; 169 | } 170 | 171 | constexpr Fp4 operator*(Fp rhs) device const { 172 | return Fp4(*this) * rhs; 173 | } 174 | 175 | constexpr bool operator==(Fp4 rhs) device const { 176 | return Fp4(*this) == rhs; 177 | } 178 | 179 | constexpr bool operator!=(Fp4 rhs) device const { 180 | return Fp4(*this) != rhs; 181 | } 182 | 183 | constexpr Fp constPart() device const { 184 | return Fp4(*this).constPart(); 185 | } 186 | }; 187 | 188 | /// Overload for case where LHS is Fp (RHS case is handled as a method) 189 | constexpr inline Fp4 operator*(Fp a, Fp4 b) { 190 | return b * a; 191 | } 192 | 193 | /// Raise an Fp4 to a power 194 | constexpr inline Fp4 pow(Fp4 x, size_t n) { 195 | Fp4 tot(1); 196 | while (n != 0) { 197 | if (n % 2 == 1) { 198 | tot *= x; 199 | } 200 | n = n / 2; 201 | x *= x; 202 | } 203 | return tot; 204 | } 205 | 206 | /// Compute the multiplicative inverse of an Fp4. 207 | constexpr inline Fp4 inv(Fp4 in) { 208 | #define a in.elems 209 | // Compute the multiplicative inverse by basicly looking at Fp4 as a composite field and using the 210 | // same basic methods used to invert complex numbers. We imagine that initially we have a 211 | // numerator of 1, and an denominator of a. i.e out = 1 / a; We set a' to be a with the first and 212 | // third components negated. We then multiply the numerator and the denominator by a', producing 213 | // out = a' / (a * a'). By construction (a * a') has 0's in it's first and third elements. We 214 | // call this number, 'b' and compute it as follows. 215 | Fp b0 = a[0] * a[0] + BETA * (a[1] * (a[3] + a[3]) - a[2] * a[2]); 216 | Fp b2 = a[0] * (a[2] + a[2]) - a[1] * a[1] + BETA * (a[3] * a[3]); 217 | // Now, we make b' by inverting b2. When we muliply both sizes by b', we get out = (a' * b') / 218 | // (b * b'). But by construcion b * b' is in fact an element of Fp, call it c. 219 | Fp c = b0 * b0 + BETA * b2 * b2; 220 | // But we can now invert C direcly, and multiply by a'*b', out = a'*b'*inv(c) 221 | Fp ic = inv(c); 222 | // Note: if c == 0 (really should only happen if in == 0), our 'safe' version of inverse results 223 | // in ic == 0, and thus out = 0, so we have the same 'safe' behavior for Fp4. Oh, and since we 224 | // want to multiply everything by ic, it's slightly faster to premultiply the two parts of b by ic 225 | // (2 multiplies instead of 4) 226 | b0 *= ic; 227 | b2 *= ic; 228 | return Fp4(a[0] * b0 + BETA * a[2] * b2, 229 | -a[1] * b0 + NBETA * a[3] * b2, 230 | -a[0] * b2 + a[2] * b0, 231 | a[1] * b2 - a[3] * b0); 232 | #undef a 233 | } 234 | 235 | #undef BETA 236 | #undef NBETA 237 | -------------------------------------------------------------------------------- /risc0/cargo-risczero/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cargo-risczero" 3 | version = { workspace = true } 4 | edition = { workspace = true } 5 | license = { workspace = true } 6 | homepage = { workspace = true } 7 | repository = { workspace = true } 8 | description = "RISC Zero CLI tools" 9 | readme = "README.md" 10 | keywords = ["risc0", "risczero", "tool", "cli", "generate"] 11 | 12 | [dependencies] 13 | # Note, due to tempfile = 3.3.0 in cargo-generate 14 | # we have to downgrade all uses of tempfile to 3.3 in our workspace due to: 15 | # https://github.com/rust-lang/cargo/issues/7880 16 | cargo-generate = "0.18" 17 | clap = { version = "4.0", features = ["derive"] } 18 | const_format = "0.2" 19 | tracing = { version = "0.1", default-features = false } 20 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 21 | 22 | [dev-dependencies] 23 | tempfile = "3.3" 24 | 25 | [[bin]] 26 | path = "src/bin/main.rs" 27 | name = "cargo-risczero" 28 | -------------------------------------------------------------------------------- /risc0/cargo-risczero/README.md: -------------------------------------------------------------------------------- 1 | # Cargo-risczero 2 | 3 | Cargo extension to help create, manage, and test [RISC Zero](https://github.com/risc0/risc0) projects. 4 | 5 | ## Install 6 | 7 | ```bash 8 | ## Install from crates.io 9 | cargo install cargo-risczero 10 | ## Installing from local source 11 | cargo install --path risc0/cargo-risczero 12 | ``` 13 | 14 | ## New 15 | 16 | The `new` command will create a new project for an existing template. It defaults to the template at [rust-starter](https://github.com/risc0/risc0/tree/main/templates/rust-starter) but can be used with other templates locally or hosted on github. 17 | 18 | ### Examples 19 | 20 | ```bash 21 | ## Create a project from the main template 22 | cargo risczero new my_project 23 | 24 | ## Create a project with 'std' support in the guest 25 | cargo risczero new my_project --std 26 | 27 | ## Disable git initialization 28 | cargo risczero new my_project --no-git 29 | 30 | ## Create from github template 31 | cargo risczero new my_project --template https://github.com/risc0/risc0-rust-starter 32 | ``` -------------------------------------------------------------------------------- /risc0/cargo-risczero/src/bin/main.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 RISC Zero, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use cargo_risczero::{Cargo, RisczeroCmd}; 16 | use clap::Parser; 17 | use tracing_subscriber::EnvFilter; 18 | 19 | fn main() { 20 | tracing_subscriber::fmt() 21 | .with_env_filter(EnvFilter::from_default_env()) 22 | .init(); 23 | 24 | let Cargo::Risczero(args) = Cargo::parse(); 25 | 26 | match args.command { 27 | RisczeroCmd::New(new) => { 28 | new.run(); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /risc0/cargo-risczero/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 RISC Zero, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #![doc = include_str!("../README.md")] 16 | #![deny(missing_docs)] 17 | 18 | use clap::{Parser, Subcommand}; 19 | 20 | use crate::commands::new::NewCommand; 21 | 22 | /// Implementations of the commands 23 | pub mod commands { 24 | /// Create a new RISC Zero project 25 | pub mod new; 26 | } 27 | 28 | #[derive(Parser)] 29 | #[command(name = "cargo", bin_name = "cargo")] 30 | /// Main cargo command 31 | pub enum Cargo { 32 | /// The `risczero` command 33 | Risczero(Risczero), 34 | } 35 | 36 | #[derive(clap::Args)] 37 | #[command(author, version, about, long_about = None)] 38 | /// `cargo risczero` 39 | pub struct Risczero { 40 | #[clap(subcommand)] 41 | /// Which `risczero` command to run 42 | pub command: RisczeroCmd, 43 | } 44 | 45 | #[derive(Subcommand)] 46 | /// Primary commands of `cargo risczero`. 47 | pub enum RisczeroCmd { 48 | /// Creates a new risczero starter project. 49 | New(NewCommand), 50 | } 51 | 52 | #[cfg(test)] 53 | mod tests { 54 | use clap::CommandFactory; 55 | 56 | use super::*; 57 | 58 | #[test] 59 | fn verify_app() { 60 | Cargo::command().debug_assert(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /risc0/core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "risc0-core" 3 | description = "Core types for RISC Zero crates" 4 | version = { workspace = true } 5 | edition = { workspace = true } 6 | license = { workspace = true } 7 | homepage = { workspace = true } 8 | repository = { workspace = true } 9 | 10 | [dependencies] 11 | bytemuck = { version = "1.12", features = ["derive"] } 12 | rand_core = "0.6" 13 | 14 | [dev-dependencies] 15 | rand = { version = "0.8", features = ["small_rng"] } 16 | 17 | [features] 18 | std = [] 19 | -------------------------------------------------------------------------------- /risc0/core/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 RISC Zero, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Core RISC Zero functionality shared across multiple crates 16 | //! 17 | //! This crate contains fundamental code that multiple RISC Zero crates rely on, 18 | //! most notably representing finite field elements and enabling finite field 19 | //! arithmetic. 20 | 21 | #![cfg_attr(not(feature = "std"), no_std)] 22 | #![deny(missing_docs)] 23 | 24 | extern crate alloc; 25 | 26 | pub mod field; 27 | -------------------------------------------------------------------------------- /risc0/r0vm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "risc0-r0vm" 3 | description = "RISC Zero zero-knowledge VM executable" 4 | version = { workspace = true } 5 | edition = { workspace = true } 6 | license = { workspace = true } 7 | homepage = { workspace = true } 8 | repository = { workspace = true } 9 | 10 | [dependencies] 11 | anyhow = "1.0" 12 | bytemuck = "1.12" 13 | clap = { version = "4.0", features = ["derive"] } 14 | env_logger = "0.10" 15 | risc0-zkvm = { workspace = true, features = ["default", "profiler", "binfmt"] } 16 | 17 | [dev-dependencies] 18 | anyhow = "1.0" 19 | assert_cmd = "2.0" 20 | assert_fs = "1.0" 21 | risc0-zkvm-platform = { workspace = true } 22 | 23 | [features] 24 | default = [] 25 | -------------------------------------------------------------------------------- /risc0/r0vm/src/bin/r0vm.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 RISC Zero, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use std::{array, fs, path::PathBuf}; 16 | 17 | use clap::Parser; 18 | use risc0_zkvm::{Executor, ExecutorEnv}; 19 | 20 | /// Runs a RISC-V ELF binary within the RISC Zero ZKVM. 21 | #[derive(Parser)] 22 | #[clap(about, version, author)] 23 | struct Args { 24 | /// The ELF file to run 25 | #[clap(long)] 26 | elf: PathBuf, 27 | 28 | /// The ELF file to run 29 | #[clap(long)] 30 | memory_data: Option, 31 | 32 | /// Receipt output file. 33 | #[clap(long)] 34 | receipt: Option, 35 | 36 | /// File to read initial input from. 37 | #[clap(long)] 38 | initial_input: Option, 39 | 40 | /// Display verbose output. 41 | #[clap(short, long, action = clap::ArgAction::Count)] 42 | verbose: u8, 43 | 44 | /// Add environment vairables in the form of NAME=value. 45 | #[clap(long, action = clap::ArgAction::Append)] 46 | env: Vec, 47 | } 48 | 49 | fn main() { 50 | env_logger::init(); 51 | 52 | let args = Args::parse(); 53 | let elf_contents = fs::read(&args.elf).unwrap(); 54 | let memory_data = args.memory_data.map(|path| fs::read(&path).unwrap()); 55 | 56 | if args.verbose > 0 { 57 | eprintln!( 58 | "Read {} bytes of ELF from {}", 59 | elf_contents.len(), 60 | args.elf.display() 61 | ); 62 | } 63 | 64 | let mut builder = ExecutorEnv::builder(); 65 | 66 | for var in args.env.iter() { 67 | let (name, value) = var 68 | .split_once('=') 69 | .expect("Environment variables should be of the form NAME=value"); 70 | builder.env_var(name, value); 71 | } 72 | 73 | if let Some(input) = args.initial_input.as_ref() { 74 | builder.stdin(fs::File::open(input).unwrap()); 75 | } 76 | 77 | let env = builder.build(); 78 | let mut exec = Executor::from_elf(env, &elf_contents, memory_data).unwrap(); 79 | let session = match exec.run() { 80 | Ok(session) => session, 81 | Err(err) => { 82 | println!("failed at PC value {:08x}", exec.pc); 83 | let regs: [u64; 32] = exec.monitor.load_registers(array::from_fn(|idx| idx)); 84 | regs.iter().enumerate().for_each(|(idx, value)| { 85 | println!("value loaded {:08x}, idx: {:?}", value, idx,); 86 | }); 87 | panic!("error {:?}", err) 88 | } 89 | }; 90 | 91 | // let receipt = session.prove(hal.as_ref(), &eval).unwrap(); 92 | 93 | // let receipt_data = encode_receipt(&receipt); 94 | // if let Some(receipt_file) = args.receipt.as_ref() { 95 | // fs::write(receipt_file, receipt_data.as_slice()).expect("Unable to 96 | // write receipt file"); if args.verbose > 0 { 97 | // eprintln!( 98 | // "Wrote {} bytes of receipt to {}", 99 | // receipt_data.len(), 100 | // receipt_file.display() 101 | // ); 102 | // } 103 | // } 104 | } 105 | -------------------------------------------------------------------------------- /risc0/sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "risc0-sys" 3 | description = "Generated / Native / HAL code for RISC Zero" 4 | version = { workspace = true } 5 | edition = { workspace = true } 6 | license = { workspace = true } 7 | homepage = { workspace = true } 8 | repository = { workspace = true } 9 | links = "risc0-sys" 10 | 11 | [dependencies] 12 | risc0-core = { workspace = true } 13 | 14 | [build-dependencies] 15 | cc = { version = "1.0", features = ["parallel"] } 16 | glob = "0.3" 17 | risc0-build-kernel = { workspace = true } 18 | 19 | [features] 20 | default = [] 21 | cuda = [] 22 | metal = [] 23 | -------------------------------------------------------------------------------- /risc0/sys/build.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 RISC Zero, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use std::{env, path::Path}; 16 | 17 | use risc0_build_kernel::{KernelBuild, KernelType}; 18 | 19 | fn main() { 20 | if env::var("CARGO_FEATURE_CUDA").is_ok() { 21 | build_cuda_kernels(); 22 | } 23 | 24 | if env::var("CARGO_FEATURE_METAL").is_ok() { 25 | build_metal_kernels(); 26 | } 27 | } 28 | 29 | fn build_cuda_kernels() { 30 | const CUDA_KERNELS: &[(&str, &str, &[&str])] = &[( 31 | "zkp", 32 | "all.cu", 33 | &[ 34 | "eltwise.cu", 35 | "fri.cu", 36 | "mix.cu", 37 | "ntt.cu", 38 | "poseidon.cu", 39 | "sha.cu", 40 | "zk.cu", 41 | "sha256.h", 42 | ], 43 | )]; 44 | 45 | let inc_path = Path::new("kernels/zkp/cuda"); 46 | for (name, src, deps) in CUDA_KERNELS { 47 | let dir = Path::new("kernels").join(name).join("cuda"); 48 | let dep_paths = deps.iter().map(|x| dir.join(x)); 49 | let out = format!("cuda_kernels_{name}"); 50 | KernelBuild::new(KernelType::Cuda) 51 | .file(dir.join(src)) 52 | .include(&inc_path) 53 | .deps(dep_paths) 54 | .compile(&out); 55 | } 56 | } 57 | 58 | fn build_metal_kernels() { 59 | const METAL_KERNELS: &[(&str, &[&str])] = &[( 60 | "zkp", 61 | &[ 62 | "eltwise.metal", 63 | "fri.metal", 64 | "mix.metal", 65 | "ntt.metal", 66 | "poseidon.metal", 67 | "sha.metal", 68 | "zk.metal", 69 | ], 70 | )]; 71 | 72 | let inc_path = Path::new("kernels/zkp/metal"); 73 | for (name, srcs) in METAL_KERNELS { 74 | let dir = Path::new("kernels").join(name).join("metal"); 75 | let src_paths = srcs.iter().map(|x| dir.join(x)); 76 | let out = format!("metal_kernels_{name}"); 77 | KernelBuild::new(KernelType::Metal) 78 | .files(src_paths) 79 | .include(&inc_path) 80 | .dep(inc_path.join("sha256.h")) 81 | .compile(&out); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /risc0/sys/kernels/zkp/cuda/all.cu: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Risc0, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "eltwise.cu" 16 | #include "fri.cu" 17 | #include "mix.cu" 18 | #include "ntt.cu" 19 | #include "poseidon.cu" 20 | #include "sha.cu" 21 | #include "zk.cu" 22 | -------------------------------------------------------------------------------- /risc0/sys/kernels/zkp/cuda/eltwise.cu: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Risc0, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "fp.h" 16 | #include "fp4.h" 17 | 18 | extern "C" __global__ 19 | void eltwise_add_fp(Fp* out, 20 | const Fp* x, 21 | const Fp* y, 22 | const uint32_t count) { 23 | uint idx = blockIdx.x * blockDim.x + threadIdx.x; 24 | if (idx < count) { 25 | out[idx] = x[idx] + y[idx]; 26 | } 27 | } 28 | 29 | extern "C" __global__ 30 | void eltwise_mul_factor_fp(Fp* io, 31 | const Fp& factor, 32 | const uint32_t count) { 33 | uint idx = blockIdx.x * blockDim.x + threadIdx.x; 34 | if (idx < count) { 35 | io[idx] = io[idx] * factor; 36 | } 37 | } 38 | 39 | extern "C" __global__ 40 | void eltwise_copy_fp(Fp* out, 41 | const Fp* in, 42 | const uint32_t count) { 43 | uint idx = blockIdx.x * blockDim.x + threadIdx.x; 44 | // If the following check is not included, there is a SIGABRT that causes tests to fail 45 | // cuda-memcheck also throws lots of out of bounds read errors if this check is omitted 46 | if (idx < count) { 47 | out[idx] = in[idx]; 48 | } 49 | } 50 | 51 | extern "C" __global__ 52 | void eltwise_sum_fp4(Fp* out, 53 | const Fp4* in, 54 | const uint32_t to_add, 55 | const uint32_t count) { 56 | uint idx = blockIdx.x * blockDim.x + threadIdx.x; 57 | if (idx < count) { 58 | Fp4 tot; 59 | for (size_t i = 0; i < to_add; i++) { 60 | tot += in[count * i + idx]; 61 | } 62 | out[idx + 0 * count] = tot.elems[0]; 63 | out[idx + 1 * count] = tot.elems[1]; 64 | out[idx + 2 * count] = tot.elems[2]; 65 | out[idx + 3 * count] = tot.elems[3]; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /risc0/sys/kernels/zkp/cuda/fri.cu: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Risc0, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "fp.h" 16 | #include "fp4.h" 17 | 18 | constexpr size_t kFriFold = 16; 19 | 20 | /// Compute `ceil(log_2(in))`, i.e. find the smallest value `out` such that `2^out >= in`. 21 | __device__ inline constexpr size_t log2Ceil(size_t in) { 22 | size_t r = 0; 23 | while ((1 << r) < in) { 24 | r++; 25 | } 26 | return r; 27 | } 28 | 29 | extern "C" __global__ 30 | void fri_fold(Fp* out, 31 | const Fp* in, 32 | const Fp4& mix, 33 | const uint32_t count) { 34 | uint idx = blockIdx.x * blockDim.x + threadIdx.x; 35 | if (idx < count) { 36 | Fp4 tot; 37 | Fp4 curMix(1); 38 | for (uint32_t i = 0; i < kFriFold; i++) { 39 | size_t rev_i = __brev(i) >> (32 - log2Ceil(kFriFold)); 40 | size_t rev_idx = rev_i * count + idx; 41 | Fp4 factor(in[0 * count * kFriFold + rev_idx], 42 | in[1 * count * kFriFold + rev_idx], 43 | in[2 * count * kFriFold + rev_idx], 44 | in[3 * count * kFriFold + rev_idx]); 45 | tot += curMix * factor; 46 | curMix *= mix; 47 | } 48 | for (size_t i = 0; i < 4; i++) { 49 | out[count * i + idx] = tot.elems[i]; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /risc0/sys/kernels/zkp/cuda/mix.cu: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Risc0, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "fp.h" 16 | #include "fp4.h" 17 | 18 | extern "C" __global__ 19 | void mix_poly_coeffs(Fp4* out, 20 | const Fp* in, 21 | const uint32_t* combos, 22 | const Fp4& mixStart, 23 | const Fp4& mix, 24 | const uint32_t inputSize, 25 | const uint32_t count) { 26 | uint idx = blockIdx.x * blockDim.x + threadIdx.x; 27 | if (idx < count) { 28 | Fp4 cur = mixStart; 29 | for (size_t i = 0; i < inputSize; i++) { 30 | size_t id = combos[i]; 31 | out[count * id + idx] += cur * in[count * i + idx]; 32 | cur *= mix; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /risc0/sys/kernels/zkp/cuda/ntt.cu: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Risc0, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "fp.h" 16 | #include "fp4.h" 17 | 18 | extern "C" __global__ 19 | void multi_bit_reverse(Fp* io, 20 | const uint32_t nBits, 21 | const uint32_t count) { 22 | uint totIdx = blockIdx.x * blockDim.x + threadIdx.x; 23 | if (totIdx < count) { 24 | uint32_t rowSize = 1 << nBits; 25 | uint32_t idx = totIdx & (rowSize - 1); 26 | uint32_t s = totIdx >> nBits; 27 | uint32_t ridx = __brev(idx) >> (32 - nBits); 28 | if (idx < ridx) { 29 | size_t idx1 = s * rowSize + idx; 30 | size_t idx2 = s * rowSize + ridx; 31 | Fp tmp = io[idx1]; 32 | io[idx1] = io[idx2]; 33 | io[idx2] = tmp; 34 | } 35 | } 36 | } 37 | 38 | extern "C" __global__ 39 | void batch_evaluate_any(Fp4* out, 40 | const Fp* coeffs, 41 | const uint32_t* which, 42 | const Fp4* xs, 43 | const uint32_t eval_count, 44 | const uint32_t count) { 45 | uint idx = blockIdx.x * blockDim.x + threadIdx.x; 46 | if (idx < count) { 47 | Fp4 tot; 48 | Fp4 cur(1); 49 | const uint32_t id = which[idx]; 50 | const Fp4& x = xs[idx]; 51 | for (uint32_t i = 0; i < eval_count; i++) { 52 | tot += cur * coeffs[eval_count * id + i]; 53 | cur *= x; 54 | } 55 | out[idx] = tot; 56 | } 57 | } 58 | 59 | extern "C" __global__ 60 | void batch_expand(Fp* out, 61 | const Fp* in, 62 | const uint32_t polyCount, 63 | const uint32_t outSize, 64 | const uint32_t inSize, 65 | const uint32_t expandBits) { 66 | uint idx = blockIdx.x * blockDim.x + threadIdx.x; 67 | if (idx < outSize) { 68 | for (uint32_t i = 0; i < polyCount; i++) { 69 | out[i * outSize + idx] = in[i * inSize + (idx >> expandBits)]; 70 | } 71 | } 72 | } 73 | 74 | extern "C" __global__ 75 | void multi_ntt_fwd_step(Fp* io, 76 | const Fp* rou, 77 | const uint32_t nBits, 78 | const uint32_t sBits, 79 | const uint32_t cSize) { 80 | uint32_t gSize = 1 << (nBits - sBits); 81 | uint32_t sSize = 1 << (sBits - 1); 82 | uint32_t nSize = 1 << nBits; 83 | uint32_t sOff = threadIdx.x + blockIdx.x * blockDim.x; 84 | uint32_t sStep = blockDim.x * gridDim.x; 85 | uint32_t gOff = threadIdx.y + blockIdx.y * blockDim.y; 86 | uint32_t gStep = blockDim.y * gridDim.y; 87 | uint32_t cOff = threadIdx.z + blockIdx.z * blockDim.z; 88 | uint32_t cStep = blockDim.z * gridDim.z; 89 | 90 | // Compute the intial multiplier for the sOff: pow(rou[s], sOff) 91 | Fp curMul(1); 92 | uint32_t curRou = sBits; 93 | uint32_t powX = sOff; 94 | while (curRou > 0) { 95 | if (powX & 1) { 96 | curMul = curMul * rou[curRou]; 97 | } 98 | powX >>= 1; 99 | curRou--; 100 | } 101 | // Compute the multiplier for each loop around s 102 | int rouStep = __ffs(sSize / sStep); 103 | Fp stepMul = rou[rouStep]; 104 | for (uint32_t s = sOff; s < sSize; s += sStep) { 105 | for (uint32_t g = gOff; g < gSize; g += gStep) { 106 | for (uint32_t c = cOff; c < cSize; c += cStep) { 107 | Fp a = io[c * nSize + g * 2 * sSize + s]; 108 | Fp b = io[c * nSize + g * 2 * sSize + s + sSize]; 109 | b *= curMul; 110 | io[c * nSize + g * 2 * sSize + s] = a + b; 111 | io[c * nSize + g * 2 * sSize + s + sSize] = a - b; 112 | } 113 | } 114 | curMul *= stepMul; 115 | } 116 | } 117 | 118 | extern "C" __global__ 119 | void multi_ntt_rev_step(Fp* io, 120 | const Fp* rou, 121 | const uint32_t nBits, 122 | const uint32_t sBits, 123 | const uint32_t cSize) { 124 | uint32_t gSize = 1 << (nBits - sBits); 125 | uint32_t sSize = 1 << (sBits - 1); 126 | uint32_t nSize = 1 << nBits; 127 | uint32_t sOff = threadIdx.x + blockIdx.x * blockDim.x; 128 | uint32_t sStep = blockDim.x * gridDim.x; 129 | uint32_t gOff = threadIdx.y + blockIdx.y * blockDim.y; 130 | uint32_t gStep = blockDim.y * gridDim.y; 131 | uint32_t cOff = threadIdx.z + blockIdx.z * blockDim.z; 132 | uint32_t cStep = blockDim.z * gridDim.z; 133 | 134 | // Compute the intial multiplier for the sOff: pow(rou[s], sOff) 135 | Fp curMul(1); 136 | uint32_t curRou = sBits; 137 | uint32_t powX = sOff; 138 | while (curRou > 0) { 139 | if (powX & 1) { 140 | curMul = curMul * rou[curRou]; 141 | } 142 | powX >>= 1; 143 | curRou--; 144 | } 145 | // Compute the multiplier for each loop around s 146 | int rouStep = __ffs(sSize / sStep); 147 | Fp stepMul = rou[rouStep]; 148 | for (uint32_t s = sOff; s < sSize; s += sStep) { 149 | for (uint32_t g = gOff; g < gSize; g += gStep) { 150 | for (uint32_t c = cOff; c < cSize; c += cStep) { 151 | Fp a = io[c * nSize + g * 2 * sSize + s]; 152 | Fp b = io[c * nSize + g * 2 * sSize + s + sSize]; 153 | io[c * nSize + g * 2 * sSize + s] = a + b; 154 | io[c * nSize + g * 2 * sSize + s + sSize] = (a - b) * curMul; 155 | } 156 | } 157 | curMul *= stepMul; 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /risc0/sys/kernels/zkp/cuda/poseidon.cu: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Risc0, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "fp.h" 16 | 17 | #define CELLS 24 18 | #define ROUNDS_FULL 8 19 | #define ROUNDS_HALF_FULL (ROUNDS_FULL / 2) 20 | #define ROUNDS_PARTIAL 21 21 | #define ROW_SIZE (CELLS + ROUNDS_PARTIAL) 22 | #define CELLS_RATE 16 23 | #define CELLS_OUT 8 24 | 25 | __device__ void add_round_constants(const Fp* ROUND_CONSTANTS, Fp* cells, uint round) { 26 | for (uint i = 0; i < CELLS; i++) { 27 | cells[i] += ROUND_CONSTANTS[round * CELLS + i]; 28 | } 29 | } 30 | 31 | __device__ Fp sbox(Fp x) { 32 | Fp x2 = x * x; 33 | Fp x4 = x2 * x2; 34 | Fp x6 = x4 * x2; 35 | return x6 * x; 36 | } 37 | 38 | __device__ void do_full_sboxes(Fp* cells) { 39 | for (uint i = 0; i < CELLS; i++) { 40 | cells[i] = sbox(cells[i]); 41 | } 42 | } 43 | 44 | __device__ void multiply_by_mds(const Fp* MDS, Fp* cells) { 45 | Fp new_cells[CELLS]; 46 | for (uint i = 0; i < CELLS; i++) { 47 | Fp tot = 0; 48 | for (uint j = 0; j < CELLS; j++) { 49 | tot += MDS[i * CELLS + j] * cells[j]; 50 | } 51 | new_cells[i] = tot; 52 | } 53 | for (uint i = 0; i < CELLS; i++) { 54 | cells[i] = new_cells[i]; 55 | } 56 | } 57 | 58 | __device__ void full_round(const Fp* ROUND_CONSTANTS, const Fp* MDS, Fp* cells, uint round) { 59 | add_round_constants(ROUND_CONSTANTS, cells, round); 60 | do_full_sboxes(cells); 61 | multiply_by_mds(MDS, cells); 62 | } 63 | 64 | __device__ void poseidon_mix(const Fp* ROUND_CONSTANTS, 65 | const Fp* MDS, 66 | const Fp* PARTIAL_COMP_MATRIX, 67 | const Fp* PARTIAL_COMP_OFFSET, 68 | Fp* cells) { 69 | uint round = 0; 70 | for (uint i = 0; i < ROUNDS_HALF_FULL; i++) { 71 | full_round(ROUND_CONSTANTS, MDS, cells, round); 72 | round++; 73 | } 74 | Fp sboxes[ROUNDS_PARTIAL]; 75 | for (uint i = 0; i < ROUNDS_PARTIAL; i++) { 76 | // For each sbox, compute it's input 77 | Fp sbox_in = PARTIAL_COMP_OFFSET[CELLS + i]; 78 | for (uint j = 0; j < CELLS; j++) { 79 | sbox_in += PARTIAL_COMP_MATRIX[(CELLS + i) * ROW_SIZE + j] * cells[j]; 80 | } 81 | for (uint j = 0; j < i; j++) { 82 | sbox_in += PARTIAL_COMP_MATRIX[(CELLS + i) * ROW_SIZE + CELLS + j] * sboxes[j]; 83 | } 84 | // Run it through the sbox + record it 85 | sboxes[i] = sbox(sbox_in); 86 | } 87 | // Forward output data back to cells 88 | Fp new_cells[CELLS]; 89 | for (uint i = 0; i < CELLS; i++) { 90 | Fp out = PARTIAL_COMP_OFFSET[i]; 91 | for (uint j = 0; j < CELLS; j++) { 92 | out += PARTIAL_COMP_MATRIX[i * ROW_SIZE + j] * cells[j]; 93 | } 94 | for (uint j = 0; j < ROUNDS_PARTIAL; j++) { 95 | out += PARTIAL_COMP_MATRIX[i * ROW_SIZE + CELLS + j] * sboxes[j]; 96 | } 97 | new_cells[i] = out; 98 | } 99 | round += ROUNDS_PARTIAL; 100 | for (uint i = 0; i < CELLS; i++) { 101 | cells[i] = new_cells[i]; 102 | } 103 | for (uint i = 0; i < ROUNDS_HALF_FULL; i++) { 104 | full_round(ROUND_CONSTANTS, MDS, cells, round); 105 | round++; 106 | } 107 | } 108 | 109 | extern "C" __global__ void poseidon_fold(const Fp* ROUND_CONSTANTS, 110 | const Fp* MDS, 111 | const Fp* PARTIAL_COMP_MATRIX, 112 | const Fp* PARTIAL_COMP_OFFSET, 113 | Fp* output, 114 | const Fp* input, 115 | uint32_t output_size) { 116 | uint32_t gid = blockDim.x * blockIdx.x + threadIdx.x; 117 | if (gid >= output_size) { return; } 118 | Fp cells[CELLS]; 119 | for (size_t i = 0; i < CELLS_OUT; i++) { 120 | cells[i] = input[2 * gid * CELLS_OUT + i]; 121 | cells[CELLS_OUT + i] = input[(2 * gid + 1) * CELLS_OUT + i]; 122 | } 123 | poseidon_mix(ROUND_CONSTANTS, MDS, PARTIAL_COMP_MATRIX, PARTIAL_COMP_OFFSET, cells); 124 | for (uint i = 0; i < CELLS_OUT; i++) { 125 | output[gid * CELLS_OUT + i] = cells[i]; 126 | } 127 | } 128 | 129 | extern "C" __global__ void poseidon_rows(const Fp* ROUND_CONSTANTS, 130 | const Fp* MDS, 131 | const Fp* PARTIAL_COMP_MATRIX, 132 | const Fp* PARTIAL_COMP_OFFSET, 133 | Fp* out, 134 | const Fp* matrix, 135 | uint32_t count, 136 | uint32_t col_size) { 137 | uint32_t gid = blockDim.x * blockIdx.x + threadIdx.x; 138 | if (gid >= count) { return; } 139 | Fp cells[CELLS]; 140 | uint used = 0; 141 | for (uint i = 0; i < col_size; i++) { 142 | cells[used++] += matrix[i * count + gid]; 143 | if (used == CELLS_RATE) { 144 | poseidon_mix(ROUND_CONSTANTS, MDS, PARTIAL_COMP_MATRIX, PARTIAL_COMP_OFFSET, cells); 145 | used = 0; 146 | } 147 | } 148 | if (used != 0 || count == 0) { 149 | poseidon_mix(ROUND_CONSTANTS, MDS, PARTIAL_COMP_MATRIX, PARTIAL_COMP_OFFSET, cells); 150 | } 151 | for (uint i = 0; i < CELLS_OUT; i++) { 152 | out[CELLS_OUT * gid + i] = cells[i]; 153 | } 154 | } 155 | 156 | -------------------------------------------------------------------------------- /risc0/sys/kernels/zkp/cuda/sha.cu: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Risc0, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "sha256.h" 16 | 17 | extern "C" __global__ 18 | void sha_rows(ShaDigest* out, 19 | const Fp* matrix, 20 | uint32_t count, 21 | uint32_t colSize) { 22 | uint32_t idx = blockDim.x * blockIdx.x + threadIdx.x; 23 | if (idx < count) { 24 | out[idx] = shaHash(matrix + idx, colSize, count, false); 25 | } 26 | } 27 | 28 | extern "C" __global__ 29 | void sha_fold(ShaDigest* out, 30 | const ShaDigest* in, 31 | uint32_t count) { 32 | uint32_t idx = blockDim.x * blockIdx.x + threadIdx.x; 33 | if (idx < count) { 34 | out[idx] = shaHashPair(in[2 * idx], in[2 * idx + 1]); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /risc0/sys/kernels/zkp/cuda/sha256.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 RISC Zero, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #pragma once 16 | 17 | #include "fp.h" 18 | 19 | // A internal implementation of SHA-256 (which is the only one we use). It's very basic, but I 20 | // would like to avoid dependencies on third party libraries, and in some places we need direct 21 | // access to the compression function, for example, when doing unpadded merkle tree rollups. 22 | // Additionally it's useful for the GPU acceleration. 23 | // 24 | // The core compression function is a modification (for style, GPU, and c++isms), based on the code 25 | // at: https://github.com/B-Con/crypto-algorithms/blob/master/sha256.c which is a public domain 26 | // implementation by Brad Conte (brad@bradconte.com) 27 | // 28 | // We avoid endian conversions by usually keeping the form of uint32_t values. We do 29 | // support hashing of byte arrays including endian conversion and padding, but we generally avoid it 30 | // when possible. 31 | 32 | // A digest (still in uint32_t parts for easy rolling up in merkle trees). 33 | struct ShaDigest { 34 | uint32_t words[8]; 35 | 36 | // The 'zero' digest, sort of the nullptr of digests. 37 | __device__ static ShaDigest zero() { return {{0, 0, 0, 0, 0, 0, 0, 0}}; } 38 | 39 | __device__ int cmp(ShaDigest rhs) const { 40 | for (size_t i = 0; i < 8; i++) { 41 | if (words[i] != rhs.words[i]) { 42 | return words[i] < rhs.words[i] ? -1 : 1; 43 | } 44 | } 45 | return 0; 46 | } 47 | 48 | __device__ bool operator==(ShaDigest rhs) const { return cmp(rhs) == 0; } 49 | 50 | __device__ bool operator!=(ShaDigest rhs) const { return cmp(rhs) != 0; } 51 | }; 52 | 53 | // Namespace to hide away some of the details from the user. 54 | namespace impl { 55 | 56 | // Generate the initialization for hash state. 57 | __device__ inline ShaDigest initState() { 58 | return {{0x6a09e667, 59 | 0xbb67ae85, 60 | 0x3c6ef372, 61 | 0xa54ff53a, 62 | 0x510e527f, 63 | 0x9b05688c, 64 | 0x1f83d9ab, 65 | 0x5be0cd19}}; 66 | } 67 | 68 | // Internal compression function, presumes chunk of 16 elements) 69 | __device__ inline void compress(ShaDigest& state, const uint32_t* chunk) { 70 | uint32_t roundK[64] = {0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 71 | 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 72 | 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 73 | 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 74 | 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 75 | 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 76 | 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 77 | 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 78 | 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 79 | 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 80 | 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2}; 81 | 82 | #define ROTLEFT(a, b) (((a) << (b)) | ((a) >> (32 - (b)))) 83 | #define ROTRIGHT(a, b) (((a) >> (b)) | ((a) << (32 - (b)))) 84 | #define CH(x, y, z) (((x) & (y)) ^ (~(x) & (z))) 85 | #define MAJ(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) 86 | #define EP0(x) (ROTRIGHT(x, 2) ^ ROTRIGHT(x, 13) ^ ROTRIGHT(x, 22)) 87 | #define EP1(x) (ROTRIGHT(x, 6) ^ ROTRIGHT(x, 11) ^ ROTRIGHT(x, 25)) 88 | #define SIG0(x) (ROTRIGHT(x, 7) ^ ROTRIGHT(x, 18) ^ ((x) >> 3)) 89 | #define SIG1(x) (ROTRIGHT(x, 17) ^ ROTRIGHT(x, 19) ^ ((x) >> 10)) 90 | 91 | #define ROUND_FUNC \ 92 | uint32_t t1 = h + EP1(e) + CH(e, f, g) + roundK[i] + w[i]; \ 93 | uint32_t t2 = EP0(a) + MAJ(a, b, c); \ 94 | h = g; \ 95 | g = f; \ 96 | f = e; \ 97 | e = d + t1; \ 98 | d = c; \ 99 | c = b; \ 100 | b = a; \ 101 | a = t1 + t2; 102 | uint32_t w[64]; 103 | 104 | uint32_t a = state.words[0]; 105 | uint32_t b = state.words[1]; 106 | uint32_t c = state.words[2]; 107 | uint32_t d = state.words[3]; 108 | uint32_t e = state.words[4]; 109 | uint32_t f = state.words[5]; 110 | uint32_t g = state.words[6]; 111 | uint32_t h = state.words[7]; 112 | 113 | for (size_t i = 0; i < 16; i++) { 114 | w[i] = chunk[i]; 115 | ROUND_FUNC; 116 | } 117 | for (size_t i = 16; i < 64; i++) { 118 | w[i] = SIG1(w[i - 2]) + w[i - 7] + SIG0(w[i - 15]) + w[i - 16]; 119 | ROUND_FUNC; 120 | } 121 | 122 | state.words[0] += a; 123 | state.words[1] += b; 124 | state.words[2] += c; 125 | state.words[3] += d; 126 | state.words[4] += e; 127 | state.words[5] += f; 128 | state.words[6] += g; 129 | state.words[7] += h; 130 | 131 | #undef ROTLEFT 132 | #undef ROTRIGHT 133 | #undef CH 134 | #undef MAJ 135 | #undef EP0 136 | #undef EP1 137 | #undef SIG0 138 | #undef SIG1 139 | #undef ROUND_FUNC 140 | } 141 | 142 | __device__ inline uint32_t convertU32(uint32_t in) { 143 | uint32_t x0 = in & 0x000000ff; 144 | uint32_t x1 = in & 0x0000ff00; 145 | uint32_t x2 = in & 0x00ff0000; 146 | uint32_t x3 = in & 0xff000000; 147 | return (x0 << 24) | (x1 << 8) | (x2 >> 8) | (x3 >> 24); 148 | } 149 | 150 | __device__ inline uint32_t convertU32(Fp in) { 151 | return convertU32(in.asRaw()); 152 | } 153 | 154 | // Main entry point for uint32_t sized objects 155 | template 156 | __device__ inline ShaDigest shaHashInner(const T* data, size_t size, size_t stride, bool pad) { 157 | // Prepare the inital state 158 | uint32_t words[16]; 159 | uint32_t curWord = 0; 160 | ShaDigest state = initState(); 161 | 162 | // Push all of the values 163 | for (size_t i = 0; i < size; i++) { 164 | words[curWord++] = convertU32(data[i * stride]); 165 | if (curWord == 16) { 166 | compress(state, words); 167 | curWord = 0; 168 | } 169 | } 170 | if (pad) { 171 | // Add padding 172 | words[curWord++] = 0x80000000UL; 173 | } 174 | // Clear rest of the block 175 | for (size_t i = curWord; i < 16; i++) { 176 | words[i] = 0; 177 | } 178 | 179 | // If we can't fit the size in the remaining room, compress + clear 180 | if (pad) { 181 | if (curWord > 14) { 182 | compress(state, words); 183 | for (size_t i = 0; i < 16; i++) { 184 | words[i] = 0; 185 | } 186 | } 187 | // Now add size in bits 188 | uint64_t bitSize = size * uint64_t(32); 189 | words[14] = bitSize >> 32; 190 | words[15] = bitSize & 0xffffffff; 191 | } 192 | // Do final compression 193 | if (pad || curWord != 0) { 194 | compress(state, words); 195 | } 196 | 197 | for (size_t i = 0; i < 8; i++) { 198 | state.words[i] = impl::convertU32(state.words[i]); 199 | } 200 | 201 | return state; 202 | } 203 | 204 | } // namespace impl 205 | 206 | // Main entry points 207 | __device__ inline ShaDigest 208 | shaHash(const uint32_t* data, size_t size, size_t stride = 1, bool pad = true) { 209 | return impl::shaHashInner(data, size, stride, pad); 210 | } 211 | 212 | __device__ inline ShaDigest 213 | shaHash(const Fp* data, size_t size, size_t stride = 1, bool pad = true) { 214 | return impl::shaHashInner(data, size, stride, pad); 215 | } 216 | 217 | __device__ inline ShaDigest shaHashPair(ShaDigest x, ShaDigest y) { 218 | // Copy both hash states into a single buffer 219 | uint32_t words[16]; 220 | for (size_t i = 0; i < 8; i++) { 221 | words[i] = impl::convertU32(x.words[i]); 222 | } 223 | for (size_t i = 0; i < 8; i++) { 224 | words[8 + i] = impl::convertU32(y.words[i]); 225 | } 226 | 227 | // Initialize state + compress 228 | ShaDigest state = impl::initState(); 229 | impl::compress(state, words); 230 | 231 | for (size_t i = 0; i < 8; i++) { 232 | state.words[i] = impl::convertU32(state.words[i]); 233 | } 234 | 235 | // Return the results 236 | return state; 237 | } 238 | -------------------------------------------------------------------------------- /risc0/sys/kernels/zkp/cuda/zk.cu: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Risc0, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "fp.h" 16 | 17 | extern "C" __global__ 18 | void zk_shift(Fp* io, 19 | const uint32_t bits, 20 | const uint32_t count) { 21 | uint idx = blockIdx.x * blockDim.x + threadIdx.x; 22 | if (idx < count) { 23 | uint32_t pos = idx & ((1 << bits) - 1); 24 | uint32_t posRev = __brev(pos) >> (32 - bits); 25 | Fp pow3 = pow(Fp(3), posRev); 26 | io[idx] = io[idx] * pow3; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /risc0/sys/kernels/zkp/metal/eltwise.metal: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Risc0, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "fp.h" 16 | #include "fp4.h" 17 | #include "sha256.h" 18 | 19 | using namespace metal; 20 | 21 | kernel void eltwise_add_fp(device Fp* out, 22 | const device Fp* in1, 23 | const device Fp* in2, 24 | uint gid [[thread_position_in_grid]]) { 25 | out[gid] = in1[gid] + in2[gid]; 26 | } 27 | 28 | kernel void eltwise_mul_factor_fp(device Fp* io, 29 | const device Fp& factor, 30 | uint gid [[thread_position_in_grid]]) { 31 | io[gid] = io[gid] * factor; 32 | } 33 | 34 | 35 | kernel void eltwise_copy_fp(device Fp* out, 36 | const device Fp* in, 37 | uint gid [[thread_position_in_grid]]) { 38 | out[gid] = in[gid]; 39 | } 40 | 41 | kernel void eltwise_sum_fp4(device Fp* out, 42 | const device Fp4* in, 43 | device uint32_t& count, 44 | device uint32_t& to_add, 45 | uint gid [[thread_position_in_grid]]) { 46 | Fp4 tot; 47 | for (size_t i = 0; i < to_add; i++) { 48 | tot += in[count * i + gid]; 49 | } 50 | for (uint32_t i = 0; i < 4; i++) { 51 | out[gid + i * count] = tot.elems[i]; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /risc0/sys/kernels/zkp/metal/fri.metal: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Risc0, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "fp.h" 16 | #include "fp4.h" 17 | 18 | using namespace metal; 19 | 20 | constant size_t kFriFold = 16; 21 | 22 | /// Compute `ceil(log_2(in))`, i.e. find the smallest value `out` such that `2^out >= in`. 23 | inline constexpr size_t log2Ceil(size_t in) { 24 | size_t r = 0; 25 | while ((1 << r) < in) { 26 | r++; 27 | } 28 | return r; 29 | } 30 | 31 | kernel void fri_fold(device Fp* out, 32 | const device Fp* in, 33 | const device Fp4& mix, 34 | const device uint32_t& count, 35 | uint gid [[thread_position_in_grid]]) { 36 | Fp4 tot; 37 | Fp4 curMix(1); 38 | for (uint32_t i = 0; i < kFriFold; i++) { 39 | size_t rev_i = reverse_bits(i) >> (32 - log2Ceil(kFriFold)); 40 | size_t rev_idx = rev_i * count + gid; 41 | Fp4 factor(in[0 * count * kFriFold + rev_idx], 42 | in[1 * count * kFriFold + rev_idx], 43 | in[2 * count * kFriFold + rev_idx], 44 | in[3 * count * kFriFold + rev_idx]); 45 | tot += curMix * factor; 46 | curMix *= mix; 47 | } 48 | for (size_t i = 0; i < 4; i++) { 49 | out[count * i + gid] = tot.elems[i]; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /risc0/sys/kernels/zkp/metal/mix.metal: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Risc0, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "fp.h" 16 | #include "fp4.h" 17 | 18 | using namespace metal; 19 | 20 | kernel void mix_poly_coeffs(device Fp4* out, 21 | const device Fp* in, 22 | const device uint32_t* combos, 23 | const device Fp4& mixStart, 24 | const device Fp4& mix, 25 | const device uint32_t& inputSize, 26 | const device uint32_t& count, 27 | uint gid [[thread_position_in_grid]]) { 28 | Fp4 cur = mixStart; 29 | for (size_t i = 0; i < inputSize; i++) { 30 | size_t id = combos[i]; 31 | out[count * id + gid] += cur * in[count * i + gid]; 32 | cur *= mix; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /risc0/sys/kernels/zkp/metal/ntt.metal: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Risc0, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "fp.h" 16 | #include "fp4.h" 17 | 18 | using namespace metal; 19 | 20 | // Find the first bit that is set. 21 | inline uint32_t accel_ffs(uint32_t x) { return 32 - clz(x & -x); } 22 | 23 | kernel void multi_bit_reverse(device Fp* io, 24 | const device uint32_t& nBits, 25 | uint gid [[thread_position_in_grid]]) { 26 | uint32_t rowSize = 1 << nBits; 27 | uint32_t idx = gid & (rowSize - 1); 28 | uint32_t s = gid >> nBits; 29 | uint32_t ridx = reverse_bits(idx) >> (32 - nBits); 30 | if (idx < ridx) { 31 | size_t idx1 = s * rowSize + idx; 32 | size_t idx2 = s * rowSize + ridx; 33 | Fp tmp = io[idx1]; 34 | io[idx1] = io[idx2]; 35 | io[idx2] = tmp; 36 | } 37 | } 38 | 39 | kernel void multi_poly_eval(device Fp4* out, 40 | const device Fp* coeffs, 41 | const device uint32_t* which, 42 | const device Fp4* xs, 43 | const device uint32_t& deg, 44 | uint3 blockIdx [[threadgroup_position_in_grid]], 45 | uint3 threadIdx [[thread_position_in_threadgroup]]) { 46 | device const Fp* cur_poly = coeffs + which[blockIdx.x] * deg; 47 | Fp4 x = xs[blockIdx.x]; 48 | Fp4 stepx = pow(x, 256); 49 | Fp4 powx = pow(x, threadIdx.x); 50 | Fp4 tot; 51 | for (size_t i = threadIdx.x; i < deg; i += 256) { 52 | tot += powx * cur_poly[i]; 53 | powx *= stepx; 54 | } 55 | threadgroup uint32_t totsBuf[256 * 4]; 56 | threadgroup Fp4* tots = reinterpret_cast(totsBuf); 57 | tots[threadIdx.x] = tot; 58 | threadgroup_barrier(mem_flags::mem_threadgroup); 59 | unsigned cur = 256; 60 | while (cur) { 61 | cur /= 2; 62 | if (threadIdx.x < cur) { 63 | tots[threadIdx.x] = Fp4(tots[threadIdx.x]) + Fp4(tots[threadIdx.x + cur]); 64 | } 65 | threadgroup_barrier(mem_flags::mem_threadgroup); 66 | } 67 | if (threadIdx.x == 0) { 68 | out[blockIdx.x] = tots[0]; 69 | } 70 | } 71 | 72 | kernel void batch_expand(device Fp* out, 73 | const device Fp* in, 74 | const device uint32_t& polyCount, 75 | const device uint32_t& outSize, 76 | const device uint32_t& inSize, 77 | const device uint32_t& expandBits, 78 | uint gid [[thread_position_in_grid]]) { 79 | for (uint32_t i = 0; i < polyCount; i++) { 80 | out[i * outSize + gid] = in[i * inSize + (gid >> expandBits)]; 81 | } 82 | } 83 | 84 | kernel void multi_ntt_fwd_step(device Fp* io, 85 | const device Fp* rou, 86 | const device uint32_t& nBits, 87 | const device uint32_t& sBits, 88 | const device uint32_t& cSize, 89 | uint3 gridDim [[threadgroups_per_grid]], 90 | uint3 blockDim [[threads_per_threadgroup]], 91 | uint3 blockIdx [[threadgroup_position_in_grid]], 92 | uint3 threadIdx [[thread_position_in_threadgroup]]) { 93 | uint32_t gSize = 1 << (nBits - sBits); 94 | uint32_t sSize = 1 << (sBits - 1); 95 | uint32_t nSize = 1 << nBits; 96 | uint32_t sOff = threadIdx.x + blockIdx.x * blockDim.x; 97 | uint32_t sStep = blockDim.x * gridDim.x; 98 | uint32_t gOff = threadIdx.y + blockIdx.y * blockDim.y; 99 | uint32_t gStep = blockDim.y * gridDim.y; 100 | uint32_t cOff = threadIdx.z + blockIdx.z * blockDim.z; 101 | uint32_t cStep = blockDim.z * gridDim.z; 102 | 103 | // Compute the intial multiplier for the sOff: pow(rou[s], sOff) 104 | Fp curMul(1); 105 | uint32_t curRou = sBits; 106 | uint32_t powX = sOff; 107 | while (curRou > 0) { 108 | if (powX & 1) { 109 | curMul = curMul * rou[curRou]; 110 | } 111 | powX >>= 1; 112 | curRou--; 113 | } 114 | // Compute the multiplier for each loop around s 115 | int rouStep = accel_ffs(sSize / sStep); 116 | Fp stepMul = rou[rouStep]; 117 | for (uint32_t s = sOff; s < sSize; s += sStep) { 118 | for (uint32_t g = gOff; g < gSize; g += gStep) { 119 | for (uint32_t c = cOff; c < cSize; c += cStep) { 120 | Fp a = io[c * nSize + g * 2 * sSize + s]; 121 | Fp b = io[c * nSize + g * 2 * sSize + s + sSize]; 122 | b *= curMul; 123 | io[c * nSize + g * 2 * sSize + s] = a + b; 124 | io[c * nSize + g * 2 * sSize + s + sSize] = a - b; 125 | } 126 | } 127 | curMul *= stepMul; 128 | } 129 | } 130 | 131 | kernel void multi_ntt_rev_step(device Fp* io, 132 | const device Fp* rou, 133 | const device uint32_t& nBits, 134 | const device uint32_t& sBits, 135 | const device uint32_t& cSize, 136 | uint3 gridDim [[threadgroups_per_grid]], 137 | uint3 blockDim [[threads_per_threadgroup]], 138 | uint3 blockIdx [[threadgroup_position_in_grid]], 139 | uint3 threadIdx [[thread_position_in_threadgroup]]) { 140 | uint32_t gSize = 1 << (nBits - sBits); 141 | uint32_t sSize = 1 << (sBits - 1); 142 | uint32_t nSize = 1 << nBits; 143 | uint32_t sOff = threadIdx.x + blockIdx.x * blockDim.x; 144 | uint32_t sStep = blockDim.x * gridDim.x; 145 | uint32_t gOff = threadIdx.y + blockIdx.y * blockDim.y; 146 | uint32_t gStep = blockDim.y * gridDim.y; 147 | uint32_t cOff = threadIdx.z + blockIdx.z * blockDim.z; 148 | uint32_t cStep = blockDim.z * gridDim.z; 149 | 150 | // Compute the intial multiplier for the sOff: pow(rou[s], sOff) 151 | Fp curMul(1); 152 | uint32_t curRou = sBits; 153 | uint32_t powX = sOff; 154 | while (curRou > 0) { 155 | if (powX & 1) { 156 | curMul = curMul * rou[curRou]; 157 | } 158 | powX >>= 1; 159 | curRou--; 160 | } 161 | // Compute the multiplier for each loop around s 162 | int rouStep = accel_ffs(sSize / sStep); 163 | Fp stepMul = rou[rouStep]; 164 | for (uint32_t s = sOff; s < sSize; s += sStep) { 165 | for (uint32_t g = gOff; g < gSize; g += gStep) { 166 | for (uint32_t c = cOff; c < cSize; c += cStep) { 167 | Fp a = io[c * nSize + g * 2 * sSize + s]; 168 | Fp b = io[c * nSize + g * 2 * sSize + s + sSize]; 169 | io[c * nSize + g * 2 * sSize + s] = a + b; 170 | io[c * nSize + g * 2 * sSize + s + sSize] = (a - b) * curMul; 171 | } 172 | } 173 | curMul *= stepMul; 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /risc0/sys/kernels/zkp/metal/poseidon.metal: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Risc0, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "fp.h" 16 | #include "sha256.h" 17 | 18 | using namespace metal; 19 | 20 | #define CELLS 24 21 | #define ROUNDS_FULL 8 22 | #define ROUNDS_HALF_FULL (ROUNDS_FULL / 2) 23 | #define ROUNDS_PARTIAL 21 24 | #define ROW_SIZE (CELLS + ROUNDS_PARTIAL) 25 | #define CELLS_RATE 16 26 | #define CELLS_OUT 8 27 | 28 | void add_round_constants(const device Fp* ROUND_CONSTANTS, thread Fp* cells, uint round) { 29 | for (uint i = 0; i < CELLS; i++) { 30 | cells[i] += ROUND_CONSTANTS[round * CELLS + i]; 31 | } 32 | } 33 | 34 | Fp sbox(Fp x) { 35 | Fp x2 = x * x; 36 | Fp x4 = x2 * x2; 37 | Fp x6 = x4 * x2; 38 | return x6 * x; 39 | } 40 | 41 | void do_full_sboxes(thread Fp* cells) { 42 | for (uint i = 0; i < CELLS; i++) { 43 | cells[i] = sbox(cells[i]); 44 | } 45 | } 46 | 47 | void multiply_by_mds(const device Fp* MDS, thread Fp* cells) { 48 | Fp new_cells[CELLS]; 49 | for (uint i = 0; i < CELLS; i++) { 50 | Fp tot = 0; 51 | for (uint j = 0; j < CELLS; j++) { 52 | tot += MDS[i * CELLS + j] * cells[j]; 53 | } 54 | new_cells[i] = tot; 55 | } 56 | for (uint i = 0; i < CELLS; i++) { 57 | cells[i] = new_cells[i]; 58 | } 59 | } 60 | 61 | void full_round(const device Fp* ROUND_CONSTANTS, const device Fp* MDS, thread Fp* cells, uint round) { 62 | add_round_constants(ROUND_CONSTANTS, cells, round); 63 | do_full_sboxes(cells); 64 | multiply_by_mds(MDS, cells); 65 | } 66 | 67 | void poseidon_mix(const device Fp* ROUND_CONSTANTS, 68 | const device Fp* MDS, 69 | const device Fp* PARTIAL_COMP_MATRIX, 70 | const device Fp* PARTIAL_COMP_OFFSET, 71 | thread Fp* cells) { 72 | uint round = 0; 73 | for (uint i = 0; i < ROUNDS_HALF_FULL; i++) { 74 | full_round(ROUND_CONSTANTS, MDS, cells, round); 75 | round++; 76 | } 77 | Fp sboxes[ROUNDS_PARTIAL]; 78 | for (uint i = 0; i < ROUNDS_PARTIAL; i++) { 79 | // For each sbox, compute it's input 80 | Fp sbox_in = PARTIAL_COMP_OFFSET[CELLS + i]; 81 | for (uint j = 0; j < CELLS; j++) { 82 | sbox_in += PARTIAL_COMP_MATRIX[(CELLS + i) * ROW_SIZE + j] * cells[j]; 83 | } 84 | for (uint j = 0; j < i; j++) { 85 | sbox_in += PARTIAL_COMP_MATRIX[(CELLS + i) * ROW_SIZE + CELLS + j] * sboxes[j]; 86 | } 87 | // Run it through the sbox + record it 88 | sboxes[i] = sbox(sbox_in); 89 | } 90 | // Forward output data back to cells 91 | Fp new_cells[CELLS]; 92 | for (uint i = 0; i < CELLS; i++) { 93 | Fp out = PARTIAL_COMP_OFFSET[i]; 94 | for (uint j = 0; j < CELLS; j++) { 95 | out += PARTIAL_COMP_MATRIX[i * ROW_SIZE + j] * cells[j]; 96 | } 97 | for (uint j = 0; j < ROUNDS_PARTIAL; j++) { 98 | out += PARTIAL_COMP_MATRIX[i * ROW_SIZE + CELLS + j] * sboxes[j]; 99 | } 100 | new_cells[i] = out; 101 | } 102 | round += ROUNDS_PARTIAL; 103 | for (uint i = 0; i < CELLS; i++) { 104 | cells[i] = new_cells[i]; 105 | } 106 | for (uint i = 0; i < ROUNDS_HALF_FULL; i++) { 107 | full_round(ROUND_CONSTANTS, MDS, cells, round); 108 | round++; 109 | } 110 | } 111 | 112 | kernel void poseidon_fold(const device Fp* ROUND_CONSTANTS, 113 | const device Fp* MDS, 114 | const device Fp* PARTIAL_COMP_MATRIX, 115 | const device Fp* PARTIAL_COMP_OFFSET, 116 | device Fp* output, 117 | const device Fp* input, 118 | device uint32_t& output_size, 119 | uint gid [[thread_position_in_grid]]) { 120 | Fp cells[CELLS]; 121 | for (size_t i = 0; i < CELLS_OUT; i++) { 122 | cells[i] = input[2 * gid * CELLS_OUT + i]; 123 | cells[CELLS_OUT + i] = input[(2 * gid + 1) * CELLS_OUT + i]; 124 | } 125 | poseidon_mix(ROUND_CONSTANTS, MDS, PARTIAL_COMP_MATRIX, PARTIAL_COMP_OFFSET, cells); 126 | for (uint i = 0; i < CELLS_OUT; i++) { 127 | output[gid * CELLS_OUT + i] = cells[i]; 128 | } 129 | } 130 | 131 | kernel void poseidon_rows(const device Fp* ROUND_CONSTANTS, 132 | const device Fp* MDS, 133 | const device Fp* PARTIAL_COMP_MATRIX, 134 | const device Fp* PARTIAL_COMP_OFFSET, 135 | device Fp* out, 136 | const device Fp* matrix, 137 | device uint32_t& count, 138 | device uint32_t& col_size, 139 | uint gid [[thread_position_in_grid]]) { 140 | Fp cells[CELLS]; 141 | uint used = 0; 142 | for (uint i = 0; i < col_size; i++) { 143 | cells[used++] += matrix[i * count + gid]; 144 | if (used == CELLS_RATE) { 145 | poseidon_mix(ROUND_CONSTANTS, MDS, PARTIAL_COMP_MATRIX, PARTIAL_COMP_OFFSET, cells); 146 | used = 0; 147 | } 148 | } 149 | if (used != 0 || count == 0) { 150 | poseidon_mix(ROUND_CONSTANTS, MDS, PARTIAL_COMP_MATRIX, PARTIAL_COMP_OFFSET, cells); 151 | } 152 | for (uint i = 0; i < CELLS_OUT; i++) { 153 | out[CELLS_OUT * gid + i] = cells[i]; 154 | } 155 | } 156 | 157 | 158 | -------------------------------------------------------------------------------- /risc0/sys/kernels/zkp/metal/sha.metal: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Risc0, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "fp.h" 16 | #include "sha256.h" 17 | 18 | using namespace metal; 19 | 20 | kernel void sha_rows(device ShaDigest* out, 21 | const device Fp* matrix, 22 | device uint32_t& count, 23 | device uint32_t& col_size, 24 | uint gid [[thread_position_in_grid]]) { 25 | out[gid] = shaHash(matrix + gid, col_size, count); 26 | } 27 | 28 | kernel void sha_fold(device ShaDigest* out, 29 | const device ShaDigest* in, 30 | uint gid [[thread_position_in_grid]]) { 31 | out[gid] = shaHashPair(in[2 * gid], in[2 * gid + 1]); 32 | } 33 | -------------------------------------------------------------------------------- /risc0/sys/kernels/zkp/metal/sha256.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023 RISC Zero, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #pragma once 16 | 17 | #include 18 | 19 | #include "fp.h" 20 | 21 | // A internal implementation of SHA-256 (which is the only one we use). It's very basic, but I 22 | // would like to avoid dependencies on third party libraries, and in some places we need direct 23 | // access to the compression function, for example, when doing unpadded merkle tree rollups. 24 | // Additionally it's useful for the GPU acceleration. 25 | // 26 | // The core compression function is a modification (for style, GPU, and c++isms), based on the code 27 | // at: https://github.com/B-Con/crypto-algorithms/blob/master/sha256.c which is a public domain 28 | // implementation by Brad Conte (brad@bradconte.com) 29 | // 30 | // We avoid endian conversions by usually keeping the form of uint32_t values. We do 31 | // support hashing of byte arrays including endian conversion and padding, but we generally avoid it 32 | // when possible. 33 | 34 | // A digest (still in uint32_t parts for easy rolling up in merkle trees). 35 | struct ShaDigest { 36 | uint32_t words[8]; 37 | 38 | // The 'zero' digest, sort of the nullptr of digests. 39 | static ShaDigest zero() { return {{0, 0, 0, 0, 0, 0, 0, 0}}; } 40 | 41 | int cmp(ShaDigest rhs) const { 42 | for (size_t i = 0; i < 8; i++) { 43 | if (words[i] != rhs.words[i]) { 44 | return words[i] < rhs.words[i] ? -1 : 1; 45 | } 46 | } 47 | return 0; 48 | } 49 | 50 | bool operator==(ShaDigest rhs) const { return cmp(rhs) == 0; } 51 | 52 | bool operator!=(ShaDigest rhs) const { return cmp(rhs) != 0; } 53 | }; 54 | 55 | // Namespace to hide away some of the details from the user. 56 | namespace impl { 57 | 58 | inline uint32_t convertU32(uint32_t in) { 59 | uint32_t x0 = in & 0x000000ff; 60 | uint32_t x1 = in & 0x0000ff00; 61 | uint32_t x2 = in & 0x00ff0000; 62 | uint32_t x3 = in & 0xff000000; 63 | return (x0 << 24) | (x1 << 8) | (x2 >> 8) | (x3 >> 24); 64 | } 65 | 66 | inline uint32_t convertU32(Fp in) { 67 | return convertU32(in.asRaw()); 68 | } 69 | 70 | // Generate the initialization for hash state. 71 | inline ShaDigest initState() { 72 | return {{ 73 | convertU32(0x6a09e667), 74 | convertU32(0xbb67ae85), 75 | convertU32(0x3c6ef372), 76 | convertU32(0xa54ff53a), 77 | convertU32(0x510e527f), 78 | convertU32(0x9b05688c), 79 | convertU32(0x1f83d9ab), 80 | convertU32(0x5be0cd19), 81 | }}; 82 | } 83 | 84 | // Internal compression function, presumes chunk of 16 elements) 85 | inline void compress(thread ShaDigest& state, const thread uint32_t* chunk) { 86 | uint32_t roundK[64] = {0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 87 | 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 88 | 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 89 | 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 90 | 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 91 | 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 92 | 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 93 | 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 94 | 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 95 | 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 96 | 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2}; 97 | 98 | #define ROTLEFT(a, b) (((a) << (b)) | ((a) >> (32 - (b)))) 99 | #define ROTRIGHT(a, b) (((a) >> (b)) | ((a) << (32 - (b)))) 100 | #define CH(x, y, z) (((x) & (y)) ^ (~(x) & (z))) 101 | #define MAJ(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) 102 | #define EP0(x) (ROTRIGHT(x, 2) ^ ROTRIGHT(x, 13) ^ ROTRIGHT(x, 22)) 103 | #define EP1(x) (ROTRIGHT(x, 6) ^ ROTRIGHT(x, 11) ^ ROTRIGHT(x, 25)) 104 | #define SIG0(x) (ROTRIGHT(x, 7) ^ ROTRIGHT(x, 18) ^ ((x) >> 3)) 105 | #define SIG1(x) (ROTRIGHT(x, 17) ^ ROTRIGHT(x, 19) ^ ((x) >> 10)) 106 | 107 | #define ROUND_FUNC \ 108 | uint32_t t1 = h + EP1(e) + CH(e, f, g) + roundK[i] + w[i]; \ 109 | uint32_t t2 = EP0(a) + MAJ(a, b, c); \ 110 | h = g; \ 111 | g = f; \ 112 | f = e; \ 113 | e = d + t1; \ 114 | d = c; \ 115 | c = b; \ 116 | b = a; \ 117 | a = t1 + t2; 118 | uint32_t w[64]; 119 | 120 | uint32_t a = state.words[0]; 121 | uint32_t b = state.words[1]; 122 | uint32_t c = state.words[2]; 123 | uint32_t d = state.words[3]; 124 | uint32_t e = state.words[4]; 125 | uint32_t f = state.words[5]; 126 | uint32_t g = state.words[6]; 127 | uint32_t h = state.words[7]; 128 | 129 | for (size_t i = 0; i < 16; i++) { 130 | w[i] = chunk[i]; 131 | ROUND_FUNC; 132 | } 133 | for (size_t i = 16; i < 64; i++) { 134 | w[i] = SIG1(w[i - 2]) + w[i - 7] + SIG0(w[i - 15]) + w[i - 16]; 135 | ROUND_FUNC; 136 | } 137 | 138 | state.words[0] += a; 139 | state.words[1] += b; 140 | state.words[2] += c; 141 | state.words[3] += d; 142 | state.words[4] += e; 143 | state.words[5] += f; 144 | state.words[6] += g; 145 | state.words[7] += h; 146 | 147 | #undef ROTLEFT 148 | #undef ROTRIGHT 149 | #undef CH 150 | #undef MAJ 151 | #undef EP0 152 | #undef EP1 153 | #undef SIG0 154 | #undef SIG1 155 | #undef ROUND_FUNC 156 | } 157 | 158 | } // namespace impl 159 | 160 | // Main entry points 161 | inline ShaDigest shaHash(const device Fp* data, size_t size, size_t stride) { 162 | // Prepare the inital state 163 | uint32_t words[16]; 164 | uint32_t curWord = 0; 165 | ShaDigest state = impl::initState(); 166 | for (size_t i = 0; i < 8; i++) { 167 | state.words[i] = impl::convertU32(state.words[i]); 168 | } 169 | 170 | // Push all of the values 171 | for (size_t i = 0; i < size; i++) { 172 | words[curWord++] = impl::convertU32(data[i * stride]); 173 | if (curWord == 16) { 174 | impl::compress(state, words); 175 | curWord = 0; 176 | } 177 | } 178 | // Clear rest of the block 179 | for (size_t i = curWord; i < 16; i++) { 180 | words[i] = 0; 181 | } 182 | 183 | // Do final compression 184 | if (curWord != 0) { 185 | impl::compress(state, words); 186 | } 187 | 188 | for (size_t i = 0; i < 8; i++) { 189 | state.words[i] = impl::convertU32(state.words[i]); 190 | } 191 | 192 | return state; 193 | } 194 | 195 | inline ShaDigest shaHashPair(ShaDigest x, ShaDigest y) { 196 | // Copy both hash states into a single buffer 197 | uint32_t words[16]; 198 | for (size_t i = 0; i < 8; i++) { 199 | words[i] = impl::convertU32(x.words[i]); 200 | } 201 | for (size_t i = 0; i < 8; i++) { 202 | words[8 + i] = impl::convertU32(y.words[i]); 203 | } 204 | 205 | // Initialize state + compress 206 | ShaDigest state = impl::initState(); 207 | for (size_t i = 0; i < 8; i++) { 208 | state.words[i] = impl::convertU32(state.words[i]); 209 | } 210 | 211 | impl::compress(state, words); 212 | 213 | for (size_t i = 0; i < 8; i++) { 214 | state.words[i] = impl::convertU32(state.words[i]); 215 | } 216 | 217 | // Return the results 218 | return state; 219 | } 220 | -------------------------------------------------------------------------------- /risc0/sys/kernels/zkp/metal/zk.metal: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Risc0, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "fp.h" 16 | 17 | using namespace metal; 18 | 19 | kernel void zk_shift(device Fp* io, 20 | const device uint32_t& bits, 21 | uint gid [[thread_position_in_grid]]) { 22 | uint32_t pos = gid & ((1 << bits) - 1); 23 | uint32_t posRev = reverse_bits(pos) >> (32 - bits); 24 | Fp pow3 = pow(Fp(3), posRev); 25 | io[gid] = io[gid] * pow3; 26 | } 27 | -------------------------------------------------------------------------------- /risc0/sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 RISC Zero, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | -------------------------------------------------------------------------------- /risc0/zkvm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "risc0-zkvm" 3 | description = "RISC Zero zero-knowledge VM" 4 | version = { workspace = true } 5 | edition = { workspace = true } 6 | license = { workspace = true } 7 | homepage = { workspace = true } 8 | repository = { workspace = true } 9 | 10 | 11 | #[[bench]] 12 | #name = "guest_run" 13 | #harness = false 14 | 15 | [build-dependencies] 16 | prost-build = { version = "0.11", optional = true } 17 | protobuf-src = { version = "1.1", optional = true } 18 | 19 | [dependencies] 20 | anyhow = { version = "1.0", default-features = false } 21 | bytemuck = "1.12" 22 | cfg-if = "1.0" 23 | getrandom = { version = "0.2", features = ["custom"] } 24 | hex = { version = "0.4.3", default-features = false, features = ["alloc"] } 25 | risc0-core = { workspace = true } 26 | risc0-zkvm-platform = { workspace = true } 27 | serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] } 28 | tracing = { version = "0.1", default-features = false, features = ["attributes"] } 29 | 30 | # TODO(nils): Change these `target_os` checks to `target_vendor` checks when we 31 | # have a real target triple. 32 | # 33 | # Host dependencies 34 | [target.'cfg(not(target_os = "zkvm"))'.dependencies] 35 | addr2line = { version = "0.19", optional = true } 36 | elf = { version = "0.7", optional = true } 37 | generic-array = { version = "0.14", default-features = false, optional = true } 38 | getrandom = { version = "0.2", optional = true } 39 | gimli = { version = "0.27", optional = true } 40 | lazy-regex = { version = "2.3", optional = true } 41 | log = "0.4" 42 | num-derive = "0.3" 43 | num-traits = { version = "0.2", default-features = false } 44 | prost = { version = "0.11", optional = true } 45 | rand = { version = "0.8", optional = true } 46 | rayon = { version = "1.5", optional = true } 47 | rrs-lib = { version = "0.1", optional = true } 48 | sha2 = { version = "0.10", optional = true } 49 | 50 | [dev-dependencies] 51 | clap = { version = "4.0", features = ["derive"] } 52 | criterion = { version = "0.4", features = ["html_reports"] } 53 | human-repr = "1.0" 54 | tracing-forest = "0.1" 55 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 56 | 57 | [target.'cfg(not(target_os = "zkvm"))'.dev-dependencies] 58 | env_logger = "0.10" 59 | flate2 = "1.0" 60 | serial_test = "2.0" 61 | tar = "0.4" 62 | test-log = { version = "0.2", features = ["trace"] } 63 | 64 | [features] 65 | binfmt = ["dep:elf", "std"] 66 | default = ["prove"] 67 | dual = [] 68 | insecure_skip_seal = [] 69 | profiler = ["dep:addr2line", "dep:gimli", "dep:prost", "dep:prost-build", "dep:protobuf-src"] 70 | prove = [ 71 | "binfmt", 72 | "dep:generic-array", 73 | "dep:getrandom", 74 | "dep:lazy-regex", 75 | "dep:rand", 76 | "dep:rayon", 77 | "dep:rrs-lib", 78 | "dep:sha2", 79 | "std", 80 | ] 81 | std = [ 82 | "anyhow/std", 83 | "num-traits/std", 84 | "serde/std", 85 | ] 86 | -------------------------------------------------------------------------------- /risc0/zkvm/README.md: -------------------------------------------------------------------------------- 1 | The RISC Zero zkVM is a RISC-V virtual machine that produces [zero-knowledge 2 | proofs](https://en.wikipedia.org/wiki/Zero-knowledge_proof) of code it executes. 3 | By using the zkVM, a cryptographic [receipt](SessionReceipt) is produced which 4 | anyone can [verify](SessionReceipt::verify) was produced by the zkVM's guest 5 | code. No additional information about the code execution (such as, for example, 6 | the inputs provided) is revealed by publishing the [receipt](SessionReceipt). 7 | 8 | In addition to [our reference documentation on 9 | docs.rs](https://docs.rs/risc0-zkvm), we have additional (non-reference) 10 | resources for using our zkVM that you may also find helpful, especially if 11 | you're new to the RISC Zero zkVM. These include: 12 | 13 | * Our [Hello Multiply!](https://www.risczero.com/docs/examples/hello_multiply) 14 | tutorial, which walks you through writing your first zkVM project. 15 | * The [`cargo risczero` tool](https://crates.io/crates/cargo-risczero). It 16 | includes a `new` command which generates code for building and launching a zkVM 17 | guest and guidance on where projects most commonly modify host and guest code. 18 | * The [zkVM Rust examples 19 | directory](https://github.com/risc0/risc0/tree/main/examples), which contains 20 | various examples using our zkVM. 21 | * [This clip](https://youtu.be/cLqFvhmXiD0) from our presentation at ZK Hack III 22 | gives an overview of the RISC Zero zkVM. [Our YouTube 23 | channel](https://www.youtube.com/@risczero) has many more videos as well. 24 | * We track zkVM issues with known workarounds using the [rust guest 25 | workarounds](https://github.com/risc0/risc0/issues?q=is%3Aissue+is%3Aopen+label%3A%22rust+guest+workarounds%22) 26 | GitHub tag. If you're having problems running your code in the zkVM, you can 27 | see if there's a workaround, and if you're using a workaround, you can track 28 | when it gets resolved to a permanent solution. 29 | * And more on [the RISC Zero website](https://www.risczero.com/)! 30 | -------------------------------------------------------------------------------- /risc0/zkvm/build.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 RISC Zero, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | fn main() { 16 | #[cfg(feature = "profiler")] 17 | { 18 | std::env::set_var("PROTOC", protobuf_src::protoc()); 19 | prost_build::compile_protos(&["src/exec/profile.proto"], &["src/exec/"]).unwrap(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /risc0/zkvm/examples/fib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 RISC Zero, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use clap::Parser; 16 | use risc0_core::field::baby_bear::{BabyBear, BabyBearElem, BabyBearExtElem}; 17 | use risc0_zkp::{ 18 | core::hash::HashSuite, 19 | hal::{EvalCheck, Hal}, 20 | }; 21 | use risc0_zkvm::{prove::default_hal, ControlId, Executor, ExecutorEnv, Session, SessionReceipt}; 22 | use risc0_zkvm_methods::FIB_ELF; 23 | use tracing_subscriber::{prelude::*, EnvFilter}; 24 | 25 | #[derive(Parser)] 26 | #[clap()] 27 | struct Args { 28 | /// Number of iterations. 29 | #[clap(long)] 30 | iterations: u32, 31 | } 32 | 33 | fn main() { 34 | tracing_subscriber::registry() 35 | .with(EnvFilter::from_default_env()) 36 | .with(tracing_forest::ForestLayer::default()) 37 | .init(); 38 | 39 | let args = Args::parse(); 40 | let (hal, eval) = default_hal(); 41 | 42 | let (session, receipt) = top(hal.as_ref(), &eval, args.iterations); 43 | let po2 = session.segments[0].po2; 44 | let seal = receipt.segments[0].get_seal_bytes().len(); 45 | let journal = receipt.journal.len(); 46 | let total = seal + journal; 47 | println!("Po2: {po2}, Seal: {seal} bytes, Journal: {journal} bytes, Total: {total} bytes"); 48 | } 49 | 50 | #[tracing::instrument(skip_all)] 51 | fn top(hal: &H, eval: &E, iterations: u32) -> (Session, SessionReceipt) 52 | where 53 | H: Hal, 54 | <::HashSuite as HashSuite>::HashFn: ControlId, 55 | E: EvalCheck, 56 | { 57 | let env = ExecutorEnv::builder().add_input(&[iterations]).build(); 58 | let mut exec = Executor::from_elf(env, FIB_ELF).unwrap(); 59 | let session = exec.run().unwrap(); 60 | let receipt = session.prove(hal, eval).unwrap(); 61 | (session, receipt) 62 | } 63 | -------------------------------------------------------------------------------- /risc0/zkvm/examples/loop.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 RISC Zero, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use std::{process::Command, time::Instant}; 16 | 17 | use clap::Parser; 18 | use human_repr::{HumanCount, HumanDuration}; 19 | use risc0_core::field::baby_bear::{BabyBear, Elem, ExtElem}; 20 | use risc0_zkp::{ 21 | core::hash::HashSuite, 22 | hal::{EvalCheck, Hal}, 23 | }; 24 | use risc0_zkvm::{ 25 | prove::default_hal, serde::to_vec, ControlId, Executor, ExecutorEnv, Session, SessionReceipt, 26 | }; 27 | use risc0_zkvm_methods::{ 28 | bench::{BenchmarkSpec, SpecWithIters}, 29 | BENCH_ELF, 30 | }; 31 | use tracing_subscriber::{prelude::*, EnvFilter}; 32 | 33 | #[derive(Parser)] 34 | struct Args { 35 | /// Number of iterations. 36 | #[arg(long, short)] 37 | iterations: Option, 38 | 39 | #[arg(long, short)] 40 | quiet: bool, 41 | } 42 | 43 | fn main() { 44 | let args = Args::parse(); 45 | if let Some(iterations) = args.iterations { 46 | tracing_subscriber::registry() 47 | .with(EnvFilter::from_default_env()) 48 | .with(tracing_forest::ForestLayer::default()) 49 | .init(); 50 | 51 | let (hal, eval) = default_hal(); 52 | 53 | let start = Instant::now(); 54 | let (session, receipt) = top(hal.as_ref(), &eval, iterations); 55 | let duration = start.elapsed(); 56 | 57 | let cycles = session 58 | .segments 59 | .iter() 60 | .fold(0, |acc, segment| acc + (1 << segment.po2)); 61 | 62 | let seal = receipt 63 | .segments 64 | .iter() 65 | .fold(0, |acc, segment| acc + segment.get_seal_bytes().len()); 66 | let usage = hal.get_memory_usage(); 67 | let throughput = (cycles as f64) / duration.as_secs_f64(); 68 | 69 | if !args.quiet { 70 | println!( 71 | "| {:>9}k | {:>10} | {:>10} | {:>10} | {:>8}hz |", 72 | cycles / 1024, 73 | duration.human_duration().to_string(), 74 | usage.human_count_bytes().to_string(), 75 | seal.human_count_bytes().to_string(), 76 | throughput.human_count_bare().to_string() 77 | ); 78 | } 79 | } else { 80 | println!( 81 | "| {:>10} | {:>10} | {:>10} | {:>10} | {:>10} |", 82 | "Cycles", "Duration", "RAM", "Seal", "Speed" 83 | ); 84 | 85 | for iterations in [ 86 | 0, // warm-up 87 | 1, // 16, 64K 88 | 4 * 1024, // 17, 128K 89 | 16 * 1024, // 18, 256K 90 | 32 * 1024, // 19, 512K 91 | 64 * 1024, // 20, 1M 92 | 200 * 1024, // 21, 2M 93 | 400 * 1024, // 22, 4M 94 | 900 * 1024, // 23, 8M 95 | 1400 * 1024, // 24, 16M 96 | ] { 97 | run_with_iterations(iterations); 98 | } 99 | } 100 | } 101 | 102 | fn run_with_iterations(iterations: usize) { 103 | let mut cmd = Command::new(std::env::current_exe().unwrap()); 104 | if iterations == 0 { 105 | cmd.arg("--quiet"); 106 | } 107 | let ok = cmd 108 | .arg("--iterations") 109 | .arg(iterations.to_string()) 110 | .status() 111 | .unwrap() 112 | .success(); 113 | assert!(ok); 114 | } 115 | 116 | #[tracing::instrument(skip_all)] 117 | fn top(hal: &H, eval: &E, iterations: u64) -> (Session, SessionReceipt) 118 | where 119 | H: Hal, 120 | <::HashSuite as HashSuite>::HashFn: ControlId, 121 | E: EvalCheck, 122 | { 123 | let spec = SpecWithIters(BenchmarkSpec::SimpleLoop, iterations); 124 | let env = ExecutorEnv::builder() 125 | .add_input(&to_vec(&spec).unwrap()) 126 | .build(); 127 | let mut exec = Executor::from_elf(env, BENCH_ELF).unwrap(); 128 | let session = exec.run().unwrap(); 129 | let receipt = session.prove(hal, eval).unwrap(); 130 | (session, receipt) 131 | } 132 | -------------------------------------------------------------------------------- /risc0/zkvm/platform/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "risc0-zkvm-platform" 3 | description = "RISC Zero zero-knowledge VM" 4 | version = { workspace = true } 5 | edition = { workspace = true } 6 | license = { workspace = true } 7 | homepage = { workspace = true } 8 | repository = { workspace = true } 9 | 10 | # This crate is a potential dependency of the rust standard library. 11 | # As such, it should not depend on anything the rust standard library 12 | # doesn't. 13 | # 14 | # Also, it needs special trickery to work with the rust standard 15 | # library build scripts; see rustc-std-workspace-core/README.md in the 16 | # rust tree for details. 17 | [dependencies] 18 | compiler_builtins = { version = "0.1", optional = true } 19 | core = { optional = true, package = "rustc-std-workspace-core", version = "1.0.0" } 20 | 21 | [features] 22 | default = [] 23 | rustc-dep-of-std = ["core", "compiler_builtins"] 24 | -------------------------------------------------------------------------------- /risc0/zkvm/platform/README.md: -------------------------------------------------------------------------------- 1 | 2 | Platform definitions for the RISC Zero zkVM, including IO port addresses, 3 | memory regions, and low-level runtime functions. -------------------------------------------------------------------------------- /risc0/zkvm/platform/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 RISC Zero, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #![doc = include_str!("../README.md")] 16 | #![no_std] 17 | #![allow(unused_variables)] 18 | 19 | pub mod memory; 20 | #[macro_use] 21 | pub mod syscall; 22 | 23 | pub const DOUBLE_WORD_SIZE: usize = core::mem::size_of::(); 24 | pub const WORD_SIZE: usize = core::mem::size_of::(); 25 | pub const PAGE_SIZE: usize = 1024; 26 | 27 | /// Standard IO file descriptors for use with sys_read and sys_write. 28 | pub mod fileno { 29 | pub const STDIN: u32 = 0; 30 | pub const STDOUT: u32 = 1; 31 | pub const STDERR: u32 = 2; 32 | pub const JOURNAL: u32 = 3; 33 | } 34 | -------------------------------------------------------------------------------- /risc0/zkvm/platform/src/memory.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 RISC Zero, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use super::WORD_SIZE; 16 | use crate::DOUBLE_WORD_SIZE; 17 | 18 | pub const MEM_BITS: usize = 28; 19 | pub const MEM_SIZE: usize = 1 << MEM_BITS; 20 | 21 | // sp start from MEM_SIZE - 4096 * DOUBLE_WORD_SIZE and grows downwards 22 | // conventionally 23 | pub const STACK_INITIAL_ADDRESS: usize = MEM_SIZE - (32) * DOUBLE_WORD_SIZE; 24 | pub const HEAP_INITIAL_ADDRESS: usize = MEM_SIZE; // heap grows from top of 2^28 25 | 26 | pub struct Region { 27 | start: usize, 28 | len_bytes: usize, 29 | } 30 | 31 | const fn kb(kb: usize) -> usize { 32 | kb * 1024 33 | } 34 | 35 | const fn mb(mb: usize) -> usize { 36 | kb(mb * 1024) 37 | } 38 | 39 | impl Region { 40 | pub const fn new(start: usize, len_bytes: usize) -> Self { 41 | Self { start, len_bytes } 42 | } 43 | 44 | pub const fn start(&self) -> usize { 45 | self.start 46 | } 47 | 48 | pub const fn len_bytes(&self) -> usize { 49 | self.len_bytes 50 | } 51 | 52 | pub const fn len_words(&self) -> usize { 53 | assert!((self.len_bytes % WORD_SIZE) == 0); 54 | self.len_bytes / WORD_SIZE 55 | } 56 | 57 | pub const fn end(&self) -> usize { 58 | self.start + self.len_bytes 59 | } 60 | } 61 | 62 | // These should match the linker script in `risc0/build/risc0.ld`. 63 | pub const STACK: Region = Region::new(0x0000_0400, mb(8) - kb(1)); 64 | pub const DATA: Region = Region::new(0x0008_0000, mb(24)); 65 | pub const HEAP: Region = Region::new(0x0200_0000, mb(80)); 66 | pub const TEXT: Region = Region::new(0x0700_0000, mb(80)); 67 | pub const SYSTEM: Region = Region::new(0x0C00_0000, mb(16)); 68 | pub const PAGE_TABLE: Region = Region::new(0x0D00_0000, mb(16)); 69 | pub const PRE_LOAD: Region = Region::new(0x0D70_0000, mb(9)); 70 | -------------------------------------------------------------------------------- /risc0/zkvm/src/binfmt/elf.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 RISC Zero, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use alloc::collections::BTreeMap; 16 | 17 | use anyhow::{anyhow, bail, Context, Result}; 18 | use elf::{endian::LittleEndian, file::Class, ElfBytes}; 19 | 20 | /// A RISC Zero program 21 | pub struct Program { 22 | /// The entrypoint of the program 23 | pub entry: u64, 24 | 25 | /// The initial memory image 26 | pub image: BTreeMap, 27 | } 28 | 29 | impl Program { 30 | /// Initialize a RISC Zero Program from an appropriate ELF file 31 | pub fn load_elf(input: &[u8], max_mem: u64) -> Result { 32 | let mut image: BTreeMap = BTreeMap::new(); 33 | let elf = ElfBytes::::minimal_parse(input)?; 34 | if elf.ehdr.class != Class::ELF64 { 35 | bail!("Not a 64-bit ELF"); 36 | } 37 | if elf.ehdr.e_machine != elf::abi::EM_RISCV { 38 | bail!("Invalid machine type, must be RISC-V"); 39 | } 40 | if elf.ehdr.e_type != elf::abi::ET_EXEC { 41 | bail!("Invalid ELF type, must be executable"); 42 | } 43 | let entry: u64 = elf.ehdr.e_entry.try_into()?; 44 | if entry >= max_mem || entry % 4 != 0 { 45 | bail!("Invalid entrypoint"); 46 | } 47 | let segments = elf.segments().ok_or(anyhow!("Missing segment table"))?; 48 | if segments.len() > 256 { 49 | bail!("Too many program headers"); 50 | } 51 | for segment in segments.iter().filter(|x| x.p_type == elf::abi::PT_LOAD) { 52 | let file_size: u64 = segment.p_filesz.try_into()?; 53 | if file_size >= max_mem { 54 | bail!("Invalid segment file_size"); 55 | } 56 | let mem_size: u64 = segment.p_memsz.try_into()?; 57 | if mem_size >= max_mem { 58 | bail!("Invalid segment mem_size"); 59 | } 60 | let vaddr: u64 = segment.p_vaddr.try_into()?; 61 | let offset: u64 = segment.p_offset.try_into()?; 62 | for i in (0..mem_size).step_by(4) { 63 | let addr = vaddr.checked_add(i).context("Invalid segment vaddr")?; 64 | if i >= file_size { 65 | // Past the file size, all zeros. 66 | image.insert(addr, 0); 67 | } else { 68 | let mut word = 0; 69 | // Don't read past the end of the file. 70 | let len = std::cmp::min(file_size - i, 4); 71 | for j in 0..len { 72 | let offset = (offset + i + j) as usize; 73 | let byte = input.get(offset).context("Invalid segment offset")?; 74 | word |= (*byte as u32) << (j * 8); 75 | } 76 | image.insert(addr, word); 77 | } 78 | } 79 | } 80 | // patch below symbols to `ret` assembly 81 | // refer https://github.com/ethereum-optimism/cannon/blob/32c76db43dc4b5fb25f49ba8fbdb84fed8e5615a/mipsevm/patch.go#L66 82 | let (symtab, strtab) = elf 83 | .symbol_table() 84 | .expect("Failed to read symbol table") 85 | .expect("Failed to find strtab table"); 86 | symtab.iter().for_each(|entry| { 87 | let symbol_name = strtab.get(entry.st_name as usize).unwrap(); 88 | match symbol_name { 89 | "runtime.gcenable" 90 | | "runtime.init.5" // patch out: init() { go forcegchelper() } 91 | | "runtime.main.func1" // patch out: main.func() { newm(sysmon, ....) } 92 | | "runtime.deductSweepCredit" // uses floating point nums and interacts with gc we disabled 93 | | "runtime.(*gcControllerState).commit" 94 | // these prometheus packages rely on concurrent background things. We cannot run those. 95 | | "github.com/prometheus/client_golang/prometheus.init" 96 | | "github.com/prometheus/client_golang/prometheus.init.0" 97 | | "github.com/prometheus/procfs.init" 98 | | "github.com/prometheus/common/model.init" 99 | | "github.com/prometheus/client_model/go.init" 100 | | "github.com/prometheus/client_model/go.init.0" 101 | | "github.com/prometheus/client_model/go.init.1" 102 | // skip flag pkg init, we need to debug arg-processing more to see why this fails 103 | | "flag.init" 104 | | "runtime.fastexprand" // for mcache profiling, got float point inside 105 | | "runtime.getRandomData" // we do not need randomness. Besides it got os.open/os.read 106 | // We need to patch this out, we don't pass float64nan because we don't support floats 107 | | "runtime.initsig" // we dont need init signal since target on baremental env https://github.com/golang/go/blob/512361fb1fa805f10f183e0b96248e523e68c192/src/runtime/signal_unix.go#LL114C6-L114C13 108 | | "runtime.check" 109 | | "runtime.init" // patch out init, with float point initialization 110 | // | "runtime.doInit1" // patch out doInit https://github.com/golang/go/blob/512361fb1fa805f10f183e0b96248e523e68c192/src/runtime/proc.go#L198, since it got float point inside 111 | // | "runtime.lock2" // another choice is implement lock, which just need to implement `amoswap.w.aq`, 112 | // | "runtime.args" 113 | // | "runtime.osinit" 114 | // | "runtime.schedinit" 115 | => { 116 | println!( 117 | "symbol_name: {:?}, st_value {:08x}, image.get(key): {:08x}", 118 | symbol_name, 119 | entry.st_value, 120 | image.get(&entry.st_value).unwrap(), 121 | ); 122 | image.insert(entry.st_value, 0x00008067); // ret, immediate return 123 | () 124 | } 125 | _ => (), 126 | } 127 | }); 128 | // common.symtab.iter().map(|f| f) 129 | Ok(Program { entry, image }) 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /risc0/zkvm/src/binfmt/image.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 RISC Zero, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | use risc0_zkvm_platform::memory::{MEM_SIZE, PAGE_TABLE}; 15 | use rrs_lib::{ 16 | memories::{MemorySpace, VecMemory}, 17 | MemAccessSize, Memory, 18 | }; 19 | 20 | use crate::binfmt::elf::Program; 21 | 22 | /// Compute `ceil(a / b)` via truncated integer division. 23 | const fn div_ceil(a: u64, b: u64) -> u64 { 24 | (a + b - 1) / b 25 | } 26 | 27 | /// Round `a` up to the nearest multipe of `b`. 28 | const fn round_up(a: u64, b: u64) -> u64 { 29 | div_ceil(a, b) * b 30 | } 31 | 32 | /// An image of a zkVM guest's memory 33 | /// 34 | /// This is an image of the full memory state of the zkVM, including the data, 35 | /// text, inputs, page table, and system memory. In addition to the memory image 36 | /// proper, this includes some metadata about the page table. 37 | pub struct MemoryImage { 38 | /// The memory image as a vector of bytes 39 | // pub buf: Vec, 40 | 41 | /// memorySpace to support memory segment across different region 42 | pub memory_space: MemorySpace, 43 | } 44 | 45 | impl MemoryImage { 46 | /// Construct the initial memory image for `program` 47 | /// 48 | /// The result is a MemoryImage with the ELF of `program` loaded (but 49 | /// execution not yet begun), and with the page table Merkle tree 50 | /// constructed. 51 | pub fn new(program: &Program, page_size: u64, memory_data: Option>) -> Self { 52 | // let mut buf = vec![0_u8; MEM_SIZE]; 53 | 54 | let mut memory_space = MemorySpace::new(); 55 | let _ = memory_space 56 | .add_memory( 57 | 0, 58 | MEM_SIZE as u64, 59 | Box::new(VecMemory::new(vec![0_u64; MEM_SIZE / 8])), 60 | ) 61 | .unwrap(); 62 | // Load the ELF into the memory image. 63 | let program_region = memory_space.get_memory_mut::(0).unwrap(); 64 | for (addr, data) in program.image.iter() { 65 | program_region.write_mem(*addr, MemAccessSize::Word, u64::from(*data)); 66 | } 67 | // add memory region `0xd0000deadbeef` as playground 68 | let _ = memory_space 69 | .add_memory( 70 | 0xd0000deadbee0, 71 | MEM_SIZE as u64, 72 | Box::new(VecMemory::new(vec![0_u64; MEM_SIZE / 8])), 73 | ) 74 | .unwrap(); 75 | 76 | // Load the ELF into the memory image. 77 | let _ = memory_data.map(|memory_data| { 78 | println!("first length {:?}", memory_data[..4].to_vec()); 79 | let memory_data = memory_data 80 | .chunks(8) 81 | .map(|chunk| { 82 | let mut chunk = chunk.to_vec(); 83 | if chunk.len() < 8 { 84 | chunk.resize(8, 0) 85 | } 86 | u64::from_le_bytes(chunk.try_into().unwrap()) 87 | }) 88 | .collect::>(); 89 | // memory data space 90 | let _ = memory_space 91 | .add_memory( 92 | 0x100000000000, 93 | memory_data.len() as u64 * 8 as u64, 94 | Box::new(VecMemory::new(memory_data)), 95 | ) 96 | .unwrap(); 97 | }); 98 | 99 | // Compute the page table hashes except for the very last root hash. 100 | Self { memory_space } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /risc0/zkvm/src/binfmt/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 RISC Zero, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Manages formatted binaries used by the RISC Zero zkVM 16 | 17 | pub(crate) mod elf; 18 | pub(crate) mod image; 19 | -------------------------------------------------------------------------------- /risc0/zkvm/src/exec/env.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 RISC Zero, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! This module defines the [ExecutorEnv] and [ExecutorEnvBuilder]. 16 | 17 | use std::{ 18 | cell::RefCell, 19 | collections::HashMap, 20 | io::{BufRead, BufReader, Cursor, Read, Write}, 21 | rc::Rc, 22 | }; 23 | 24 | use bytemuck::Pod; 25 | use risc0_zkvm_platform::{ 26 | fileno, 27 | syscall::{ 28 | nr::{SYS_GETENV, SYS_READ, SYS_READ_AVAIL, SYS_WRITE}, 29 | SyscallName, 30 | }, 31 | }; 32 | 33 | use super::io::PosixIo; 34 | 35 | // use super::io::{slice_io_from_fn, syscalls, PosixIo, SliceIo, Syscall, 36 | // SyscallTable}; 37 | 38 | /// The default segment limit specified in powers of 2 cycles. Choose this value 39 | /// to try and fit with 8GB of RAM. 40 | const DEFAULT_SEGMENT_LIMIT_PO2: usize = 20; // 1M cycles 41 | 42 | /// The default session limit specified in cycles. 43 | const DEFAULT_SESSION_LIMIT: usize = 64 * 1024 * 1024; // 64M cycles 44 | 45 | /// A builder pattern used to construct an [ExecutorEnv]. 46 | #[derive(Clone)] 47 | pub struct ExecutorEnvBuilder<'a> { 48 | inner: ExecutorEnv<'a>, 49 | } 50 | 51 | /// The [super::Executor] is configured from this object. 52 | #[derive(Clone)] 53 | pub struct ExecutorEnv<'a> { 54 | env_vars: HashMap, 55 | pub(crate) segment_limit_po2: usize, 56 | session_limit: usize, 57 | // syscalls: SyscallTable<'a>, 58 | pub(crate) io: Rc>>, 59 | input: Vec, 60 | } 61 | 62 | impl<'a> ExecutorEnv<'a> { 63 | /// Construct a [ExecutorEnvBuilder]. 64 | pub fn builder() -> ExecutorEnvBuilder<'a> { 65 | ExecutorEnvBuilder::default() 66 | } 67 | 68 | pub(crate) fn get_segment_limit(&self) -> usize { 69 | 1 << self.segment_limit_po2 70 | } 71 | 72 | pub(crate) fn get_session_limit(&self) -> usize { 73 | self.session_limit 74 | } 75 | 76 | // pub(crate) fn get_syscall(&self, name: &str) -> Option<&Rc>> { self.syscalls.inner.get(name) 78 | // } 79 | } 80 | 81 | impl<'a> Default for ExecutorEnv<'a> { 82 | fn default() -> Self { 83 | Self::builder().build() 84 | } 85 | } 86 | 87 | impl<'a> Default for ExecutorEnvBuilder<'a> { 88 | fn default() -> Self { 89 | Self { 90 | inner: ExecutorEnv { 91 | env_vars: Default::default(), 92 | segment_limit_po2: DEFAULT_SEGMENT_LIMIT_PO2, 93 | session_limit: DEFAULT_SESSION_LIMIT, 94 | // syscalls: Default::default(), 95 | io: Default::default(), 96 | input: Default::default(), 97 | }, 98 | } 99 | } 100 | } 101 | 102 | impl<'a> ExecutorEnvBuilder<'a> { 103 | /// Finalize this builder to construct an [ExecutorEnv]. 104 | pub fn build(&mut self) -> ExecutorEnv<'a> { 105 | let mut result = self.clone(); 106 | // let getenv = syscalls::Getenv(self.inner.env_vars.clone()); 107 | if !self.inner.input.is_empty() { 108 | // let reader = Cursor::new(self.inner.input.clone()); 109 | // result 110 | // .inner 111 | // .io 112 | // .borrow_mut() 113 | // .with_read_fd(fileno::STDIN, reader); 114 | } 115 | // let io = result.inner.io.clone(); 116 | // result 117 | // .syscall(SYS_GETENV, getenv) 118 | // .syscall(SYS_READ, io.clone()) 119 | // .syscall(SYS_READ_AVAIL, io.clone()) 120 | // .syscall(SYS_WRITE, io); 121 | result.inner.clone() 122 | } 123 | 124 | /// Set a segment limit, specified in powers of 2 cycles. 125 | pub fn segment_limit_po2(&mut self, limit: usize) -> &mut Self { 126 | self.inner.segment_limit_po2 = limit; 127 | self 128 | } 129 | 130 | /// Set a session limit, specified in number of cycles. 131 | pub fn session_limit(&mut self, limit: usize) -> &mut Self { 132 | self.inner.session_limit = limit; 133 | self 134 | } 135 | 136 | /// Add environment variables to the guest environment. 137 | pub fn env_vars(&mut self, vars: HashMap) -> &mut Self { 138 | self.inner.env_vars = vars; 139 | self 140 | } 141 | 142 | /// Add an environment variable to the guest environment. 143 | pub fn env_var(&mut self, name: &str, val: &str) -> &mut Self { 144 | self.inner 145 | .env_vars 146 | .insert(name.to_string(), val.to_string()); 147 | self 148 | } 149 | 150 | /// Add initial input that can be read by the guest from stdin. 151 | pub fn add_input(&mut self, slice: &[T]) -> &mut Self { 152 | self.inner 153 | .input 154 | .extend_from_slice(bytemuck::cast_slice(slice)); 155 | self 156 | } 157 | 158 | // Add a handler for a raw syscall implementation. 159 | // pub fn syscall(&mut self, syscall: SyscallName, handler: impl Syscall + 'a) 160 | // -> &mut Self { self.inner.syscalls.with_syscall(syscall, handler); 161 | // self 162 | // } 163 | 164 | /// Add a posix-style standard input. 165 | pub fn stdin(&mut self, reader: impl Read + 'a) -> &mut Self { 166 | self.read_fd(fileno::STDIN, BufReader::new(reader)) 167 | } 168 | 169 | // Add a posix-style standard output. 170 | // pub fn stdout(&mut self, writer: impl Write + 'a) -> &mut Self { 171 | // self.write_fd(fileno::STDOUT, writer) 172 | // } 173 | 174 | /// Add a posix-style file descriptor for reading. 175 | pub fn read_fd(&mut self, fd: u32, reader: impl BufRead + 'a) -> &mut Self { 176 | self.inner.io.borrow_mut().with_read_fd(fd, reader); 177 | self 178 | } 179 | 180 | // Add a posix-style file descriptor for writing. 181 | // pub fn write_fd(&mut self, fd: u64, writer: impl Write + 'a) -> &mut Self { 182 | // self.inner.io.borrow_mut().with_write_fd(fd, writer); 183 | // self 184 | // } 185 | 186 | // Add a handler for a syscall which inputs and outputs a slice 187 | // of plain old data. The guest can call these by invoking 188 | // `risc0_zkvm::guest::env::send_recv_slice` 189 | // pub fn slice_io(&mut self, syscall: SyscallName, handler: impl SliceIo + 'a) 190 | // -> &mut Self { self.syscall(syscall, handler.to_syscall()); 191 | // self 192 | // } 193 | 194 | // Add a handler for a syscall which inputs and outputs a slice 195 | // of plain old data. The guest can call these callbacks by 196 | // invoking `risc0_zkvm::guest::env::send_recv_slice`. 197 | // pub fn io_callback( 198 | // &mut self, 199 | // syscall: SyscallName, 200 | // f: impl Fn(&[u8]) -> Vec + 'a, 201 | // ) -> &mut Self { 202 | // self.slice_io(syscall, slice_io_from_fn(f)); 203 | // self 204 | // } 205 | } 206 | -------------------------------------------------------------------------------- /risc0/zkvm/src/exec/profile.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Profile is a common stacktrace profile format. 16 | // 17 | // Measurements represented with this format should follow the 18 | // following conventions: 19 | // 20 | // - Consumers should treat unset optional fields as if they had been 21 | // set with their default value. 22 | // 23 | // - When possible, measurements should be stored in "unsampled" form 24 | // that is most useful to humans. There should be enough 25 | // information present to determine the original sampled values. 26 | // 27 | // - On-disk, the serialized proto must be gzip-compressed. 28 | // 29 | // - The profile is represented as a set of samples, where each sample 30 | // references a sequence of locations, and where each location belongs 31 | // to a mapping. 32 | // - There is a N->1 relationship from sample.location_id entries to 33 | // locations. For every sample.location_id entry there must be a 34 | // unique Location with that id. 35 | // - There is an optional N->1 relationship from locations to 36 | // mappings. For every nonzero Location.mapping_id there must be a 37 | // unique Mapping with that id. 38 | 39 | syntax = "proto3"; 40 | 41 | package perftools.profiles; 42 | 43 | option java_package = "com.google.perftools.profiles"; 44 | option java_outer_classname = "ProfileProto"; 45 | 46 | message Profile { 47 | // A description of the samples associated with each Sample.value. 48 | // For a cpu profile this might be: 49 | // [["cpu","nanoseconds"]] or [["wall","seconds"]] or [["syscall","count"]] 50 | // For a heap profile, this might be: 51 | // [["allocations","count"], ["space","bytes"]], 52 | // If one of the values represents the number of events represented 53 | // by the sample, by convention it should be at index 0 and use 54 | // sample_type.unit == "count". 55 | repeated ValueType sample_type = 1; 56 | // The set of samples recorded in this profile. 57 | repeated Sample sample = 2; 58 | // Mapping from address ranges to the image/binary/library mapped 59 | // into that address range. mapping[0] will be the main binary. 60 | repeated Mapping mapping = 3; 61 | // Useful program location 62 | repeated Location location = 4; 63 | // Functions referenced by locations 64 | repeated Function function = 5; 65 | // A common table for strings referenced by various messages. 66 | // string_table[0] must always be "". 67 | repeated string string_table = 6; 68 | // frames with Function.function_name fully matching the following 69 | // regexp will be dropped from the samples, along with their successors. 70 | int64 drop_frames = 7; // Index into string table. 71 | // frames with Function.function_name fully matching the following 72 | // regexp will be kept, even if it matches drop_frames. 73 | int64 keep_frames = 8; // Index into string table. 74 | 75 | // The following fields are informational, do not affect 76 | // interpretation of results. 77 | 78 | // Time of collection (UTC) represented as nanoseconds past the epoch. 79 | int64 time_nanos = 9; 80 | // Duration of the profile, if a duration makes sense. 81 | int64 duration_nanos = 10; 82 | // The kind of events between sampled ocurrences. 83 | // e.g [ "cpu","cycles" ] or [ "heap","bytes" ] 84 | ValueType period_type = 11; 85 | // The number of events between sampled occurrences. 86 | int64 period = 12; 87 | // Freeform text associated to the profile. 88 | repeated int64 comment = 13; // Indices into string table. 89 | // Index into the string table of the type of the preferred sample 90 | // value. If unset, clients should default to the last sample value. 91 | int64 default_sample_type = 14; 92 | } 93 | 94 | // ValueType describes the semantics and measurement units of a value. 95 | message ValueType { 96 | int64 type = 1; // Index into string table. 97 | int64 unit = 2; // Index into string table. 98 | } 99 | 100 | // Each Sample records values encountered in some program 101 | // context. The program context is typically a stack trace, perhaps 102 | // augmented with auxiliary information like the thread-id, some 103 | // indicator of a higher level request being handled etc. 104 | message Sample { 105 | // The ids recorded here correspond to a Profile.location.id. 106 | // The leaf is at location_id[0]. 107 | repeated uint64 location_id = 1; 108 | // The type and unit of each value is defined by the corresponding 109 | // entry in Profile.sample_type. All samples must have the same 110 | // number of values, the same as the length of Profile.sample_type. 111 | // When aggregating multiple samples into a single sample, the 112 | // result has a list of values that is the element-wise sum of the 113 | // lists of the originals. 114 | repeated int64 value = 2; 115 | // label includes additional context for this sample. It can include 116 | // things like a thread id, allocation size, etc 117 | repeated Label label = 3; 118 | } 119 | 120 | message Label { 121 | int64 key = 1; // Index into string table 122 | 123 | // At most one of the following must be present 124 | int64 str = 2; // Index into string table 125 | int64 num = 3; 126 | 127 | // Should only be present when num is present. 128 | // Specifies the units of num. 129 | // Use arbitrary string (for example, "requests") as a custom count unit. 130 | // If no unit is specified, consumer may apply heuristic to deduce the unit. 131 | // Consumers may also interpret units like "bytes" and "kilobytes" as memory 132 | // units and units like "seconds" and "nanoseconds" as time units, 133 | // and apply appropriate unit conversions to these. 134 | int64 num_unit = 4; // Index into string table 135 | } 136 | 137 | message Mapping { 138 | // Unique nonzero id for the mapping. 139 | uint64 id = 1; 140 | // Address at which the binary (or DLL) is loaded into memory. 141 | uint64 memory_start = 2; 142 | // The limit of the address range occupied by this mapping. 143 | uint64 memory_limit = 3; 144 | // Offset in the binary that corresponds to the first mapped address. 145 | uint64 file_offset = 4; 146 | // The object this entry is loaded from. This can be a filename on 147 | // disk for the main binary and shared libraries, or virtual 148 | // abstractions like "[vdso]". 149 | int64 filename = 5; // Index into string table 150 | // A string that uniquely identifies a particular program version 151 | // with high probability. E.g., for binaries generated by GNU tools, 152 | // it could be the contents of the .note.gnu.build-id field. 153 | int64 build_id = 6; // Index into string table 154 | 155 | // The following fields indicate the resolution of symbolic info. 156 | bool has_functions = 7; 157 | bool has_filenames = 8; 158 | bool has_line_numbers = 9; 159 | bool has_inline_frames = 10; 160 | } 161 | 162 | // Describes function and line table debug information. 163 | message Location { 164 | // Unique nonzero id for the location. A profile could use 165 | // instruction addresses or any integer sequence as ids. 166 | uint64 id = 1; 167 | // The id of the corresponding profile.Mapping for this location. 168 | // It can be unset if the mapping is unknown or not applicable for 169 | // this profile type. 170 | uint64 mapping_id = 2; 171 | // The instruction address for this location, if available. It 172 | // should be within [Mapping.memory_start...Mapping.memory_limit] 173 | // for the corresponding mapping. A non-leaf address may be in the 174 | // middle of a call instruction. It is up to display tools to find 175 | // the beginning of the instruction if necessary. 176 | uint64 address = 3; 177 | // Multiple line indicates this location has inlined functions, 178 | // where the last entry represents the caller into which the 179 | // preceding entries were inlined. 180 | // 181 | // E.g., if memcpy() is inlined into printf: 182 | // line[0].function_name == "memcpy" 183 | // line[1].function_name == "printf" 184 | repeated Line line = 4; 185 | // Provides an indication that multiple symbols map to this location's 186 | // address, for example due to identical code folding by the linker. In that 187 | // case the line information above represents one of the multiple 188 | // symbols. This field must be recomputed when the symbolization state of the 189 | // profile changes. 190 | bool is_folded = 5; 191 | } 192 | 193 | message Line { 194 | // The id of the corresponding profile.Function for this line. 195 | uint64 function_id = 1; 196 | // Line number in source code. 197 | int64 line = 2; 198 | } 199 | 200 | message Function { 201 | // Unique nonzero id for the function. 202 | uint64 id = 1; 203 | // Name of the function, in human-readable form if available. 204 | int64 name = 2; // Index into string table 205 | // Name of the function, as identified by the system. 206 | // For instance, it can be a C++ mangled name. 207 | int64 system_name = 3; // Index into string table 208 | // Source file containing the function. 209 | int64 filename = 4; // Index into string table 210 | // Line number in source file. 211 | int64 start_line = 5; 212 | } 213 | -------------------------------------------------------------------------------- /risc0/zkvm/src/guest/alloc.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 RISC Zero, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use core::{ 16 | alloc::{GlobalAlloc, Layout}, 17 | cell::UnsafeCell, 18 | }; 19 | 20 | use risc0_zkvm_platform::{memory, syscall, WORD_SIZE}; 21 | 22 | struct BumpPointerAlloc; 23 | 24 | #[cfg(target_os = "zkvm")] 25 | unsafe impl GlobalAlloc for BumpPointerAlloc { 26 | unsafe fn alloc(&self, layout: Layout) -> *mut u8 { 27 | let nwords = layout 28 | .align_to(WORD_SIZE) 29 | .expect("Unable to align allocation to word size") 30 | .pad_to_align() 31 | .size() 32 | / WORD_SIZE; 33 | 34 | syscall::sys_alloc_words(nwords) as *mut u8 35 | } 36 | 37 | unsafe fn dealloc(&self, _: *mut u8, _: Layout) { 38 | // this allocator never deallocates memory 39 | } 40 | } 41 | 42 | #[cfg(target_os = "zkvm")] 43 | #[global_allocator] 44 | static HEAP: BumpPointerAlloc = BumpPointerAlloc; 45 | -------------------------------------------------------------------------------- /risc0/zkvm/src/guest/memset.s: -------------------------------------------------------------------------------- 1 | // This is musl-libc memset commit 37e18b7bf307fa4a8c745feebfcba54a0ba74f30: 2 | // 3 | // src/string/memset.c 4 | // 5 | // This was compiled into assembly with: 6 | // 7 | // clang-14 -target riscv32 -march=rv32im -O3 -S memset.c -nostdlib -fno-builtin -funroll-loops 8 | // 9 | // and labels manually updated to not conflict. 10 | // 11 | // musl as a whole is licensed under the following standard MIT license: 12 | // 13 | // ---------------------------------------------------------------------- 14 | // Copyright © 2005-2020 Rich Felker, et al. 15 | // 16 | // Permission is hereby granted, free of charge, to any person obtaining 17 | // a copy of this software and associated documentation files (the 18 | // "Software"), to deal in the Software without restriction, including 19 | // without limitation the rights to use, copy, modify, merge, publish, 20 | // distribute, sublicense, and/or sell copies of the Software, and to 21 | // permit persons to whom the Software is furnished to do so, subject to 22 | // the following conditions: 23 | // 24 | // The above copyright notice and this permission notice shall be 25 | // included in all copies or substantial portions of the Software. 26 | // 27 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 28 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 29 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 30 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 31 | // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 32 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 33 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 34 | // ---------------------------------------------------------------------- 35 | // 36 | // Authors/contributors include: 37 | // 38 | // A. Wilcox 39 | // Ada Worcester 40 | // Alex Dowad 41 | // Alex Suykov 42 | // Alexander Monakov 43 | // Andre McCurdy 44 | // Andrew Kelley 45 | // Anthony G. Basile 46 | // Aric Belsito 47 | // Arvid Picciani 48 | // Bartosz Brachaczek 49 | // Benjamin Peterson 50 | // Bobby Bingham 51 | // Boris Brezillon 52 | // Brent Cook 53 | // Chris Spiegel 54 | // Clément Vasseur 55 | // Daniel Micay 56 | // Daniel Sabogal 57 | // Daurnimator 58 | // David Carlier 59 | // David Edelsohn 60 | // Denys Vlasenko 61 | // Dmitry Ivanov 62 | // Dmitry V. Levin 63 | // Drew DeVault 64 | // Emil Renner Berthing 65 | // Fangrui Song 66 | // Felix Fietkau 67 | // Felix Janda 68 | // Gianluca Anzolin 69 | // Hauke Mehrtens 70 | // He X 71 | // Hiltjo Posthuma 72 | // Isaac Dunham 73 | // Jaydeep Patil 74 | // Jens Gustedt 75 | // Jeremy Huntwork 76 | // Jo-Philipp Wich 77 | // Joakim Sindholt 78 | // John Spencer 79 | // Julien Ramseier 80 | // Justin Cormack 81 | // Kaarle Ritvanen 82 | // Khem Raj 83 | // Kylie McClain 84 | // Leah Neukirchen 85 | // Luca Barbato 86 | // Luka Perkov 87 | // M Farkas-Dyck (Strake) 88 | // Mahesh Bodapati 89 | // Markus Wichmann 90 | // Masanori Ogino 91 | // Michael Clark 92 | // Michael Forney 93 | // Mikhail Kremnyov 94 | // Natanael Copa 95 | // Nicholas J. Kain 96 | // orc 97 | // Pascal Cuoq 98 | // Patrick Oppenlander 99 | // Petr Hosek 100 | // Petr Skocik 101 | // Pierre Carrier 102 | // Reini Urban 103 | // Rich Felker 104 | // Richard Pennington 105 | // Ryan Fairfax 106 | // Samuel Holland 107 | // Segev Finer 108 | // Shiz 109 | // sin 110 | // Solar Designer 111 | // Stefan Kristiansson 112 | // Stefan O'Rear 113 | // Szabolcs Nagy 114 | // Timo Teräs 115 | // Trutz Behn 116 | // Valentin Ochs 117 | // Will Dietz 118 | // William Haddon 119 | // William Pitcock 120 | // 121 | // Portions of this software are derived from third-party works licensed 122 | // under terms compatible with the above MIT license: 123 | // 124 | // The TRE regular expression implementation (src/regex/reg* and 125 | // src/regex/tre*) is Copyright © 2001-2008 Ville Laurikari and licensed 126 | // under a 2-clause BSD license (license text in the source files). The 127 | // included version has been heavily modified by Rich Felker in 2012, in 128 | // the interests of size, simplicity, and namespace cleanliness. 129 | // 130 | // Much of the math library code (src/math/* and src/complex/*) is 131 | // Copyright © 1993,2004 Sun Microsystems or 132 | // Copyright © 2003-2011 David Schultz or 133 | // Copyright © 2003-2009 Steven G. Kargl or 134 | // Copyright © 2003-2009 Bruce D. Evans or 135 | // Copyright © 2008 Stephen L. Moshier or 136 | // Copyright © 2017-2018 Arm Limited 137 | // and labelled as such in comments in the individual source files. All 138 | // have been licensed under extremely permissive terms. 139 | // 140 | // The ARM memcpy code (src/string/arm/memcpy.S) is Copyright © 2008 141 | // The Android Open Source Project and is licensed under a two-clause BSD 142 | // license. It was taken from Bionic libc, used on Android. 143 | // 144 | // The AArch64 memcpy and memset code (src/string/aarch64/*) are 145 | // Copyright © 1999-2019, Arm Limited. 146 | // 147 | // The implementation of DES for crypt (src/crypt/crypt_des.c) is 148 | // Copyright © 1994 David Burren. It is licensed under a BSD license. 149 | // 150 | // The implementation of blowfish crypt (src/crypt/crypt_blowfish.c) was 151 | // originally written by Solar Designer and placed into the public 152 | // domain. The code also comes with a fallback permissive license for use 153 | // in jurisdictions that may not recognize the public domain. 154 | // 155 | // The smoothsort implementation (src/stdlib/qsort.c) is Copyright © 2011 156 | // Valentin Ochs and is licensed under an MIT-style license. 157 | // 158 | // The x86_64 port was written by Nicholas J. Kain and is licensed under 159 | // the standard MIT terms. 160 | // 161 | // The mips and microblaze ports were originally written by Richard 162 | // Pennington for use in the ellcc project. The original code was adapted 163 | // by Rich Felker for build system and code conventions during upstream 164 | // integration. It is licensed under the standard MIT terms. 165 | // 166 | // The mips64 port was contributed by Imagination Technologies and is 167 | // licensed under the standard MIT terms. 168 | // 169 | // The powerpc port was also originally written by Richard Pennington, 170 | // and later supplemented and integrated by John Spencer. It is licensed 171 | // under the standard MIT terms. 172 | // 173 | // All other files which have no copyright comments are original works 174 | // produced specifically for use as part of this library, written either 175 | // by Rich Felker, the main author of the library, or by one or more 176 | // contibutors listed above. Details on authorship of individual files 177 | // can be found in the git version control history of the project. The 178 | // omission of copyright and license comments in each file is in the 179 | // interest of source tree size. 180 | // 181 | // In addition, permission is hereby granted for all public header files 182 | // (include/* and arch/* /bits/* ) and crt files intended to be linked into 183 | // applications (crt/*, ldso/dlstart.c, and arch/* /crt_arch.h) to omit 184 | // the copyright notice and permission notice otherwise required by the 185 | // license, and to use these files without any requirement of 186 | // attribution. These files include substantial contributions from: 187 | // 188 | // Bobby Bingham 189 | // John Spencer 190 | // Nicholas J. Kain 191 | // Rich Felker 192 | // Richard Pennington 193 | // Stefan Kristiansson 194 | // Szabolcs Nagy 195 | // 196 | // all of whom have explicitly granted such permission. 197 | // 198 | // This file previously contained text expressing a belief that most of 199 | // the files covered by the above exception were sufficiently trivial not 200 | // to be subject to copyright, resulting in confusion over whether it 201 | // negated the permissions granted in the license. In the spirit of 202 | // permissive licensing, and of not having licensing issues being an 203 | // obstacle to adoption, that text has been removed. 204 | .text 205 | .attribute 4, 16 206 | .attribute 5, "rv32i2p0_m2p0" 207 | .file "musl_memset.c" 208 | .globl memset 209 | .p2align 2 210 | .type memset,@function 211 | memset: 212 | beqz a2, .LBB0_9memset 213 | sb a1, 0(a0) 214 | add a3, a2, a0 215 | li a4, 3 216 | sb a1, -1(a3) 217 | bltu a2, a4, .LBB0_9memset 218 | sb a1, 1(a0) 219 | sb a1, 2(a0) 220 | sb a1, -2(a3) 221 | li a4, 7 222 | sb a1, -3(a3) 223 | bltu a2, a4, .LBB0_9memset 224 | sb a1, 3(a0) 225 | li a5, 9 226 | sb a1, -4(a3) 227 | bltu a2, a5, .LBB0_9memset 228 | neg a3, a0 229 | andi a4, a3, 3 230 | add a3, a0, a4 231 | sub a2, a2, a4 232 | andi a2, a2, -4 233 | andi a1, a1, 255 234 | lui a4, 4112 235 | addi a4, a4, 257 236 | mul a1, a1, a4 237 | sw a1, 0(a3) 238 | add a4, a3, a2 239 | sw a1, -4(a4) 240 | bltu a2, a5, .LBB0_9memset 241 | sw a1, 4(a3) 242 | sw a1, 8(a3) 243 | sw a1, -12(a4) 244 | li a5, 25 245 | sw a1, -8(a4) 246 | bltu a2, a5, .LBB0_9memset 247 | sw a1, 12(a3) 248 | sw a1, 16(a3) 249 | sw a1, 20(a3) 250 | sw a1, 24(a3) 251 | sw a1, -28(a4) 252 | sw a1, -24(a4) 253 | sw a1, -20(a4) 254 | andi a5, a3, 4 255 | ori a5, a5, 24 256 | sub a2, a2, a5 257 | li a6, 32 258 | sw a1, -16(a4) 259 | bltu a2, a6, .LBB0_9memset 260 | add a3, a3, a5 261 | li a4, 31 262 | .LBB0_8memset: 263 | sw a1, 0(a3) 264 | sw a1, 4(a3) 265 | sw a1, 8(a3) 266 | sw a1, 12(a3) 267 | sw a1, 16(a3) 268 | sw a1, 20(a3) 269 | sw a1, 24(a3) 270 | sw a1, 28(a3) 271 | addi a2, a2, -32 272 | addi a3, a3, 32 273 | bltu a4, a2, .LBB0_8memset 274 | .LBB0_9memset: 275 | ret 276 | .Lfunc_end0memset: 277 | .size memset, .Lfunc_end0memset-memset 278 | 279 | .ident "Ubuntu clang version 14.0.6-++20220622053131+f28c006a5895-1~exp1~20220622173215.157" 280 | .section ".note.GNU-stack","",@progbits 281 | .addrsig 282 | -------------------------------------------------------------------------------- /risc0/zkvm/src/guest/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 RISC Zero, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! The RISC Zero ZKVM's guest-side RISC-V API. 16 | //! 17 | //! Code that is validated by the [RISC Zero zkVM](crate) is run inside the 18 | //! guest. In the minimal case, an entrypoint (the guest's "`main`" function) 19 | //! must be provided by using the [entry! macro](entry). In almost all 20 | //! practical cases, the guest will want to read private input data using 21 | //! [env::read] and commit public output data using [env::commit]; additional 22 | //! I/O functionality is also available in [mod@env]. 23 | //! 24 | //! For example[^starter-ex], the following guest code proves a number is 25 | //! composite by multiplying two unsigned integers, and panicking if either is 26 | //! `1` or if the multiplication overflows: 27 | //! ```ignore 28 | //! use risc0_zkvm::guest::env; 29 | //! 30 | //! risc0_zkvm::entry!(main); 31 | //! 32 | //! pub fn main() { 33 | //! // Load the first number from the host 34 | //! let a: u64 = env::read(); 35 | //! // Load the second number from the host 36 | //! let b: u64 = env::read(); 37 | //! // Verify that neither of them are 1 (i.e. nontrivial factors) 38 | //! if a == 1 || b == 1 { 39 | //! panic!("Trivial factors") 40 | //! } 41 | //! // Compute the product while being careful with integer overflow 42 | //! let product = a.checked_mul(b).expect("Integer overflow"); 43 | //! env::commit(&product); 44 | //! } 45 | //! ``` 46 | //! Notice how the [entry! macro](entry) is used to indicate the 47 | //! entrypoint, [env::read] is used to load the two factors, and [env::commit] 48 | //! is used to make their composite product publically available. 49 | //! 50 | //! If you encounter problems building zkVM guest code, you can see if we have a 51 | //! known workaround for your issue by looking in our 52 | //! [rust guest workarounds](https://github.com/risc0/risc0/issues?q=is%3Aissue+is%3Aopen+label%3A%22rust+guest+workarounds%22) 53 | //! tag on GitHub. 54 | //! 55 | //! [^starter-ex]: The example is based on the [RISC Zero Rust Starter repository](https://github.com/risc0/risc0-rust-starter). 56 | 57 | #![allow(unused)] 58 | #![deny(missing_docs)] 59 | 60 | mod alloc; 61 | pub mod env; 62 | pub mod sha; 63 | 64 | use core::{arch::asm, mem, ptr}; 65 | 66 | use getrandom::{register_custom_getrandom, Error}; 67 | use risc0_zkvm_platform::{ 68 | syscall::{nr::SYS_PANIC, sys_panic, sys_rand}, 69 | WORD_SIZE, 70 | }; 71 | 72 | pub use crate::entry; 73 | 74 | /// This is a getrandom handler for the zkvm. It's intended to hook into a 75 | /// getrandom crate or a depdent of the getrandom crate used by the guest code. 76 | pub fn zkvm_getrandom(dest: &mut [u8]) -> Result<(), Error> { 77 | if dest.is_empty() { 78 | return Ok(()); 79 | } 80 | 81 | let words = (dest.len() + WORD_SIZE - 1) / WORD_SIZE; 82 | let mut buf = ::alloc::vec![0u32; words]; 83 | unsafe { 84 | sys_rand(buf.as_mut_ptr(), words); 85 | } 86 | dest.clone_from_slice(&bytemuck::cast_slice(buf.as_slice())[..dest.len()]); 87 | Ok(()) 88 | } 89 | 90 | register_custom_getrandom!(zkvm_getrandom); 91 | 92 | #[cfg(target_os = "zkvm")] 93 | core::arch::global_asm!(include_str!("memset.s")); 94 | #[cfg(target_os = "zkvm")] 95 | core::arch::global_asm!(include_str!("memcpy.s")); 96 | 97 | fn _fault() -> ! { 98 | #[cfg(target_os = "zkvm")] 99 | unsafe { 100 | asm!("sw x0, 1(x0)") 101 | }; 102 | unreachable!(); 103 | } 104 | 105 | /// Aborts the guest with the given message. 106 | pub fn abort(msg: &str) -> ! { 107 | // A compliant host should fault when it receives this syscall. 108 | unsafe { 109 | sys_panic(msg.as_ptr(), msg.len()); 110 | } 111 | 112 | // As a fallback for non-compliant hosts, issue an illegal instruction. 113 | _fault() 114 | } 115 | 116 | #[cfg(all(not(feature = "std"), target_os = "zkvm"))] 117 | mod handlers { 118 | use core::{alloc::Layout, panic::PanicInfo}; 119 | 120 | #[panic_handler] 121 | fn panic_fault(panic_info: &PanicInfo) -> ! { 122 | let msg = ::alloc::format!("{}", panic_info); 123 | crate::guest::abort(&msg) 124 | } 125 | 126 | #[alloc_error_handler] 127 | fn alloc_fault(_layout: Layout) -> ! { 128 | crate::guest::abort("Memory allocation failure") 129 | } 130 | } 131 | 132 | /// Used for defining a main entrypoint. 133 | /// 134 | /// # Example 135 | /// 136 | /// ```ignore 137 | /// risc0_zkvm::entry!(main); 138 | /// 139 | /// fn main() { } 140 | /// ``` 141 | #[macro_export] 142 | macro_rules! entry { 143 | ($path:path) => { 144 | #[no_mangle] 145 | fn __main() { 146 | // type check the given path 147 | let f: fn() = $path; 148 | f() 149 | } 150 | }; 151 | } 152 | 153 | #[cfg(target_os = "zkvm")] 154 | #[no_mangle] 155 | unsafe extern "C" fn __start() { 156 | extern "C" { 157 | static mut __bss_begin: *mut u32; 158 | static mut __bss_end: *mut u32; 159 | } 160 | 161 | let mut bss = __bss_begin; 162 | while bss < __bss_end { 163 | ptr::write_volatile(bss, mem::zeroed()); 164 | bss = bss.offset(1); 165 | } 166 | 167 | env::init(); 168 | 169 | extern "Rust" { 170 | fn __main(); 171 | } 172 | __main(); 173 | 174 | env::finalize(); 175 | } 176 | 177 | #[cfg(target_os = "zkvm")] 178 | core::arch::global_asm!( 179 | r#" 180 | .section .text._start; 181 | .globl _start; 182 | _start: 183 | .option push; 184 | .option norelax; 185 | la gp, __global_pointer$; 186 | .option pop; 187 | la sp, __stack_init$; 188 | jal ra, __start 189 | "# 190 | ); 191 | 192 | /// Require that accesses to behind the given pointer before the memory 193 | /// barrier don't get optimized away or reordered to after the memory 194 | /// barrier. 195 | pub fn memory_barrier(ptr: *const T) { 196 | // SAFETY: This passes a pointer in, but does nothing with it. 197 | unsafe { asm!("/* {0} */", in(reg) (ptr)) } 198 | } 199 | -------------------------------------------------------------------------------- /risc0/zkvm/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 RISC Zero, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #![doc = include_str!("../README.md")] 16 | #![cfg_attr(not(feature = "std"), no_std)] 17 | #![cfg_attr(not(feature = "std"), feature(alloc_error_handler))] 18 | #![deny(rustdoc::broken_intra_doc_links)] 19 | #![deny(missing_docs)] 20 | 21 | extern crate alloc; 22 | 23 | #[cfg(feature = "binfmt")] 24 | pub mod binfmt; 25 | #[cfg(feature = "prove")] 26 | mod exec; 27 | #[cfg(any(target_os = "zkvm", doc))] 28 | pub mod guest; 29 | #[cfg(feature = "prove")] 30 | mod opcode; 31 | pub mod serde; 32 | #[cfg(feature = "prove")] 33 | mod session; 34 | 35 | pub use anyhow::Result; 36 | pub use risc0_zkvm_platform::{declare_syscall, memory::MEM_SIZE, PAGE_SIZE}; 37 | 38 | #[cfg(feature = "binfmt")] 39 | pub use self::binfmt::{elf::Program, image::MemoryImage}; 40 | #[cfg(feature = "prove")] 41 | pub use self::{ 42 | exec::{Executor, ExecutorEnv, ExecutorEnvBuilder}, 43 | session::{ExitCode, Segment, Session}, 44 | }; 45 | 46 | /// Align the given address `addr` upwards to alignment `align`. 47 | /// 48 | /// Requires that `align` is a power of two. 49 | pub const fn align_up(addr: usize, align: usize) -> usize { 50 | (addr + align - 1) & !(align - 1) 51 | } 52 | -------------------------------------------------------------------------------- /risc0/zkvm/src/opcode.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 RISC Zero, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use anyhow::{bail, Result}; 16 | use num_traits::FromPrimitive; 17 | use rrs_lib::{instruction_string_outputter::InstructionStringOutputter, process_instruction}; 18 | 19 | #[allow(dead_code)] 20 | #[derive(Debug, num_derive::FromPrimitive, PartialEq)] 21 | #[repr(u32)] 22 | pub enum MajorType { 23 | Compute0, 24 | Compute1, 25 | Compute2, 26 | MemIo, 27 | Multiply, 28 | Divide, 29 | VerifyAnd, 30 | VerifyDivide, 31 | ECall, 32 | ShaInit, 33 | ShaLoad, 34 | ShaMain, 35 | PageFault, 36 | MuxSize, 37 | } 38 | 39 | pub struct OpCode { 40 | pub insn: u32, 41 | pub insn_pc: u64, 42 | pub mnemonic: &'static str, 43 | pub major: MajorType, 44 | pub minor: u32, 45 | pub cycles: usize, 46 | } 47 | 48 | impl MajorType { 49 | pub fn as_u32(self) -> u32 { 50 | self as u32 51 | } 52 | } 53 | 54 | impl OpCode { 55 | fn new(insn: u32, insn_pc: u64, mnemonic: &'static str, idx: u32, cycles: usize) -> Self { 56 | Self { 57 | insn, 58 | insn_pc, 59 | mnemonic, 60 | major: FromPrimitive::from_u32(idx / 8).unwrap(), 61 | minor: idx % 8, 62 | cycles, 63 | } 64 | } 65 | 66 | fn with_major_minor( 67 | insn: u32, 68 | insn_pc: u64, 69 | mnemonic: &'static str, 70 | major: MajorType, 71 | minor: u32, 72 | cycles: usize, 73 | ) -> Self { 74 | Self { 75 | insn, 76 | insn_pc, 77 | mnemonic, 78 | major, 79 | minor, 80 | cycles, 81 | } 82 | } 83 | 84 | pub fn decode(insn: u32, insn_pc: u64) -> Result { 85 | let opcode = insn & 0x0000007f; 86 | let rs2 = (insn & 0x01f00000) >> 20; 87 | let funct3 = (insn & 0x00007000) >> 12; 88 | let funct7 = (insn & 0xfe000000) >> 25; 89 | // RV64 bit 25 is used as shamt[5] 90 | let funct7_rv64 = (insn & 0xfc000000) >> 26; 91 | let funct5 = (insn & 0xf8000000) >> 27; 92 | Ok(match opcode { 93 | 0b0000011 => match funct3 { 94 | 0x0 => OpCode::new(insn, insn_pc, "LB", 24, 1), 95 | 0x1 => OpCode::new(insn, insn_pc, "LH", 25, 1), 96 | 0x2 => OpCode::new(insn, insn_pc, "LW", 26, 1), 97 | 0x3 => OpCode::new(insn, insn_pc, "LD", 27, 1), // RV64I 98 | 0x4 => OpCode::new(insn, insn_pc, "LBU", 28, 1), 99 | 0x5 => OpCode::new(insn, insn_pc, "LHU", 29, 1), 100 | 0x6 => OpCode::new(insn, insn_pc, "LWU", 30, 1), 101 | _ => { 102 | println!("opcode {:#8x}", insn); 103 | log::debug!("opcode {:?}", opcode); 104 | unreachable!() 105 | } 106 | }, 107 | 0b0010011 => match funct3 { 108 | 0x0 => OpCode::new(insn, insn_pc, "ADDI", 7, 1), 109 | 0x1 => OpCode::new(insn, insn_pc, "SLLI", 37, 1), 110 | 0x2 => OpCode::new(insn, insn_pc, "SLTI", 11, 1), 111 | 0x3 => OpCode::new(insn, insn_pc, "SLTIU", 12, 1), 112 | 0x4 => OpCode::new(insn, insn_pc, "XORI", 8, 2), 113 | 0x5 => match funct7_rv64 { 114 | 0b000000 => OpCode::new(insn, insn_pc, "SRLI", 46, 2), 115 | 0b010000 => OpCode::new(insn, insn_pc, "SRAI", 47, 2), 116 | _ => { 117 | println!("funct7 {:8x}", funct7); 118 | unreachable!() 119 | } 120 | }, 121 | 0x6 => OpCode::new(insn, insn_pc, "ORI", 9, 2), 122 | 0x7 => OpCode::new(insn, insn_pc, "ANDI", 10, 2), 123 | _ => unreachable!(), 124 | }, 125 | 0b0010111 => OpCode::new(insn, insn_pc, "AUIPC", 22, 1), 126 | 0b0100011 => match funct3 { 127 | 0x0 => OpCode::new(insn, insn_pc, "SB", 29, 1), 128 | 0x1 => OpCode::new(insn, insn_pc, "SH", 30, 1), 129 | 0x2 => OpCode::new(insn, insn_pc, "SW", 31, 1), 130 | 0x3 => OpCode::new(insn, insn_pc, "SD", 31, 1), 131 | _ => unreachable!(), 132 | }, 133 | 0b0110011 => match (funct3, funct7) { 134 | (0x0, 0x00) => OpCode::new(insn, insn_pc, "ADD", 0, 1), 135 | (0x0, 0x20) => OpCode::new(insn, insn_pc, "SUB", 1, 1), 136 | (0x1, 0x00) => OpCode::new(insn, insn_pc, "SLL", 36, 1), 137 | (0x2, 0x00) => OpCode::new(insn, insn_pc, "SLT", 5, 1), 138 | (0x3, 0x00) => OpCode::new(insn, insn_pc, "SLTU", 6, 1), 139 | (0x4, 0x00) => OpCode::new(insn, insn_pc, "XOR", 2, 2), 140 | (0x5, 0x00) => OpCode::new(insn, insn_pc, "SRL", 44, 2), 141 | (0x5, 0x20) => OpCode::new(insn, insn_pc, "SRA", 45, 2), 142 | (0x6, 0x00) => OpCode::new(insn, insn_pc, "OR", 3, 2), 143 | (0x7, 0x00) => OpCode::new(insn, insn_pc, "AND", 4, 2), 144 | (0x0, 0x01) => OpCode::new(insn, insn_pc, "MUL", 32, 1), 145 | (0x1, 0x01) => OpCode::new(insn, insn_pc, "MULH", 33, 1), 146 | (0x2, 0x01) => OpCode::new(insn, insn_pc, "MULSU", 34, 1), 147 | (0x3, 0x01) => OpCode::new(insn, insn_pc, "MULU", 35, 1), 148 | (0x4, 0x01) => OpCode::new(insn, insn_pc, "DIV", 40, 2), 149 | (0x5, 0x01) => OpCode::new(insn, insn_pc, "DIVU", 41, 2), 150 | (0x6, 0x01) => OpCode::new(insn, insn_pc, "REM", 42, 2), 151 | (0x7, 0x01) => OpCode::new(insn, insn_pc, "REMU", 43, 2), 152 | _ => unreachable!(), 153 | }, 154 | 0b0101111 => match (funct3, funct5) { 155 | (0b010, 0b00001) => OpCode::new(insn, insn_pc, "AMOSWAP.W", 0, 1), 156 | (0b010, 0b00010) => OpCode::new(insn, insn_pc, "LR.W", 2, 1), 157 | (0b010, 0b00011) => OpCode::new(insn, insn_pc, "SC.W", 3, 1), 158 | (0b010, 0b01000) => OpCode::new(insn, insn_pc, "AMOOR.W", 0, 1), 159 | (0b010, 0b00000) => OpCode::new(insn, insn_pc, "AMOADD.W", 1, 1), 160 | (0b010, 0b01100) => OpCode::new(insn, insn_pc, "AMOAND.W", 0, 1), 161 | (0b011, 0b00000) => OpCode::new(insn, insn_pc, "AMOADD.D", 1, 1), 162 | (0b011, 0b00001) => OpCode::new(insn, insn_pc, "AMOSWAP.D", 2, 1), 163 | (0b011, 0b00010) => OpCode::new(insn, insn_pc, "LR.D", 3, 1), 164 | (0b011, 0b00011) => OpCode::new(insn, insn_pc, "SC.D", 4, 1), 165 | _ => unreachable!(), 166 | }, 167 | 0b0110111 => OpCode::new(insn, insn_pc, "LUI", 21, 1), 168 | 0b1100011 => match funct3 { 169 | 0x0 => OpCode::new(insn, insn_pc, "BEQ", 13, 1), 170 | 0x1 => OpCode::new(insn, insn_pc, "BNE", 14, 1), 171 | 0x4 => OpCode::new(insn, insn_pc, "BLT", 15, 1), 172 | 0x5 => OpCode::new(insn, insn_pc, "BGE", 16, 1), 173 | 0x6 => OpCode::new(insn, insn_pc, "BLTU", 17, 1), 174 | 0x7 => OpCode::new(insn, insn_pc, "BGEU", 18, 1), 175 | _ => unreachable!(), 176 | }, 177 | 0b1100111 => match funct3 { 178 | 0x0 => OpCode::new(insn, insn_pc, "JALR", 20, 1), 179 | _ => unreachable!(), 180 | }, 181 | 0b0011011 => match funct3 { 182 | 0b000 => OpCode::new(insn, insn_pc, "ADDIW", 0, 1), 183 | _ => unreachable!(), 184 | }, 185 | 0b0111011 => match (funct3, funct7) { 186 | (0b000, 0b0000001) => OpCode::new(insn, insn_pc, "MULW", 0, 1), 187 | (0b111, 0b0000001) => OpCode::new(insn, insn_pc, "REMUW", 1, 1), 188 | _ => unreachable!(), 189 | }, 190 | 0b1101111 => OpCode::new(insn, insn_pc, "JAL", 19, 1), 191 | 0b1110011 => match funct3 { 192 | 0x0 => match (rs2, funct7) { 193 | (0x0, 0x0) => { 194 | OpCode::with_major_minor(insn, insn_pc, "ECALL", MajorType::ECall, 0, 1) 195 | } 196 | (0x1, 0x0) => { 197 | OpCode::with_major_minor(insn, insn_pc, "EBREAK", MajorType::ECall, 1, 1) 198 | } 199 | _ => unreachable!(), 200 | }, 201 | // system call 202 | 0b010 => OpCode::new(insn, insn_pc, "RDTIME", 0, 1), 203 | _ => unreachable!(), 204 | }, 205 | 0b0001111 => OpCode::new(insn, insn_pc, "FENCE", 0, 1), 206 | _ => bail!("Illegal opcode: 0b{opcode:07b}"), 207 | }) 208 | } 209 | 210 | #[allow(dead_code)] 211 | pub fn debug(&self, cycle: usize, insn_pc: u64) -> String { 212 | let mut outputter = InstructionStringOutputter { insn_pc }; 213 | let desc = process_instruction(&mut outputter, self.insn); 214 | format!( 215 | "[{}] pc: 0x{:08x}, insn: 0x{:08x} => {}", 216 | cycle, 217 | insn_pc, 218 | self.insn, 219 | desc.unwrap_or(self.mnemonic.into()) 220 | ) 221 | } 222 | } 223 | 224 | impl core::fmt::Debug for OpCode { 225 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 226 | let mut outputter = InstructionStringOutputter { 227 | insn_pc: self.insn_pc, 228 | }; 229 | let desc = process_instruction(&mut outputter, self.insn); 230 | f.write_fmt(format_args!("{}", desc.unwrap_or(self.mnemonic.into()))) 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /risc0/zkvm/src/serde/err.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 RISC Zero, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use core::fmt::{Display, Formatter}; 16 | 17 | // use alloc::string::{String, ToString}; 18 | 19 | #[derive(Clone, Debug, Eq, PartialEq)] 20 | /// Errors used by Serde 21 | pub enum Error { 22 | /// Found a bool that wasn't 0 or 1 23 | DeserializeBadBool, 24 | /// Found an invalid unicode char 25 | DeserializeBadChar, 26 | /// Found an Option discriminant that wasn't 0 or 1 27 | DeserializeBadOption, 28 | /// Tried to parse invalid utf-8 29 | DeserializeBadUtf8, 30 | /// Unexpected end during deserialization 31 | DeserializeUnexpectedEnd, 32 | /// Not supported 33 | NotSupported, 34 | /// The serialize buffer is full 35 | SerializeBufferFull, 36 | } 37 | 38 | /// A Result type for `risc0_zkvm::serde` operations that can fail 39 | pub type Result = core::result::Result; 40 | 41 | impl Display for Error { 42 | fn fmt(&self, formatter: &mut Formatter) -> core::fmt::Result { 43 | formatter.write_str(match self { 44 | // Self::Custom(msg) => msg, 45 | Self::DeserializeBadBool => "Found a bool that wasn't 0 or 1", 46 | Self::DeserializeBadChar => "Found an invalid unicode char", 47 | Self::DeserializeBadOption => "Found an Option discriminant that wasn't 0 or 1", 48 | Self::DeserializeBadUtf8 => "Tried to parse invalid utf-8", 49 | Self::DeserializeUnexpectedEnd => "Unexpected end during deserialization", 50 | Self::NotSupported => "Not supported", 51 | Self::SerializeBufferFull => "The serialize buffer is full", 52 | }) 53 | } 54 | } 55 | 56 | impl serde::ser::Error for Error { 57 | fn custom(_msg: T) -> Self { 58 | // Error::Custom(msg.to_string()) 59 | Error::NotSupported 60 | } 61 | } 62 | 63 | impl serde::de::Error for Error { 64 | fn custom(_msg: T) -> Self { 65 | // Error::Custom(msg.to_string()) 66 | Error::NotSupported 67 | } 68 | } 69 | 70 | // This is an alias for either std::Error, or serde's no_std error replacement. 71 | impl serde::ser::StdError for Error {} 72 | -------------------------------------------------------------------------------- /risc0/zkvm/src/serde/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 RISC Zero, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Serialization and deserialization tools for the RISC Zero zkVM 16 | //! 17 | //! Data needs to be serialized for transmission between the zkVM host and 18 | //! guest. This module contains tools for this serialization and the 19 | //! corresponding deserialization. 20 | //! 21 | //! On the host side, a serialization function such as [to_vec] should be used 22 | //! when transmitting data to the guest. Similarly, the deserialization function 23 | //! [from_slice] should be used when reading data from the guest. For example: 24 | //! ```rust 25 | //! use risc0_zkvm::serde::{from_slice, to_vec}; 26 | //! let input = 42_u32; 27 | //! let encoded = to_vec(&[input]).unwrap(); 28 | //! let output: u32 = from_slice(&encoded).unwrap(); 29 | //! assert_eq!(input, output); 30 | //! ``` 31 | //! 32 | //! On the guest side, the necessary (de)serialization functionality is 33 | //! included in [`env`] module functions such as [`env::read`] and 34 | //! [`env::commit`], so this crate rarely needs to be directly used in the 35 | //! guest. 36 | //! 37 | //! [`env`]: ../guest/env/index.html 38 | //! [`env::commit`]: ../guest/env/fn.commit.html 39 | //! [`env::read`]: ../guest/env/fn.read.html 40 | 41 | mod deserializer; 42 | mod err; 43 | mod serializer; 44 | 45 | pub use deserializer::{from_slice, Deserializer, WordRead}; 46 | pub use err::{Error, Result}; 47 | pub use serializer::{to_vec, to_vec_with_capacity, Serializer, WordWrite}; 48 | 49 | #[cfg(test)] 50 | mod tests { 51 | use std::collections::HashMap; 52 | 53 | use crate::serde::{from_slice, to_vec}; 54 | 55 | #[test] 56 | fn test_vec_round_trip() { 57 | let input: Vec = vec![1, 2, 3]; 58 | let data = to_vec(&input).unwrap(); 59 | let output: Vec = from_slice(data.as_slice()).unwrap(); 60 | assert_eq!(input, output); 61 | } 62 | 63 | #[test] 64 | fn test_map_round_trip() { 65 | let input: HashMap = 66 | HashMap::from([("foo".into(), 1), ("bar".into(), 2), ("baz".into(), 3)]); 67 | let data = to_vec(&input).unwrap(); 68 | let output: HashMap = from_slice(data.as_slice()).unwrap(); 69 | assert_eq!(input, output); 70 | } 71 | 72 | #[test] 73 | fn test_tuple_round_trip() { 74 | let input: (u32, u64) = (1, 2); 75 | let data = to_vec(&input).unwrap(); 76 | let output: (u32, u64) = from_slice(data.as_slice()).unwrap(); 77 | assert_eq!(input, output); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /risc0/zkvm/src/session.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 RISC Zero, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! This module defines [Session] and [Segment] which provides a way to share 16 | //! execution traces between the execution phase and the proving phase. 17 | 18 | use alloc::collections::BTreeSet; 19 | 20 | use serde::{Deserialize, Serialize}; 21 | 22 | use crate::{exec::SyscallRecord, MemoryImage}; 23 | 24 | /// Indicates how a [Segment] or [Session]'s execution has terminated 25 | #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] 26 | pub enum ExitCode { 27 | /// This indicates that the session limit has been reached. 28 | SessionLimit, 29 | 30 | /// This indicates normal termination of a program with an interior exit 31 | /// code returned from the guest. 32 | Halted(u32), 33 | } 34 | 35 | #[derive(Clone, Default, Serialize, Deserialize, Debug)] 36 | pub struct PageFaults { 37 | pub(crate) reads: BTreeSet, 38 | pub(crate) writes: BTreeSet, 39 | } 40 | 41 | /// The execution trace of a program. 42 | /// 43 | /// The record of memory transactions of an execution that starts from an 44 | /// initial memory image (which includes the starting PC) and proceeds until 45 | /// either a sys_halt or a sys_pause syscall is encountered. This record is 46 | /// stored as a vector of [Segment]s. 47 | #[derive(Serialize, Deserialize)] 48 | pub struct Session { 49 | /// The constituent [Segment]s of the Session. The final [Segment] will have 50 | /// an [ExitCode] of [Halted](ExitCode::Halted), [Paused](ExitCode::Paused), 51 | /// or [SessionLimit](ExitCode::SessionLimit), and all other [Segment]s (if 52 | /// any) will have [ExitCode::SystemSplit]. 53 | pub segments: Vec, 54 | 55 | /// The data publicly committed by the guest program. 56 | // pub journal: Vec, 57 | 58 | /// The [ExitCode] of the session. 59 | pub exit_code: ExitCode, 60 | } 61 | 62 | /// The execution trace of a portion of a program. 63 | /// 64 | /// The record of memory transactions of an execution that starts from an 65 | /// initial memory image, and proceeds until terminated by the system or user. 66 | /// This represents a chunk of execution work that will be proven in a single 67 | /// call to the ZKP system. It does not necessarily represent an entire program; 68 | /// see [Session] for tracking memory transactions until a user-requested 69 | /// termination. 70 | #[derive(Serialize, Deserialize)] 71 | pub struct Segment { 72 | // pub(crate) pre_image: MemoryImage, 73 | // pub(crate) post_image_id: Digest, 74 | pub(crate) pc: u64, 75 | // pub(crate) faults: PageFaults, 76 | // pub(crate) syscalls: Vec, 77 | pub(crate) exit_code: ExitCode, 78 | // The number of cycles in powers of 2. 79 | // pub po2: usize, 80 | } 81 | 82 | impl Session { 83 | /// Construct a new [Session] from its constituent components. 84 | pub fn new(segments: Vec, exit_code: ExitCode) -> Self { 85 | Self { 86 | segments, 87 | // journal, 88 | exit_code, 89 | } 90 | } 91 | } 92 | 93 | impl Segment { 94 | /// Create a new [Segment] from its constituent components. 95 | pub(crate) fn new( 96 | pre_image: MemoryImage, 97 | // post_image_id: Digest, 98 | pc: u64, 99 | // faults: PageFaults, 100 | // syscalls: Vec, 101 | exit_code: ExitCode, 102 | // po2: usize, 103 | ) -> Self { 104 | Self { 105 | // pre_image, 106 | // post_image_id, 107 | pc, 108 | // faults, 109 | // syscalls, 110 | exit_code, 111 | // po2, 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /risc0/zkvm/src/testdata/riscv-tests.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hero78119/risc0-nova/c44038ffcb7d9408d6d7b2f1574e4bf64a2b94f2/risc0/zkvm/src/testdata/riscv-tests.tgz -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly-2022-10-28" 3 | components = [ "rustfmt", "rust-src" ] 4 | targets = [ "wasm32-unknown-unknown" ] 5 | profile = "minimal" 6 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | wrap_comments = true 2 | normalize_comments = true 3 | group_imports = "StdExternalCrate" 4 | imports_granularity = "Crate" 5 | --------------------------------------------------------------------------------