├── .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 |
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 | [](https://crates.io/crates/cargo-risczero) |
124 | | risc0-r0vm | [](https://crates.io/crates/risc0-r0vm) |
125 | | risc0-tools | [](https://crates.io/crates/risc0-tools) |
126 |
127 | ## Rust Libraries
128 |
129 | | crate | [crates.io] | [docs.rs](https://docs.rs) |
130 | | ------------------------ | -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
131 | | risc0-build | [](https://crates.io/crates/risc0-build) | [](https://docs.rs/risc0-build) |
132 | | risc0-build-kernel | [](https://crates.io/crates/risc0-build-kernel) | [](https://docs.rs/risc0-build-kernel) |
133 | | risc0-circuit-rv32im | [](https://crates.io/crates/risc0-circuit-rv32im) | [](https://docs.rs/risc0-circuit-rv32im) |
134 | | risc0-circuit-rv32im-sys | [](https://crates.io/crates/risc0-circuit-rv32im-sys) | [](https://docs.rs/risc0-circuit-rv32im-sys) |
135 | | risc0-core | [](https://crates.io/crates/risc0-core) | [](https://docs.rs/risc0-core) |
136 | | risc0-sys | [](https://crates.io/crates/risc0-sys) | [](https://docs.rs/risc0-sys) |
137 | | risc0-zkp | [](https://crates.io/crates/risc0-zkp) | [](https://docs.rs/risc0-zkp) |
138 | | risc0-zkvm | [](https://crates.io/crates/risc0-zkvm) | [](https://docs.rs/risc0-zkvm) |
139 | | risc0-zkvm-platform | [](https://crates.io/crates/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 |
--------------------------------------------------------------------------------