├── .github └── workflows │ ├── continuous-integration-workflow.yml │ └── pr.yml ├── .gitignore ├── .gitmodules ├── .vscode └── settings.json ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE.md ├── LICENSE-MIT.md ├── README.md ├── audits └── Curta_Plonky2x_Audit_Report_KALOS.md ├── rust-toolchain ├── rustfmt.toml └── starkyx ├── Cargo.toml └── src ├── air ├── curta_air.rs ├── extension │ ├── cubic.rs │ └── mod.rs ├── fibonacci.rs ├── mod.rs ├── opening.rs └── parser.rs ├── chip ├── air.rs ├── arithmetic │ ├── expression.rs │ ├── expression_slice.rs │ └── mod.rs ├── bool.rs ├── builder │ ├── arithmetic.rs │ ├── memory.rs │ ├── mod.rs │ ├── range_check.rs │ └── shared_memory.rs ├── constraint │ └── mod.rs ├── ec │ ├── edwards │ │ ├── add.rs │ │ ├── assert_valid.rs │ │ ├── bigint_operations.rs │ │ ├── ed25519 │ │ │ ├── decompress.rs │ │ │ ├── gadget.rs │ │ │ ├── instruction.rs │ │ │ ├── mod.rs │ │ │ ├── params.rs │ │ │ ├── point.rs │ │ │ └── sqrt.rs │ │ └── mod.rs │ ├── gadget.rs │ ├── instruction_set.rs │ ├── mod.rs │ ├── point.rs │ ├── scalar.rs │ ├── scalar_mul │ │ └── mod.rs │ └── weierstrass │ │ ├── biguint_operations.rs │ │ ├── bn254.rs │ │ ├── group.rs │ │ ├── mod.rs │ │ └── slope.rs ├── field │ ├── add.rs │ ├── constants.rs │ ├── den.rs │ ├── div.rs │ ├── inner_product.rs │ ├── instruction.rs │ ├── mod.rs │ ├── mul.rs │ ├── mul_const.rs │ ├── ops.rs │ ├── parameters.rs │ ├── register.rs │ ├── sub.rs │ └── util.rs ├── instruction │ ├── assign.rs │ ├── bit.rs │ ├── clock.rs │ ├── cycle.rs │ ├── empty.rs │ ├── mod.rs │ └── set.rs ├── memory │ ├── builder.rs │ ├── get.rs │ ├── instruction.rs │ ├── map.rs │ ├── mod.rs │ ├── pointer │ │ ├── accumulate.rs │ │ ├── key.rs │ │ ├── mod.rs │ │ ├── raw.rs │ │ ├── slice.rs │ │ └── typed.rs │ ├── set.rs │ ├── time.rs │ ├── value.rs │ └── watch.rs ├── mod.rs ├── register │ ├── array.rs │ ├── bit.rs │ ├── cell.rs │ ├── cubic.rs │ ├── element.rs │ ├── memory.rs │ ├── mod.rs │ ├── slice.rs │ └── u16.rs ├── table │ ├── accumulator │ │ ├── constraint.rs │ │ ├── mod.rs │ │ └── trace.rs │ ├── bus │ │ ├── channel │ │ │ ├── constraint.rs │ │ │ ├── mod.rs │ │ │ └── trace.rs │ │ ├── global │ │ │ ├── constraint.rs │ │ │ ├── mod.rs │ │ │ └── trace.rs │ │ └── mod.rs │ ├── log_derivative │ │ ├── constraints.rs │ │ ├── entry.rs │ │ ├── mod.rs │ │ └── trace.rs │ ├── lookup │ │ ├── constraint.rs │ │ ├── mod.rs │ │ ├── table │ │ │ ├── constraint.rs │ │ │ ├── mod.rs │ │ │ └── trace.rs │ │ ├── trace.rs │ │ └── values │ │ │ ├── constraint.rs │ │ │ ├── mod.rs │ │ │ └── trace.rs │ ├── mod.rs │ └── powers.rs ├── trace │ ├── data.rs │ ├── generator.rs │ ├── mod.rs │ └── writer │ │ ├── data.rs │ │ ├── mod.rs │ │ ├── public.rs │ │ ├── row.rs │ │ └── window.rs ├── uint │ ├── bytes │ │ ├── bit_operations │ │ │ ├── and.rs │ │ │ ├── mod.rs │ │ │ ├── not.rs │ │ │ ├── rotate.rs │ │ │ ├── shift.rs │ │ │ └── xor.rs │ │ ├── decode.rs │ │ ├── lookup_table │ │ │ ├── builder_operations.rs │ │ │ ├── mod.rs │ │ │ ├── multiplicity_data.rs │ │ │ └── table.rs │ │ ├── mod.rs │ │ ├── operations │ │ │ ├── instruction.rs │ │ │ ├── mod.rs │ │ │ └── value.rs │ │ ├── register.rs │ │ └── util.rs │ ├── mod.rs │ ├── operations │ │ ├── add.rs │ │ ├── and.rs │ │ ├── instruction.rs │ │ ├── mod.rs │ │ ├── not.rs │ │ ├── rotate.rs │ │ ├── shr.rs │ │ └── xor.rs │ ├── register.rs │ └── util.rs └── utils.rs ├── lib.rs ├── machine ├── builder │ ├── mod.rs │ └── ops.rs ├── bytes │ ├── air.rs │ ├── builder.rs │ ├── mod.rs │ ├── ops.rs │ ├── proof.rs │ └── stark.rs ├── ec │ ├── builder.rs │ ├── mod.rs │ └── scalar_mul.rs ├── emulated │ ├── builder.rs │ ├── mod.rs │ ├── proof.rs │ └── stark.rs ├── hash │ ├── blake │ │ ├── blake2b │ │ │ ├── air.rs │ │ │ ├── builder.rs │ │ │ ├── data.rs │ │ │ ├── mod.rs │ │ │ ├── pure.rs │ │ │ ├── register.rs │ │ │ └── utils.rs │ │ └── mod.rs │ ├── mod.rs │ └── sha │ │ ├── algorithm.rs │ │ ├── builder.rs │ │ ├── data.rs │ │ ├── mod.rs │ │ ├── sha256 │ │ ├── air.rs │ │ ├── mod.rs │ │ ├── pure.rs │ │ ├── register.rs │ │ └── util.rs │ │ └── sha512 │ │ ├── air.rs │ │ ├── data.rs │ │ ├── mod.rs │ │ ├── pure.rs │ │ ├── register.rs │ │ └── util.rs ├── mod.rs └── stark │ ├── builder.rs │ └── mod.rs ├── math ├── algebra.rs ├── extension │ ├── cubic │ │ ├── element.rs │ │ ├── extension.rs │ │ ├── mod.rs │ │ └── parameters.rs │ └── mod.rs ├── field.rs ├── goldilocks │ ├── cubic.rs │ └── mod.rs └── mod.rs ├── maybe_rayon.rs ├── plonky2 ├── cubic │ ├── arithmetic_gate.rs │ ├── builder.rs │ ├── mod.rs │ ├── mul_gate.rs │ ├── operations.rs │ └── target.rs ├── field.rs ├── mod.rs ├── parser │ ├── consumer.rs │ ├── global.rs │ └── mod.rs ├── stark │ ├── config.rs │ ├── gadget.rs │ ├── generator │ │ ├── mod.rs │ │ └── simple.rs │ ├── mod.rs │ ├── proof.rs │ ├── prover.rs │ └── verifier.rs └── trace.rs ├── polynomial ├── mod.rs ├── ops.rs └── parser.rs ├── trace ├── generator.rs ├── mod.rs ├── view.rs ├── window.rs └── window_parser.rs └── utils ├── mod.rs ├── serde.rs └── watcher.rs /.github/workflows/continuous-integration-workflow.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: 8 | - "**" 9 | workflow_dispatch: 10 | branches: 11 | - "**" 12 | 13 | jobs: 14 | # test: 15 | # name: Test Suite 16 | # runs-on: ubuntu-latest 17 | # if: "! contains(toJSON(github.event.commits.*.message), '[skip-ci]')" 18 | # steps: 19 | # - name: Checkout sources 20 | # uses: actions/checkout@v2 21 | 22 | # - name: Install nightly toolchain 23 | # id: rustc-toolchain 24 | # uses: actions-rs/toolchain@v1 25 | # with: 26 | # profile: minimal 27 | # toolchain: nightly-2024-02-22 28 | # override: true 29 | 30 | # - name: rust-cache 31 | # uses: actions/cache@v3 32 | # with: 33 | # path: | 34 | # ~/.cargo/bin/ 35 | # ~/.cargo/registry/index/ 36 | # ~/.cargo/registry/cache/ 37 | # ~/.cargo/git/db/ 38 | # target/ 39 | # key: rustc-test-${{ steps.rustc-toolchain.outputs.rustc_hash }}-cargo-${{ hashFiles('**/Cargo.toml') }} 40 | 41 | # - name: Run cargo test 42 | # uses: actions-rs/cargo@v1 43 | # with: 44 | # command: test 45 | # args: --all --release 46 | # env: 47 | # RUSTFLAGS: -Copt-level=3 -Cdebug-assertions -Coverflow-checks=y -Cdebuginfo=0 -Cprefer-dynamic=y 48 | # CARGO_INCREMENTAL: 1 49 | # RUST_BACKTRACE: 1 50 | 51 | lints: 52 | name: Formatting and Clippy 53 | runs-on: ubuntu-latest 54 | if: "! contains(toJSON(github.event.commits.*.message), '[skip-ci]')" 55 | steps: 56 | - name: Checkout sources 57 | uses: actions/checkout@v2 58 | 59 | - name: Install nightly toolchain 60 | id: rustc-toolchain 61 | uses: actions-rs/toolchain@v1 62 | with: 63 | profile: minimal 64 | toolchain: nightly-2024-02-22 65 | override: true 66 | components: rustfmt, clippy 67 | 68 | - name: rust-cache 69 | uses: actions/cache@v3 70 | with: 71 | path: | 72 | ~/.cargo/bin/ 73 | ~/.cargo/registry/index/ 74 | ~/.cargo/registry/cache/ 75 | ~/.cargo/git/db/ 76 | target/ 77 | key: rustc-lints-${{ steps.rustc-toolchain.outputs.rustc_hash }}-cargo-${{ hashFiles('**/Cargo.toml') }} 78 | 79 | - name: Run cargo fmt 80 | uses: actions-rs/cargo@v1 81 | with: 82 | command: fmt 83 | args: --all -- --check 84 | env: 85 | CARGO_INCREMENTAL: 1 86 | 87 | - name: Run cargo clippy 88 | uses: actions-rs/cargo@v1 89 | with: 90 | command: clippy 91 | args: --all-features --all-targets -- -D warnings -A incomplete-features 92 | env: 93 | CARGO_INCREMENTAL: 1 94 | -------------------------------------------------------------------------------- /.github/workflows/pr.yml: -------------------------------------------------------------------------------- 1 | name: "PR" 2 | 3 | on: 4 | pull_request_target: 5 | types: 6 | - opened 7 | - edited 8 | - synchronize 9 | 10 | permissions: 11 | pull-requests: read 12 | 13 | jobs: 14 | main: 15 | name: PR 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: amannn/action-semantic-pull-request@v5 19 | env: 20 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Cargo build 2 | /target 3 | 4 | # Profile-guided optimization 5 | /tmp 6 | pgo-data.profdata 7 | 8 | # MacOS nuisances 9 | .DS_Store 10 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/succinctlabs/starkyx/1644af58bfc7d305c0066597c303a129fec7f427/.gitmodules -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.inlineSuggest.enabled": true, 3 | "[rust]": { 4 | "editor.defaultFormatter": "rust-lang.rust-analyzer", 5 | "editor.formatOnSave": true, 6 | }, 7 | "editor.rulers": [ 8 | 100 9 | ], 10 | "rust-analyzer.linkedProjects": [ 11 | "./starkyx/Cargo.toml", 12 | ], 13 | "rust-analyzer.check.overrideCommand": [ 14 | "cargo", 15 | "clippy", 16 | "--workspace", 17 | "--message-format=json", 18 | "--all-features", 19 | "--all-targets", 20 | "--", 21 | "-A", 22 | "incomplete-features" 23 | ], 24 | "rust-analyzer.runnables.extraEnv": { 25 | "RUST_LOG": "debug", 26 | "RUSTFLAGS": "-Ctarget-cpu=native" 27 | }, 28 | "rust-analyzer.runnables.extraArgs": [ 29 | "--release" 30 | ], 31 | "rust-analyzer.diagnostics.disabled": [ 32 | "unresolved-proc-macro" 33 | ], 34 | } -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["starkyx"] 3 | resolver = "2" 4 | 5 | [profile.release] 6 | opt-level = 3 7 | 8 | #[profile.bench] 9 | #opt-level = 3 10 | 11 | 12 | [patch.crates-io] 13 | starkyx = {path = "starkyx"} -------------------------------------------------------------------------------- /LICENSE-MIT.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2023 Succinct Labs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # StarkyX 2 | 3 | Curta type I-CnAM 40092-IMG 6721-white 4 | 5 | ## Introduction 6 | 7 | StarkyX is a library for writing AIR constraints with composable and typed constraints. Currently, the main purpose of the library is to prove STARK-based accelerators for [Plonky2](https://github.com/mir-protocol/plonky2) circuits. 8 | 9 | Stark proofs and verification are implemented via [Starky](https://github.com/0xPolygonZero/plonky2/tree/main/starky). This repository contains a modified fork of Starky to enable the support a 1-round AIR with random challenges and using the StarkyX AIR constraints. All the cryptographic primitives are imported from the [Plonky2](https://github.com/mir-protocol/plonky2) proving system. 10 | 11 | ## Building 12 | 13 | StarkyX requires the use of the nightly Rust toolchain. To use it by default, run the following command: 14 | 15 | ```bash 16 | rustup override set nightly 17 | ``` 18 | 19 | In the root directory of the project. 20 | 21 | We recommend running the tests using the `--release` flag, as they are quite slow otherwise. 22 | 23 | ```bash 24 | cargo test --release 25 | ``` 26 | 27 | ## Usage 28 | 29 | ## Building an AIR computation using StarkyX 30 | 31 | ## Creating a STARK proof for an AIR computation 32 | 33 | ## Integrating into a Plonky2 circuit 34 | 35 | StarkyX starks can be integrated into a [Plonky2](https://github.com/mir-protocol/plonky2) circuit 36 | 37 | ## Audit 38 | 39 | StarkyX has been audited by [KALOS](https://kalos.xyz). The audit report can be found [here](https://hackmd.io/qS36EcIASx6Gt_2uNwlK4A). This repo was formerly named Curta, in the audit report it is referenced as Curta. 40 | -------------------------------------------------------------------------------- /rust-toolchain: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly-2024-02-22" 3 | components = ["llvm-tools", "rustc-dev"] -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | group_imports = "StdExternalCrate" 2 | imports_granularity = "Module" 3 | unstable_features = true 4 | -------------------------------------------------------------------------------- /starkyx/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "starkyx" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [features] 9 | default = ["plonky2", "parallel", "std", "timing"] 10 | parallel = ["plonky2/parallel", "plonky2_maybe_rayon/parallel"] 11 | std = ["anyhow/std", "plonky2/std", "num/std"] 12 | timing = ["plonky2/timing"] 13 | 14 | [dependencies] 15 | anyhow = { version = "1.0.40", default-features = false } 16 | itertools = { version = "0.10.0", default-features = false } 17 | log = { version = "0.4.14", default-features = false } 18 | plonky2_maybe_rayon = { version = "0.2.0", default-features = false } 19 | plonky2 = { git = "https://github.com/0xPolygonZero/plonky2.git", tag = "v0.2.0", default-features = false, optional = true } 20 | num = { version = "0.4", default-features = false } 21 | rand = "0.8.4" 22 | serde = { version = "1.0", features = ["derive"] } 23 | hex = "0.4.3" 24 | subtle-encoding = "0.5.1" 25 | bincode = "1.3.3" 26 | curve25519-dalek = "4" 27 | env_logger = "0.9.0" 28 | 29 | [dev-dependencies] 30 | plonky2 = { git = "https://github.com/0xPolygonZero/plonky2.git", tag = "v0.2.0", features = [ 31 | "gate_testing", 32 | ] } 33 | env_logger = { version = "0.9.0", default-features = false } 34 | rand = "0.8.4" 35 | num = { version = "0.4", features = ["rand"] } 36 | criterion = { version = "0.4", features = ["html_reports"] } 37 | pprof = { version = "0.11", features = ["criterion", "flamegraph"] } 38 | seq-macro = "0.3.3" 39 | -------------------------------------------------------------------------------- /starkyx/src/air/curta_air.rs: -------------------------------------------------------------------------------- 1 | use super::parser::AirParser; 2 | 3 | pub trait Curta { 4 | type Input; 5 | type Output; 6 | 7 | fn generate_trace(&self, input: &Self::Input) -> Self::Output; 8 | } 9 | -------------------------------------------------------------------------------- /starkyx/src/air/extension/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod cubic; 2 | -------------------------------------------------------------------------------- /starkyx/src/air/fibonacci.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use super::parser::AirParser; 4 | use super::{RAir, RAirData}; 5 | use crate::air::RoundDatum; 6 | use crate::math::prelude::*; 7 | use crate::trace::AirTrace; 8 | 9 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] 10 | pub struct FibonacciAir; 11 | 12 | impl FibonacciAir { 13 | pub fn new() -> Self { 14 | Self {} 15 | } 16 | 17 | pub fn fibonacci(n: usize, x0: F, x1: F) -> F { 18 | (0..n).fold((x0, x1), |x, _| (x.1, x.0 + x.1)).1 19 | } 20 | 21 | pub fn generate_trace(x0: F, x1: F, num_rows: usize) -> AirTrace { 22 | let trace_rows = (0..num_rows) 23 | .scan([x0, x1], |acc, _| { 24 | let tmp = *acc; 25 | acc[0] = tmp[1]; 26 | acc[1] = tmp[0] + tmp[1]; 27 | Some(tmp) 28 | }) 29 | .flatten() 30 | .collect::>(); 31 | assert!(trace_rows.len() == num_rows * 2); 32 | AirTrace::from_rows(trace_rows, 2) 33 | } 34 | } 35 | 36 | impl Default for FibonacciAir { 37 | fn default() -> Self { 38 | Self::new() 39 | } 40 | } 41 | 42 | impl RAirData for FibonacciAir { 43 | fn constraint_degree(&self) -> usize { 44 | 1 45 | } 46 | 47 | fn width(&self) -> usize { 48 | 2 49 | } 50 | 51 | fn round_data(&self) -> Vec { 52 | vec![RoundDatum::new(self.width(), (0, 3), 0)] 53 | } 54 | 55 | fn num_public_inputs(&self) -> usize { 56 | 3 57 | } 58 | } 59 | 60 | impl RAir for FibonacciAir { 61 | fn eval(&self, parser: &mut AP) { 62 | // Check public inputs. 63 | let pis_constraints = [ 64 | parser.sub(parser.local_slice()[0], parser.public_slice()[0]), 65 | parser.sub(parser.local_slice()[1], parser.public_slice()[1]), 66 | // parser.sub(parser.local_slice()[1], parser.global_slice()[2]), 67 | ]; 68 | parser.constraint_first_row(pis_constraints[0]); 69 | parser.constraint_first_row(pis_constraints[1]); 70 | // parser.constraint_last_row(pis_constraints[2]); 71 | 72 | // x0' <- x1 73 | let first_col_constraint = parser.sub(parser.next_slice()[0], parser.local_slice()[1]); 74 | parser.constraint_transition(first_col_constraint); 75 | // x1' <- x0 + x1 76 | let second_col_constraint = { 77 | let tmp = parser.sub(parser.next_slice()[1], parser.local_slice()[0]); 78 | parser.sub(tmp, parser.local_slice()[1]) 79 | }; 80 | parser.constraint_transition(second_col_constraint); 81 | } 82 | 83 | fn eval_global(&self, _parser: &mut AP) {} 84 | } 85 | 86 | #[cfg(test)] 87 | mod tests { 88 | use plonky2::field::goldilocks_field::GoldilocksField; 89 | 90 | use super::*; 91 | use crate::trace::window_parser::TraceWindowParser; 92 | 93 | #[test] 94 | fn test_fibonacci_air() { 95 | type F = GoldilocksField; 96 | 97 | let num_rows = 1 << 5usize; 98 | let air = FibonacciAir::new(); 99 | 100 | let public_inputs = [ 101 | F::ZERO, 102 | F::ONE, 103 | FibonacciAir::fibonacci(num_rows - 1, F::ZERO, F::ONE), 104 | ]; 105 | let trace = FibonacciAir::generate_trace(F::ZERO, F::ONE, num_rows); 106 | 107 | for window in trace.windows() { 108 | assert_eq!(window.local_slice.len(), 2); 109 | let mut window_parser = TraceWindowParser::new(window, &[], &[], &public_inputs); 110 | assert_eq!(window_parser.local_slice().len(), 2); 111 | air.eval(&mut window_parser); 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /starkyx/src/air/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod curta_air; 2 | pub mod extension; 3 | pub mod opening; 4 | pub mod parser; 5 | 6 | #[cfg(test)] 7 | pub mod fibonacci; 8 | 9 | use parser::AirParser; 10 | 11 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 12 | pub struct RoundDatum { 13 | /// The number of columns generated in this round 14 | pub num_columns: usize, 15 | /// The range of global values generated in this round 16 | pub global_values_range: (usize, usize), 17 | /// The number of validator challenges needed after this round 18 | pub num_challenges: usize, 19 | } 20 | 21 | pub trait AirConstraint { 22 | /// Evaluation of the vanishing polynomials. 23 | fn eval(&self, parser: &mut AP); 24 | } 25 | 26 | pub trait RAirData { 27 | fn width(&self) -> usize; 28 | 29 | /// The maximal constraint degree 30 | fn constraint_degree(&self) -> usize; 31 | 32 | /// The data needed for each round 33 | fn round_data(&self) -> Vec; 34 | 35 | fn num_columns(&self) -> usize { 36 | self.round_data().iter().map(|d| d.num_columns).sum() 37 | } 38 | 39 | fn num_public_inputs(&self) -> usize; 40 | 41 | fn num_rounds(&self) -> usize { 42 | self.round_data().len() 43 | } 44 | 45 | fn num_global_values(&self) -> usize { 46 | self.round_data() 47 | .iter() 48 | .map(|d| d.global_values_range.1 - d.global_values_range.0) 49 | .sum() 50 | } 51 | 52 | fn quotient_degree_factor(&self) -> usize { 53 | 1.max(self.constraint_degree() - 1) 54 | } 55 | } 56 | 57 | pub trait RAir: RAirData { 58 | /// Evaluation of the vanishing polynomials. 59 | fn eval(&self, parser: &mut AP); 60 | 61 | // Evaluation of global vanishing constraints 62 | fn eval_global(&self, parser: &mut AP); 63 | } 64 | 65 | impl RoundDatum { 66 | pub fn new( 67 | num_columns: usize, 68 | global_values_range: (usize, usize), 69 | num_challenges: usize, 70 | ) -> Self { 71 | Self { 72 | num_columns, 73 | global_values_range, 74 | num_challenges, 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /starkyx/src/air/opening.rs: -------------------------------------------------------------------------------- 1 | use super::parser::AirParser; 2 | 3 | #[derive(Debug, Clone)] 4 | pub struct AirOpeningSet { 5 | pub local_values: Vec, 6 | pub next_values: Vec, 7 | pub quotient_values: Vec, 8 | } 9 | -------------------------------------------------------------------------------- /starkyx/src/chip/air.rs: -------------------------------------------------------------------------------- 1 | use super::constraint::Constraint; 2 | use super::{AirParameters, Chip}; 3 | use crate::air::parser::AirParser; 4 | use crate::air::{AirConstraint, RAir, RAirData, RoundDatum}; 5 | 6 | impl RAirData for Chip { 7 | /// The maximal constraint degree 8 | fn constraint_degree(&self) -> usize { 9 | 3 10 | } 11 | 12 | /// Columns for each round 13 | fn round_data(&self) -> Vec { 14 | let total = L::num_columns(); 15 | let execution_trace_length = self.execution_trace_length; 16 | let extended_trace_length = total - execution_trace_length; 17 | 18 | if extended_trace_length == 0 { 19 | return vec![RoundDatum::new( 20 | total, 21 | (0, self.num_global_values), 22 | self.num_challenges, 23 | )]; 24 | } 25 | vec![ 26 | RoundDatum::new(execution_trace_length, (0, 0), self.num_challenges), 27 | RoundDatum::new(extended_trace_length, (0, self.num_global_values), 0), 28 | ] 29 | } 30 | 31 | fn num_public_inputs(&self) -> usize { 32 | self.num_public_values 33 | } 34 | 35 | fn width(&self) -> usize { 36 | L::NUM_ARITHMETIC_COLUMNS + L::NUM_FREE_COLUMNS + L::EXTENDED_COLUMNS 37 | } 38 | } 39 | 40 | impl> RAir for Chip 41 | where 42 | Constraint: AirConstraint, 43 | { 44 | fn eval(&self, parser: &mut AP) { 45 | for constraint in self.constraints.iter() { 46 | constraint.eval(parser); 47 | } 48 | } 49 | 50 | fn eval_global(&self, parser: &mut AP) { 51 | for constraint in self.global_constraints.iter() { 52 | constraint.eval(parser); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /starkyx/src/chip/arithmetic/mod.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use self::expression::ArithmeticExpression; 4 | use crate::air::parser::AirParser; 5 | use crate::air::AirConstraint; 6 | 7 | pub mod expression; 8 | pub(crate) mod expression_slice; 9 | 10 | use crate::math::prelude::*; 11 | 12 | #[derive(Clone, Debug, Serialize, Deserialize)] 13 | pub enum ArithmeticConstraint { 14 | First(ArithmeticExpression), 15 | Last(ArithmeticExpression), 16 | Transition(ArithmeticExpression), 17 | All(ArithmeticExpression), 18 | } 19 | 20 | impl> AirConstraint for ArithmeticConstraint { 21 | fn eval(&self, parser: &mut AP) { 22 | match self { 23 | ArithmeticConstraint::First(expression) => { 24 | let constraints = expression.eval(parser); 25 | for constraint in constraints { 26 | parser.constraint_first_row(constraint); 27 | } 28 | } 29 | ArithmeticConstraint::Last(expression) => { 30 | let constraints = expression.eval(parser); 31 | for constraint in constraints { 32 | parser.constraint_last_row(constraint); 33 | } 34 | } 35 | ArithmeticConstraint::Transition(expression) => { 36 | let constraints = expression.eval(parser); 37 | for constraint in constraints { 38 | parser.constraint_transition(constraint); 39 | } 40 | } 41 | ArithmeticConstraint::All(expression) => { 42 | let constraints = expression.eval(parser); 43 | for constraint in constraints { 44 | parser.constraint(constraint); 45 | } 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /starkyx/src/chip/builder/range_check.rs: -------------------------------------------------------------------------------- 1 | use super::AirBuilder; 2 | use crate::chip::arithmetic::expression::ArithmeticExpression; 3 | use crate::chip::register::array::ArrayRegister; 4 | use crate::chip::register::element::ElementRegister; 5 | use crate::chip::register::memory::MemorySlice; 6 | use crate::chip::register::{Register, RegisterSerializable}; 7 | use crate::chip::table::lookup::table::LookupTable; 8 | use crate::chip::table::lookup::values::LookupValues; 9 | use crate::chip::AirParameters; 10 | 11 | impl AirBuilder { 12 | pub(crate) fn arithmetic_range_checks(&mut self) { 13 | let table = self.alloc::(); 14 | 15 | let one = || -> ArithmeticExpression { ArithmeticExpression::one() }; 16 | let zero = || -> ArithmeticExpression { ArithmeticExpression::zero() }; 17 | 18 | // Table constraints 19 | self.assert_expressions_equal_first_row(table.expr(), zero()); 20 | self.assert_expressions_equal_transition(table.expr() + one(), table.next().expr()); 21 | 22 | let values = ArrayRegister::::from_register_unsafe(MemorySlice::Local( 23 | 0, 24 | L::NUM_ARITHMETIC_COLUMNS, 25 | )) 26 | .into_iter() 27 | .chain(self.global_arithmetic.iter().copied()) 28 | .collect::>(); 29 | 30 | let multiplicities = self.alloc_array::(1); 31 | let mut table_data = self.new_lookup(&[table], &multiplicities); 32 | let lookup_values = table_data.register_lookup_values(self, &values); 33 | self.constrain_element_lookup_table(table_data.clone()); 34 | 35 | self.range_data = Some(( 36 | LookupTable::Element(table_data), 37 | LookupValues::Element(lookup_values), 38 | )); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /starkyx/src/chip/builder/shared_memory.rs: -------------------------------------------------------------------------------- 1 | use alloc::sync::Arc; 2 | use std::sync::Mutex; 3 | 4 | use crate::chip::register::memory::MemorySlice; 5 | 6 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] 7 | pub struct SharedMemeoryCore { 8 | pub global_index: usize, 9 | pub public_index: usize, 10 | pub challenge_index: usize, 11 | } 12 | 13 | #[derive(Debug, Clone)] 14 | pub struct SharedMemory(Arc>); 15 | 16 | impl SharedMemory { 17 | #[inline] 18 | pub fn new() -> Self { 19 | Self(Arc::new(Mutex::new(SharedMemeoryCore { 20 | global_index: 0, 21 | public_index: 0, 22 | challenge_index: 0, 23 | }))) 24 | } 25 | 26 | #[inline] 27 | pub fn global_index(&self) -> usize { 28 | self.0.lock().unwrap().global_index 29 | } 30 | 31 | #[inline] 32 | pub fn challenge_index(&self) -> usize { 33 | self.0.lock().unwrap().challenge_index 34 | } 35 | 36 | #[inline] 37 | pub fn public_index(&self) -> usize { 38 | self.0.lock().unwrap().public_index 39 | } 40 | 41 | #[inline] 42 | pub fn get_global_memory(&self, size: usize) -> MemorySlice { 43 | let mut core = self.0.lock().unwrap(); 44 | let register = MemorySlice::Global(core.global_index, size); 45 | core.global_index += size; 46 | register 47 | } 48 | 49 | #[inline] 50 | pub fn get_public_memory(&self, size: usize) -> MemorySlice { 51 | let mut core = self.0.lock().unwrap(); 52 | let register = MemorySlice::Public(core.public_index, size); 53 | core.public_index += size; 54 | register 55 | } 56 | 57 | #[inline] 58 | pub fn get_challenge_memory(&self, size: usize) -> MemorySlice { 59 | let mut core = self.0.lock().unwrap(); 60 | let register = MemorySlice::Challenge(core.challenge_index, size); 61 | core.challenge_index += size; 62 | register 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /starkyx/src/chip/ec/edwards/bigint_operations.rs: -------------------------------------------------------------------------------- 1 | use super::{EdwardsCurve, EdwardsParameters}; 2 | use crate::chip::ec::point::AffinePoint; 3 | use crate::chip::field::parameters::FieldParameters; 4 | 5 | impl AffinePoint> { 6 | pub(crate) fn ed_add( 7 | &self, 8 | other: &AffinePoint>, 9 | ) -> AffinePoint> { 10 | let p = E::BaseField::modulus(); 11 | let x_3n = (&self.x * &other.y + &self.y * &other.x) % &p; 12 | let y_3n = (&self.y * &other.y + &self.x * &other.x) % &p; 13 | 14 | let all_xy = (&self.x * &self.y * &other.x * &other.y) % &p; 15 | let d = E::d_biguint(); 16 | let dxy = (d * &all_xy) % &p; 17 | let den_x = ((1u32 + &dxy) % &p).modpow(&(&p - 2u32), &p); 18 | let den_y = ((1u32 + &p - &dxy) % &p).modpow(&(&p - 2u32), &p); 19 | 20 | let x_3 = (&x_3n * &den_x) % &p; 21 | let y_3 = (&y_3n * &den_y) % &p; 22 | 23 | AffinePoint::new(x_3, y_3) 24 | } 25 | 26 | pub(crate) fn ed_double(&self) -> AffinePoint> { 27 | self.ed_add(self) 28 | } 29 | } 30 | 31 | #[cfg(test)] 32 | mod tests { 33 | 34 | use num::bigint::RandBigInt; 35 | use num::BigUint; 36 | use rand::thread_rng; 37 | 38 | use super::*; 39 | use crate::chip::ec::edwards::ed25519::params::{Ed25519, Ed25519Parameters}; 40 | use crate::chip::ec::{EllipticCurve, EllipticCurveParameters}; 41 | 42 | #[test] 43 | fn test_bigint_ed_add() { 44 | type E = Ed25519; 45 | let netural = E::neutral(); 46 | let base = E::ec_generator(); 47 | 48 | assert_eq!(&base + &netural, base); 49 | assert_eq!(&netural + &base, base); 50 | assert_eq!(&netural + &netural, netural); 51 | } 52 | 53 | #[test] 54 | fn test_biguint_scalar_mul() { 55 | type E = Ed25519; 56 | let base = E::ec_generator(); 57 | 58 | let d = Ed25519Parameters::d_biguint(); 59 | let p = ::BaseField::modulus(); 60 | assert_eq!((d * 121666u32) % &p, (&p - 121665u32) % &p); 61 | 62 | let mut rng = thread_rng(); 63 | for _ in 0..10 { 64 | let x = rng.gen_biguint(24); 65 | let y = rng.gen_biguint(25); 66 | 67 | let x_base = &base * &x; 68 | let y_x_base = &x_base * &y; 69 | let xy = &x * &y; 70 | let xy_base = &base * &xy; 71 | assert_eq!(y_x_base, xy_base); 72 | } 73 | 74 | let order = BigUint::from(2u32).pow(252) 75 | + BigUint::from(27742317777372353535851937790883648493u128); 76 | assert_eq!(base, &base + &(&base * &order)); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /starkyx/src/chip/ec/edwards/ed25519/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod decompress; 2 | pub mod gadget; 3 | pub mod instruction; 4 | pub mod params; 5 | pub mod point; 6 | pub mod sqrt; 7 | -------------------------------------------------------------------------------- /starkyx/src/chip/ec/edwards/ed25519/params.rs: -------------------------------------------------------------------------------- 1 | use num::{BigUint, Num, One}; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | use crate::chip::ec::edwards::{EdwardsCurve, EdwardsParameters}; 5 | use crate::chip::ec::EllipticCurveParameters; 6 | use crate::chip::field::parameters::{FieldParameters, MAX_NB_LIMBS}; 7 | 8 | pub type Ed25519 = EdwardsCurve; 9 | 10 | #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] 11 | pub struct Ed25519Parameters; 12 | 13 | #[derive(Debug, Default, Clone, Copy, PartialEq, Serialize, Deserialize)] 14 | pub struct Ed25519BaseField; 15 | 16 | impl FieldParameters for Ed25519BaseField { 17 | const NB_BITS_PER_LIMB: usize = 16; 18 | const NB_LIMBS: usize = 16; 19 | const NB_WITNESS_LIMBS: usize = 2 * Self::NB_LIMBS - 2; 20 | const MODULUS: [u16; MAX_NB_LIMBS] = [ 21 | 65517, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 22 | 65535, 65535, 32767, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23 | ]; 24 | const WITNESS_OFFSET: usize = 1usize << 21; 25 | 26 | fn modulus() -> BigUint { 27 | (BigUint::one() << 255) - BigUint::from(19u32) 28 | } 29 | } 30 | 31 | #[derive(Debug, Default, Clone, Copy, PartialEq, Serialize, Deserialize)] 32 | pub struct Ed25519ScalarField; 33 | 34 | impl FieldParameters for Ed25519ScalarField { 35 | const NB_BITS_PER_LIMB: usize = 16; 36 | const NB_LIMBS: usize = 16; 37 | const NB_WITNESS_LIMBS: usize = 2 * Self::NB_LIMBS - 2; 38 | const MODULUS: [u16; MAX_NB_LIMBS] = [ 39 | 54253, 23797, 25370, 22546, 40150, 41719, 63966, 5342, 0, 0, 0, 0, 0, 0, 0, 4096, 0, 0, 0, 40 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41 | ]; 42 | const WITNESS_OFFSET: usize = 1usize << 21; 43 | 44 | fn modulus() -> BigUint { 45 | (BigUint::one() << 252) + BigUint::from(27742317777372353535851937790883648493u128) 46 | } 47 | } 48 | 49 | impl EllipticCurveParameters for Ed25519Parameters { 50 | type BaseField = Ed25519BaseField; 51 | } 52 | 53 | impl EdwardsParameters for Ed25519Parameters { 54 | const D: [u16; MAX_NB_LIMBS] = [ 55 | 30883, 4953, 19914, 30187, 55467, 16705, 2637, 112, 59544, 30585, 16505, 36039, 65139, 56 | 11119, 27886, 20995, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57 | ]; 58 | 59 | fn prime_group_order() -> BigUint { 60 | BigUint::from(2u32).pow(252) + BigUint::from(27742317777372353535851937790883648493u128) 61 | } 62 | 63 | fn generator() -> (BigUint, BigUint) { 64 | let x = BigUint::from_str_radix( 65 | "15112221349535400772501151409588531511454012693041857206046113283949847762202", 66 | 10, 67 | ) 68 | .unwrap(); 69 | let y = BigUint::from_str_radix( 70 | "46316835694926478169428394003475163141307993866256225615783033603165251855960", 71 | 10, 72 | ) 73 | .unwrap(); 74 | (x, y) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /starkyx/src/chip/ec/edwards/ed25519/point.rs: -------------------------------------------------------------------------------- 1 | use super::params::Ed25519BaseField; 2 | use crate::chip::field::register::FieldRegister; 3 | use crate::chip::register::bit::BitRegister; 4 | 5 | pub struct CompressedPointRegister { 6 | pub sign: BitRegister, 7 | pub y: FieldRegister, 8 | } 9 | 10 | impl CompressedPointRegister { 11 | pub fn new(sign: BitRegister, y: FieldRegister) -> Self { 12 | Self { sign, y } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /starkyx/src/chip/ec/point.rs: -------------------------------------------------------------------------------- 1 | use core::ops::{Add, Neg}; 2 | 3 | use num::BigUint; 4 | use serde::{Deserialize, Serialize}; 5 | 6 | use super::EllipticCurve; 7 | use crate::chip::field::register::FieldRegister; 8 | 9 | #[derive(Debug, Clone, PartialEq, Eq)] 10 | pub struct AffinePoint { 11 | pub x: BigUint, 12 | pub y: BigUint, 13 | _marker: std::marker::PhantomData, 14 | } 15 | 16 | impl AffinePoint { 17 | #[allow(dead_code)] 18 | pub fn new(x: BigUint, y: BigUint) -> Self { 19 | Self { 20 | x, 21 | y, 22 | _marker: std::marker::PhantomData, 23 | } 24 | } 25 | } 26 | 27 | #[derive(Debug, Clone, Copy, Serialize, Deserialize)] 28 | pub struct AffinePointRegister { 29 | pub x: FieldRegister, 30 | pub y: FieldRegister, 31 | } 32 | 33 | impl AffinePointRegister { 34 | pub fn new(x: FieldRegister, y: FieldRegister) -> Self { 35 | Self { x, y } 36 | } 37 | } 38 | 39 | impl Add<&AffinePoint> for &AffinePoint { 40 | type Output = AffinePoint; 41 | 42 | fn add(self, other: &AffinePoint) -> AffinePoint { 43 | E::ec_add(self, other) 44 | } 45 | } 46 | 47 | impl Add> for AffinePoint { 48 | type Output = AffinePoint; 49 | 50 | fn add(self, other: AffinePoint) -> AffinePoint { 51 | &self + &other 52 | } 53 | } 54 | 55 | impl Add<&AffinePoint> for AffinePoint { 56 | type Output = AffinePoint; 57 | 58 | fn add(self, other: &AffinePoint) -> AffinePoint { 59 | &self + other 60 | } 61 | } 62 | 63 | impl Neg for &AffinePoint { 64 | type Output = AffinePoint; 65 | 66 | fn neg(self) -> AffinePoint { 67 | E::ec_neg(self) 68 | } 69 | } 70 | 71 | impl Neg for AffinePoint { 72 | type Output = AffinePoint; 73 | 74 | fn neg(self) -> AffinePoint { 75 | -&self 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /starkyx/src/chip/ec/scalar_mul/mod.rs: -------------------------------------------------------------------------------- 1 | use core::ops::Mul; 2 | 3 | use num::{BigUint, One}; 4 | 5 | use super::point::AffinePoint; 6 | use super::EllipticCurve; 7 | use crate::chip::utils::biguint_to_bits_le; 8 | 9 | impl AffinePoint { 10 | pub fn scalar_mul(&self, scalar: &BigUint) -> Self { 11 | let power_two_modulus = BigUint::one() << E::nb_scalar_bits(); 12 | let scalar = scalar % &power_two_modulus; 13 | let mut result = E::ec_neutral(); 14 | let mut temp = self.clone(); 15 | let bits = biguint_to_bits_le(&scalar, E::nb_scalar_bits()); 16 | for bit in bits { 17 | if bit { 18 | result = result.map_or_else(|| Some(temp.clone()), |r| Some(&r + &temp)); 19 | } 20 | temp = &temp + &temp; 21 | } 22 | result.expect("Scalar multiplication failed") 23 | } 24 | } 25 | 26 | impl Mul<&BigUint> for &AffinePoint { 27 | type Output = AffinePoint; 28 | 29 | fn mul(self, scalar: &BigUint) -> AffinePoint { 30 | self.scalar_mul(scalar) 31 | } 32 | } 33 | 34 | impl Mul for &AffinePoint { 35 | type Output = AffinePoint; 36 | 37 | fn mul(self, scalar: BigUint) -> AffinePoint { 38 | self.scalar_mul(&scalar) 39 | } 40 | } 41 | 42 | impl Mul for AffinePoint { 43 | type Output = AffinePoint; 44 | 45 | fn mul(self, scalar: BigUint) -> AffinePoint { 46 | self.scalar_mul(&scalar) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /starkyx/src/chip/ec/weierstrass/biguint_operations.rs: -------------------------------------------------------------------------------- 1 | use num::BigUint; 2 | 3 | use super::{SWCurve, WeierstrassParameters}; 4 | use crate::chip::ec::point::AffinePoint; 5 | use crate::chip::field::parameters::FieldParameters; 6 | use crate::chip::utils::biguint_to_bits_le; 7 | 8 | impl AffinePoint> { 9 | pub fn sw_scalar_mul(&self, scalar: &BigUint) -> Self { 10 | let mut result: Option>> = None; 11 | let mut temp = self.clone(); 12 | let bits = biguint_to_bits_le(scalar, E::nb_scalar_bits()); 13 | for bit in bits { 14 | if bit { 15 | result = result.map(|r| r.sw_add(&temp)).or(Some(temp.clone())); 16 | } 17 | temp = temp.sw_double(); 18 | } 19 | result.unwrap() 20 | } 21 | } 22 | 23 | impl AffinePoint> { 24 | pub fn sw_add(&self, other: &AffinePoint>) -> AffinePoint> { 25 | let p = E::BaseField::modulus(); 26 | let slope_numerator = (&p + &other.y - &self.y) % &p; 27 | let slope_denominator = (&p + &other.x - &self.x) % &p; 28 | let slope_denom_inverse = slope_denominator.modpow(&(&p - 2u32), &p); 29 | let slope = (slope_numerator * &slope_denom_inverse) % &p; 30 | 31 | let x_3n = (&slope * &slope + &p + &p - &self.x - &other.x) % &p; 32 | let y_3n = (&slope * &(&p + &self.x - &x_3n) + &p - &self.y) % &p; 33 | 34 | AffinePoint::new(x_3n, y_3n) 35 | } 36 | 37 | pub fn sw_double(&self) -> AffinePoint> { 38 | let p = E::BaseField::modulus(); 39 | let a = E::a_int(); 40 | let slope_numerator = (&a + &(&self.x * &self.x) * 3u32) % &p; 41 | 42 | let slope_denominator = (&self.y * 2u32) % &p; 43 | let slope_denom_inverse = slope_denominator.modpow(&(&p - 2u32), &p); 44 | let slope = (slope_numerator * &slope_denom_inverse) % &p; 45 | 46 | let x_3n = (&slope * &slope + &p + &p - &self.x - &self.x) % &p; 47 | 48 | let y_3n = (&slope * &(&p + &self.x - &x_3n) + &p - &self.y) % &p; 49 | 50 | AffinePoint::new(x_3n, y_3n) 51 | } 52 | } 53 | 54 | #[cfg(test)] 55 | mod tests { 56 | 57 | use num::bigint::RandBigInt; 58 | use rand::thread_rng; 59 | 60 | use crate::chip::ec::weierstrass::bn254::Bn254; 61 | 62 | #[test] 63 | fn test_weierstrass_biguint_scalar_mul() { 64 | type E = Bn254; 65 | let base = E::generator(); 66 | 67 | let mut rng = thread_rng(); 68 | for _ in 0..10 { 69 | let x = rng.gen_biguint(24); 70 | let y = rng.gen_biguint(25); 71 | 72 | let x_base = base.sw_scalar_mul(&x); 73 | let y_x_base = x_base.sw_scalar_mul(&y); 74 | let xy = &x * &y; 75 | let xy_base = base.sw_scalar_mul(&xy); 76 | assert_eq!(y_x_base, xy_base); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /starkyx/src/chip/ec/weierstrass/bn254.rs: -------------------------------------------------------------------------------- 1 | use num::{BigUint, Num, Zero}; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | use super::{SWCurve, WeierstrassParameters}; 5 | use crate::chip::ec::EllipticCurveParameters; 6 | use crate::chip::field::parameters::FieldParameters; 7 | 8 | #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] 9 | /// Bn254 curve parameter 10 | pub struct Bn254Parameters; 11 | 12 | pub type Bn254 = SWCurve; 13 | 14 | #[derive(Debug, Default, Clone, Copy, PartialEq, Serialize, Deserialize)] 15 | /// Bn254 base field parameter 16 | pub struct Bn254BaseField; 17 | 18 | impl FieldParameters for Bn254BaseField { 19 | const NB_BITS_PER_LIMB: usize = 16; 20 | 21 | const NB_LIMBS: usize = 16; 22 | 23 | const NB_WITNESS_LIMBS: usize = 2 * Self::NB_LIMBS - 2; 24 | 25 | // Base field modulus: 26 | // 21888242871839275222246405745257275088696311157297823662689037894645226208583 27 | const MODULUS: [u16; crate::chip::field::parameters::MAX_NB_LIMBS] = [ 28 | 64839, 55420, 35862, 15392, 51853, 26737, 27281, 38785, 22621, 33153, 17846, 47184, 41001, 29 | 57649, 20082, 12388, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30 | ]; 31 | 32 | const WITNESS_OFFSET: usize = 1usize << 20; 33 | } 34 | 35 | impl EllipticCurveParameters for Bn254Parameters { 36 | type BaseField = Bn254BaseField; 37 | } 38 | 39 | impl WeierstrassParameters for Bn254Parameters { 40 | const A: [u16; crate::chip::field::parameters::MAX_NB_LIMBS] = [ 41 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42 | 0, 0, 43 | ]; 44 | 45 | const B: [u16; crate::chip::field::parameters::MAX_NB_LIMBS] = [ 46 | 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47 | 0, 0, 48 | ]; 49 | fn generator() -> (BigUint, BigUint) { 50 | let x = BigUint::from(1u32); 51 | let y = BigUint::from(2u32); 52 | (x, y) 53 | } 54 | 55 | fn prime_group_order() -> num::BigUint { 56 | BigUint::from_str_radix( 57 | "21888242871839275222246405745257275088548364400416034343698204186575808495617", 58 | 10, 59 | ) 60 | .unwrap() 61 | } 62 | 63 | fn a_int() -> BigUint { 64 | BigUint::zero() 65 | } 66 | 67 | fn b_int() -> BigUint { 68 | BigUint::from(3u32) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /starkyx/src/chip/ec/weierstrass/slope.rs: -------------------------------------------------------------------------------- 1 | use super::{SWCurve, WeierstrassParameters}; 2 | use crate::chip::builder::AirBuilder; 3 | use crate::chip::ec::point::AffinePointRegister; 4 | use crate::chip::field::instruction::FromFieldInstruction; 5 | use crate::chip::field::register::FieldRegister; 6 | use crate::chip::AirParameters; 7 | 8 | impl AirBuilder { 9 | /// Given two points `p` and `q`, compute the slope of the line passing through them. 10 | /// 11 | /// The slope is given by the formula `(q.y - p.y) / (q.x - p.x)`. This function assumes that 12 | /// `p` and `q` are different points 13 | pub(crate) fn sw_slope_different( 14 | &mut self, 15 | p: &AffinePointRegister>, 16 | q: &AffinePointRegister>, 17 | ) -> FieldRegister 18 | where 19 | L::Instruction: FromFieldInstruction, 20 | { 21 | let (x_1, y_1) = (p.x, p.y); 22 | let (x_2, y_2) = (q.x, q.y); 23 | 24 | let slope_numerator = self.fp_sub(&y_2, &y_1); 25 | let slope_denominator = self.fp_sub(&x_2, &x_1); 26 | 27 | self.fp_div(&slope_numerator, &slope_denominator) 28 | } 29 | 30 | /// Given a point `p`, compute the slope of the tangent line at `p`. 31 | /// 32 | /// The slope is given by the formula `(3 * p.x^2 + a) / (2 * p.y)`. 33 | pub(crate) fn sw_tangent( 34 | &mut self, 35 | p: &AffinePointRegister>, 36 | a: &FieldRegister, 37 | three: &FieldRegister, 38 | ) -> FieldRegister 39 | where 40 | L::Instruction: FromFieldInstruction, 41 | { 42 | let (x_1, y_1) = (p.x, p.y); 43 | 44 | let x_1_sq = self.fp_mul(&x_1, &x_1); 45 | let x_1_sq_3 = self.fp_mul(&x_1_sq, three); 46 | let slope_numerator = self.fp_add(&x_1_sq_3, a); 47 | let slope_denominator = self.fp_add(&y_1, &y_1); 48 | 49 | self.fp_div(&slope_numerator, &slope_denominator) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /starkyx/src/chip/field/constants.rs: -------------------------------------------------------------------------------- 1 | use num::{BigUint, One, Zero}; 2 | 3 | use super::parameters::FieldParameters; 4 | use super::register::FieldRegister; 5 | use crate::chip::builder::AirBuilder; 6 | use crate::chip::AirParameters; 7 | use crate::polynomial::Polynomial; 8 | 9 | impl AirBuilder { 10 | pub fn fp_constant(&mut self, num: &BigUint) -> FieldRegister

{ 11 | let poly = 12 | Polynomial::::from_biguint_field(num, P::NB_BITS_PER_LIMB, P::NB_LIMBS); 13 | 14 | self.constant(&poly) 15 | } 16 | 17 | pub fn fp_zero(&mut self) -> FieldRegister

{ 18 | self.fp_constant(&BigUint::zero()) 19 | } 20 | 21 | pub fn fp_one(&mut self) -> FieldRegister

{ 22 | self.fp_constant(&BigUint::one()) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /starkyx/src/chip/field/mod.rs: -------------------------------------------------------------------------------- 1 | //! Implements field arithmetic for any field, using a technique from Polygon Zero. 2 | //! Reference: https://github.com/mir-protocol/plonky2/blob/main/evm/src/arithmetic/addcy.rs 3 | //! 4 | //! We want to compute a + b = result mod p. In the integers, this is equivalent to witnessing some 5 | //! carry such that: 6 | //! 7 | //! a + b - result - carry * p = 0. 8 | //! 9 | //! Let us encode the integers as polynomials in the Goldilocks field, where each coefficient is 10 | //! at most 16 bits. In other words, the integers are encoded as an array of little-endian base 16 11 | //! limbs. We can then write the above equation as: 12 | //! 13 | //! a(x) + b(x) - result(x) - carry(x) * p(x) 14 | //! 15 | //! where the polynomial should evaluate to 0 if x = 2^16. To prove that the polynomial has a root 16 | //! at 2^16, we can have the prover witness a polynomial `w(x)` such that the above polynomial 17 | //! is divisble by (x - 2^16): 18 | //! 19 | //! a(x) + b(x) - result(x) - carry(x) * p(x) - (x - 2^16) * w(x) = 0 20 | //! 21 | //! Thus, if we can prove that above polynomial is 0, we can conclude that the addition has been 22 | //! computed correctly. Note that this relies on the fact that any quadratic sum of a sufficiently 23 | //! small number of terms (i.e., less than 2^32 terms) will not overflow in the Goldilocks field. 24 | //! Furthermore, one must be careful to ensure that all polynomials except w(x) are range checked 25 | //! in [0, 2^16). 26 | //! 27 | //! This technique generalizes for any quadratic sum with a "small" number of terms to avoid 28 | //! overflow. 29 | 30 | pub mod add; 31 | pub mod constants; 32 | pub mod den; 33 | pub mod div; 34 | pub mod inner_product; 35 | pub mod instruction; 36 | pub mod mul; 37 | pub mod mul_const; 38 | pub mod ops; 39 | pub mod parameters; 40 | pub mod register; 41 | pub mod sub; 42 | mod util; 43 | -------------------------------------------------------------------------------- /starkyx/src/chip/field/ops.rs: -------------------------------------------------------------------------------- 1 | use super::instruction::FromFieldInstruction; 2 | use super::parameters::FieldParameters; 3 | use super::register::FieldRegister; 4 | use crate::machine::builder::ops::{Add, Div, Mul, One, Sub, Zero}; 5 | use crate::machine::builder::Builder; 6 | 7 | impl Add for FieldRegister

8 | where 9 | B::Instruction: FromFieldInstruction

, 10 | { 11 | type Output = Self; 12 | 13 | fn add(self, rhs: Self, builder: &mut B) -> Self::Output { 14 | builder.api().fp_add(&self, &rhs) 15 | } 16 | } 17 | 18 | impl Sub for FieldRegister

19 | where 20 | B::Instruction: FromFieldInstruction

, 21 | { 22 | type Output = Self; 23 | 24 | fn sub(self, rhs: Self, builder: &mut B) -> Self::Output { 25 | builder.api().fp_sub(&self, &rhs) 26 | } 27 | } 28 | 29 | impl Mul for FieldRegister

30 | where 31 | B::Instruction: FromFieldInstruction

, 32 | { 33 | type Output = Self; 34 | 35 | fn mul(self, rhs: Self, builder: &mut B) -> Self::Output { 36 | builder.api().fp_mul(&self, &rhs) 37 | } 38 | } 39 | 40 | impl Div for FieldRegister

41 | where 42 | B::Instruction: FromFieldInstruction

, 43 | { 44 | type Output = Self; 45 | 46 | fn div(self, rhs: Self, builder: &mut B) -> Self::Output { 47 | builder.api().fp_div(&self, &rhs) 48 | } 49 | } 50 | 51 | impl Zero for FieldRegister

52 | where 53 | B::Instruction: FromFieldInstruction

, 54 | { 55 | type Output = Self; 56 | 57 | fn zero(builder: &mut B) -> Self::Output { 58 | builder.api().fp_zero() 59 | } 60 | } 61 | 62 | impl One for FieldRegister

63 | where 64 | B::Instruction: FromFieldInstruction

, 65 | { 66 | type Output = Self; 67 | 68 | fn one(builder: &mut B) -> Self::Output { 69 | builder.api().fp_one() 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /starkyx/src/chip/field/parameters.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::Debug; 2 | 3 | use num::bigint::RandBigInt; 4 | use num::{BigUint, Zero}; 5 | use rand::rngs::OsRng; 6 | use serde::de::DeserializeOwned; 7 | use serde::Serialize; 8 | 9 | pub const MAX_NB_LIMBS: usize = 32; 10 | pub const LIMB: u32 = 2u32.pow(16); 11 | 12 | pub trait FieldParameters: 13 | Send + Sync + Copy + 'static + Debug + Serialize + DeserializeOwned + Default 14 | { 15 | const NB_BITS_PER_LIMB: usize; 16 | const NB_LIMBS: usize; 17 | const NB_WITNESS_LIMBS: usize; 18 | const MODULUS: [u16; MAX_NB_LIMBS]; 19 | const WITNESS_OFFSET: usize; 20 | 21 | fn modulus() -> BigUint { 22 | let mut modulus = BigUint::zero(); 23 | for (i, limb) in Self::MODULUS.iter().enumerate() { 24 | modulus += BigUint::from(*limb) << (16 * i); 25 | } 26 | modulus 27 | } 28 | 29 | fn nb_bits() -> usize { 30 | Self::NB_BITS_PER_LIMB * Self::NB_LIMBS 31 | } 32 | 33 | fn rand() -> BigUint { 34 | OsRng.gen_biguint_below(&Self::modulus()) 35 | } 36 | } 37 | 38 | #[cfg(test)] 39 | pub mod tests { 40 | use num::One; 41 | use serde::Deserialize; 42 | 43 | use super::*; 44 | 45 | #[derive(Debug, Default, Clone, Copy, PartialEq, Serialize, Deserialize)] 46 | pub struct Fp25519; 47 | 48 | impl FieldParameters for Fp25519 { 49 | const NB_BITS_PER_LIMB: usize = 16; 50 | const NB_LIMBS: usize = 16; 51 | const NB_WITNESS_LIMBS: usize = 2 * Self::NB_LIMBS - 2; 52 | const MODULUS: [u16; MAX_NB_LIMBS] = [ 53 | 65517, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 54 | 65535, 65535, 65535, 32767, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55 | ]; 56 | const WITNESS_OFFSET: usize = 1usize << 21; 57 | 58 | fn modulus() -> BigUint { 59 | (BigUint::one() << 255) - BigUint::from(19u32) 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /starkyx/src/chip/field/util.rs: -------------------------------------------------------------------------------- 1 | use num::BigUint; 2 | 3 | use super::parameters::FieldParameters; 4 | use crate::math::prelude::*; 5 | use crate::polynomial::parser::PolynomialParser; 6 | use crate::polynomial::Polynomial; 7 | 8 | pub fn eval_field_operation( 9 | parser: &mut AP, 10 | p_vanishing: &Polynomial, 11 | p_witness_low: &Polynomial, 12 | p_witness_high: &Polynomial, 13 | ) { 14 | // Reconstruct and shift back the witness polynomial 15 | let limb_field = AP::Field::from_canonical_u32(2u32.pow(16)); 16 | let limb = parser.constant(limb_field); 17 | 18 | let p_witness_high_mul_limb = parser.poly_scalar_mul(p_witness_high, &limb); 19 | let p_witness_shifted = parser.poly_add(p_witness_low, &p_witness_high_mul_limb); 20 | 21 | // Shift down the witness polynomial. Shifting is needed to range check that each 22 | // coefficient w_i of the witness polynomial satisfies |w_i| < 2^20. 23 | let offset = AP::Field::from_canonical_u32(P::WITNESS_OFFSET as u32); 24 | let offset = parser.constant(offset); 25 | let p_witness = parser.poly_scalar_sub(&p_witness_shifted, &offset); 26 | 27 | // Multiply by (x-2^16) and make the constraint 28 | let root_monomial = Polynomial::from_coefficients(vec![-limb_field, AP::Field::ONE]); 29 | let p_witness_mul_root = parser.poly_mul_poly_const(&p_witness, &root_monomial); 30 | 31 | let constraints = parser.poly_sub(p_vanishing, &p_witness_mul_root); 32 | for constr in constraints.coefficients { 33 | parser.constraint(constr); 34 | } 35 | } 36 | 37 | pub fn modulus_field_iter() -> impl Iterator { 38 | P::MODULUS 39 | .into_iter() 40 | .map(|x| F::from_canonical_u16(x)) 41 | .take(P::NB_LIMBS) 42 | } 43 | 44 | #[inline] 45 | pub fn compute_root_quotient_and_shift( 46 | p_vanishing: &Polynomial, 47 | offset: usize, 48 | ) -> Vec { 49 | // Evaluate the vanishing polynomial at x = 2^16. 50 | let p_vanishing_eval = p_vanishing 51 | .coefficients() 52 | .iter() 53 | .enumerate() 54 | .map(|(i, x)| F::from_noncanonical_biguint(BigUint::from(2u32).pow(16 * i as u32)) * *x) 55 | .sum::(); 56 | debug_assert_eq!(p_vanishing_eval, F::ZERO); 57 | 58 | // Compute the witness polynomial by witness(x) = vanishing(x) / (x - 2^16). 59 | let root_monomial = F::from_canonical_u32(2u32.pow(16)); 60 | let p_quotient = p_vanishing.root_quotient(root_monomial); 61 | debug_assert_eq!(p_quotient.degree(), p_vanishing.degree() - 1); 62 | 63 | // Sanity Check #1: For all i, |w_i| < 2^20 to prevent overflows. 64 | let offset_u64 = offset as u64; 65 | 66 | // Sanity Check #2: w(x) * (x - 2^16) = vanishing(x). 67 | let x_minus_root = Polynomial::::from_coefficients_slice(&[-root_monomial, F::ONE]); 68 | debug_assert_eq!( 69 | (&p_quotient * &x_minus_root).coefficients(), 70 | p_vanishing.coefficients() 71 | ); 72 | 73 | // Shifting the witness polynomial to make it positive 74 | p_quotient 75 | .coefficients() 76 | .iter() 77 | .map(|x| *x + F::from_canonical_u64(offset_u64)) 78 | .collect::>() 79 | } 80 | -------------------------------------------------------------------------------- /starkyx/src/chip/instruction/bit.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use super::ConstraintInstruction; 4 | use crate::air::parser::AirParser; 5 | use crate::air::AirConstraint; 6 | use crate::chip::register::memory::MemorySlice; 7 | use crate::math::prelude::*; 8 | 9 | #[derive(Debug, Clone, Copy, Serialize, Deserialize)] 10 | pub struct BitConstraint(pub MemorySlice); 11 | 12 | impl ConstraintInstruction for BitConstraint {} 13 | 14 | impl AirConstraint for BitConstraint { 15 | fn eval(&self, parser: &mut AP) { 16 | let bit_constraint = |bit: AP::Var, parser: &mut AP| { 17 | let bit_minus_one = parser.sub_const(bit, AP::Field::ONE); 18 | let constraint = parser.mul(bit, bit_minus_one); 19 | parser.constraint(constraint); 20 | }; 21 | 22 | let bits = self.0.eval_slice(parser).to_vec(); 23 | for bit in bits { 24 | bit_constraint(bit, parser); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /starkyx/src/chip/instruction/clock.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use super::Instruction; 4 | use crate::air::parser::AirParser; 5 | use crate::air::AirConstraint; 6 | use crate::chip::register::element::ElementRegister; 7 | use crate::chip::register::{Register, RegisterSerializable}; 8 | use crate::chip::trace::writer::{AirWriter, TraceWriter}; 9 | use crate::math::prelude::*; 10 | 11 | #[derive(Debug, Clone, Serialize, Deserialize)] 12 | pub struct ClockInstruction { 13 | pub(crate) clk: ElementRegister, 14 | } 15 | 16 | impl AirConstraint for ClockInstruction { 17 | fn eval(&self, parser: &mut AP) { 18 | let clk = self.clk.eval(parser); 19 | let clk_next = self.clk.next().eval(parser); 20 | 21 | parser.constraint_first_row(clk); 22 | 23 | let mut transition = parser.sub(clk_next, clk); 24 | transition = parser.sub_const(transition, AP::Field::ONE); 25 | parser.constraint_transition(transition); 26 | } 27 | } 28 | 29 | impl Instruction for ClockInstruction { 30 | fn write(&self, writer: &TraceWriter, row_index: usize) { 31 | let value = F::from_canonical_usize(row_index); 32 | writer.write(&self.clk, &value, row_index); 33 | } 34 | 35 | fn write_to_air(&self, writer: &mut impl AirWriter) { 36 | let value = F::from_canonical_usize(writer.row_index().unwrap()); 37 | writer.write(&self.clk, &value); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /starkyx/src/chip/instruction/empty.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use super::Instruction; 4 | use crate::air::parser::AirParser; 5 | use crate::air::AirConstraint; 6 | use crate::chip::trace::writer::{AirWriter, TraceWriter}; 7 | use crate::math::prelude::*; 8 | 9 | /// A defult instruction set that contains no custom instructions 10 | #[derive(Clone, Debug, Serialize, Deserialize)] 11 | pub struct EmptyInstruction { 12 | _marker: core::marker::PhantomData, 13 | } 14 | 15 | impl Instruction for EmptyInstruction { 16 | fn write(&self, _writer: &TraceWriter, _row_index: usize) {} 17 | 18 | fn write_to_air(&self, _writer: &mut impl AirWriter) {} 19 | } 20 | 21 | impl> AirConstraint for EmptyInstruction { 22 | fn eval(&self, _parser: &mut AP) {} 23 | } 24 | -------------------------------------------------------------------------------- /starkyx/src/chip/instruction/mod.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::Debug; 2 | 3 | use serde::{Deserialize, Serialize}; 4 | 5 | use super::trace::writer::AirWriter; 6 | use crate::chip::trace::writer::TraceWriter; 7 | use crate::math::prelude::*; 8 | 9 | pub mod assign; 10 | pub mod bit; 11 | pub mod clock; 12 | pub mod cycle; 13 | pub mod empty; 14 | pub mod set; 15 | 16 | pub trait Instruction: 17 | 'static + Send + Sync + Clone + Debug + Serialize + for<'de> Deserialize<'de> 18 | { 19 | /// Writes the instruction to the trace. 20 | fn write(&self, writer: &TraceWriter, row_index: usize); 21 | 22 | #[allow(unused_variables)] 23 | // Writes the instruction to a general AirWriter. 24 | fn write_to_air(&self, writer: &mut impl AirWriter); 25 | } 26 | 27 | /// An instruction that only consists of constraints 28 | pub trait ConstraintInstruction: 29 | 'static + Clone + Debug + Send + Sync + Serialize + for<'de> Deserialize<'de> 30 | { 31 | } 32 | 33 | impl Instruction for C { 34 | fn write(&self, _writer: &TraceWriter, _row_index: usize) {} 35 | 36 | fn write_to_air(&self, _writer: &mut impl AirWriter) {} 37 | } 38 | -------------------------------------------------------------------------------- /starkyx/src/chip/memory/instruction.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use super::get::GetInstruction; 4 | use super::set::SetInstruction; 5 | use super::time::Time; 6 | use super::watch::WatchInstruction; 7 | use crate::air::parser::AirParser; 8 | use crate::air::AirConstraint; 9 | use crate::chip::instruction::Instruction; 10 | use crate::chip::register::element::ElementRegister; 11 | use crate::chip::trace::writer::{AirWriter, TraceWriter}; 12 | use crate::math::field::Field; 13 | 14 | #[derive(Debug, Clone, Serialize, Deserialize)] 15 | pub enum MemoryInstruction { 16 | Get(GetInstruction), 17 | Set(SetInstruction), 18 | Watch(WatchInstruction), 19 | } 20 | 21 | #[derive(Debug, Clone, Serialize, Deserialize)] 22 | pub struct MemoryOutput { 23 | pub label: String, 24 | pub index: Option, 25 | pub ts: Time, 26 | } 27 | 28 | #[derive(Debug, Clone, Serialize, Deserialize)] 29 | pub enum MemorySliceIndex { 30 | /// The index of the memory slice. 31 | Index(usize), 32 | /// The index of the memory slice and the index of the element within the slice. 33 | IndexElement(ElementRegister), 34 | } 35 | 36 | impl AirConstraint for MemoryInstruction { 37 | fn eval(&self, parser: &mut AP) { 38 | match self { 39 | Self::Get(instr) => instr.eval(parser), 40 | Self::Set(instr) => instr.eval(parser), 41 | Self::Watch(instr) => instr.eval(parser), 42 | } 43 | } 44 | } 45 | 46 | impl Instruction for MemoryInstruction { 47 | fn write(&self, writer: &TraceWriter, row_index: usize) { 48 | match self { 49 | Self::Get(instr) => instr.write(writer, row_index), 50 | Self::Set(instr) => instr.write(writer, row_index), 51 | Self::Watch(instr) => instr.write(writer, row_index), 52 | } 53 | } 54 | 55 | fn write_to_air(&self, writer: &mut impl AirWriter) { 56 | match self { 57 | Self::Get(instr) => instr.write_to_air(writer), 58 | Self::Set(instr) => instr.write_to_air(writer), 59 | Self::Watch(instr) => instr.write_to_air(writer), 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /starkyx/src/chip/memory/map.rs: -------------------------------------------------------------------------------- 1 | use core::hash::Hash; 2 | use std::collections::HashMap; 3 | 4 | use serde::{Deserialize, Serialize}; 5 | 6 | use super::pointer::key::RawPointerKey; 7 | 8 | #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] 9 | pub struct MemEntry { 10 | pub value: Vec, 11 | pub multiplicity: T, 12 | } 13 | 14 | #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] 15 | pub struct MemoryMap(pub(crate) HashMap, MemEntry>); 16 | 17 | impl MemoryMap { 18 | pub fn new() -> Self { 19 | Self(HashMap::new()) 20 | } 21 | 22 | pub fn get(&self, ptr: &RawPointerKey) -> Option<&MemEntry> { 23 | self.0.get(ptr) 24 | } 25 | 26 | pub fn remove(&mut self, ptr: &RawPointerKey) -> Option> { 27 | self.0.remove(ptr) 28 | } 29 | 30 | pub fn insert(&mut self, ptr: RawPointerKey, value: MemEntry) { 31 | self.0.insert(ptr, value); 32 | } 33 | 34 | pub fn get_mut(&mut self, ptr: &RawPointerKey) -> Option<&mut MemEntry> { 35 | self.0.get_mut(ptr) 36 | } 37 | } 38 | 39 | impl Default for MemoryMap { 40 | fn default() -> Self { 41 | Self::new() 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /starkyx/src/chip/memory/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod builder; 2 | pub mod get; 3 | pub mod instruction; 4 | pub mod map; 5 | pub mod pointer; 6 | pub mod set; 7 | pub mod time; 8 | pub mod value; 9 | pub mod watch; 10 | -------------------------------------------------------------------------------- /starkyx/src/chip/memory/pointer/key.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use crate::chip::register::cubic::CubicRegister; 4 | 5 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] 6 | pub struct RawPointerKey { 7 | pub challenge: CubicRegister, 8 | pub shift: T, 9 | } 10 | 11 | impl RawPointerKey { 12 | pub(crate) fn new(challenge: CubicRegister, shift: T) -> Self { 13 | Self { challenge, shift } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /starkyx/src/chip/memory/pointer/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod accumulate; 2 | pub mod key; 3 | pub mod raw; 4 | pub mod slice; 5 | mod typed; 6 | 7 | pub use typed::Pointer; 8 | -------------------------------------------------------------------------------- /starkyx/src/chip/memory/pointer/slice.rs: -------------------------------------------------------------------------------- 1 | use core::marker::PhantomData; 2 | 3 | use super::super::value::MemoryValue; 4 | use super::raw::RawPointer; 5 | use super::Pointer; 6 | use crate::chip::builder::AirBuilder; 7 | use crate::chip::register::array::ArrayRegister; 8 | use crate::chip::register::cubic::CubicRegister; 9 | use crate::chip::register::element::ElementRegister; 10 | use crate::chip::AirParameters; 11 | 12 | #[derive(Clone, Debug)] 13 | pub struct RawSlice { 14 | powers: ArrayRegister, 15 | } 16 | 17 | #[derive(Clone, Debug)] 18 | pub struct Slice { 19 | raw: RawSlice, 20 | challenges: ArrayRegister, 21 | _marker: PhantomData, 22 | } 23 | 24 | impl Slice { 25 | pub fn new(raw_slice: RawSlice, challenges: ArrayRegister) -> Self { 26 | Self { 27 | raw: raw_slice, 28 | challenges, 29 | _marker: PhantomData, 30 | } 31 | } 32 | 33 | pub fn get(&self, idx: usize) -> Pointer { 34 | let raw = self.raw.get(idx); 35 | Pointer::new(raw, self.challenges) 36 | } 37 | 38 | pub fn get_at(&self, idx: ElementRegister) -> Pointer { 39 | let raw = self.raw.get_at(idx); 40 | Pointer::new(raw, self.challenges) 41 | } 42 | 43 | pub fn get_at_shifted(&self, idx: ElementRegister, shift: i32) -> Pointer { 44 | let raw = self.raw.get_at_shifted(idx, shift); 45 | Pointer::new(raw, self.challenges) 46 | } 47 | } 48 | 49 | impl RawSlice { 50 | pub(crate) fn get(&self, idx: usize) -> RawPointer { 51 | assert!(idx <= i32::MAX as usize); 52 | RawPointer::new(self.powers, None, Some(idx as i32)) 53 | } 54 | 55 | pub(crate) fn new(builder: &mut AirBuilder) -> Self { 56 | let powers = builder.challenge_powers(3); 57 | 58 | Self { powers } 59 | } 60 | 61 | pub(crate) fn get_at(&self, idx: ElementRegister) -> RawPointer { 62 | RawPointer::new(self.powers, Some(idx), None) 63 | } 64 | 65 | pub(crate) fn get_at_shifted(&self, idx: ElementRegister, shift: i32) -> RawPointer { 66 | RawPointer::new(self.powers, Some(idx), Some(shift)) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /starkyx/src/chip/memory/pointer/typed.rs: -------------------------------------------------------------------------------- 1 | use core::marker::PhantomData; 2 | 3 | use super::raw::RawPointer; 4 | use crate::chip::register::array::ArrayRegister; 5 | use crate::chip::register::cubic::CubicRegister; 6 | 7 | /// A pointer emulating a mutable reference to a data of register type `T`. 8 | #[derive(Debug, Clone, Copy)] 9 | pub struct Pointer { 10 | pub raw: RawPointer, 11 | pub challenges: ArrayRegister, 12 | _marker: PhantomData, 13 | } 14 | 15 | impl Pointer { 16 | pub fn new(raw_ptr: RawPointer, challenges: ArrayRegister) -> Self { 17 | Self { 18 | raw: raw_ptr, 19 | challenges, 20 | _marker: PhantomData, 21 | } 22 | } 23 | 24 | pub(crate) fn from_challenges( 25 | raw_ptr_challenge_powers: ArrayRegister, 26 | compression_challenges: ArrayRegister, 27 | ) -> Self { 28 | Self::new( 29 | RawPointer::from_challenge(raw_ptr_challenge_powers), 30 | compression_challenges, 31 | ) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /starkyx/src/chip/memory/time.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use crate::chip::arithmetic::expression::ArithmeticExpression; 4 | use crate::chip::register::element::ElementRegister; 5 | use crate::chip::register::Register; 6 | use crate::math::prelude::*; 7 | 8 | /// A register that stores a timestamp. 9 | pub type Time = TimeStamp>; 10 | 11 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] 12 | pub struct TimeStamp(pub(crate) T); 13 | 14 | impl TimeStamp { 15 | pub(crate) fn new(value: T) -> Self { 16 | Self(value) 17 | } 18 | } 19 | 20 | impl Time { 21 | pub fn zero() -> Self { 22 | Self::new(ArithmeticExpression::zero()) 23 | } 24 | 25 | pub fn constant(value: usize) -> Self { 26 | Self::new(ArithmeticExpression::from(F::from_canonical_usize(value))) 27 | } 28 | 29 | pub fn from_element(element: ElementRegister) -> Self { 30 | Self::new(element.expr()) 31 | } 32 | 33 | pub fn expr(&self) -> ArithmeticExpression { 34 | self.0.clone() 35 | } 36 | 37 | pub fn advance_by(&self, interval: usize) -> Self { 38 | Self::new(self.0.clone() + ArithmeticExpression::from(F::from_canonical_usize(interval))) 39 | } 40 | 41 | pub fn advance(&self) -> Self { 42 | self.advance_by(1) 43 | } 44 | 45 | pub fn decrement_by(&self, interval: usize) -> Self { 46 | Self::new(self.0.clone() - ArithmeticExpression::from(F::from_canonical_usize(interval))) 47 | } 48 | 49 | pub fn decrement(&self) -> Self { 50 | self.decrement_by(1) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /starkyx/src/chip/memory/value.rs: -------------------------------------------------------------------------------- 1 | use super::pointer::raw::RawPointer; 2 | use super::time::Time; 3 | use crate::chip::builder::AirBuilder; 4 | use crate::chip::register::array::ArrayRegister; 5 | use crate::chip::register::cubic::CubicRegister; 6 | use crate::chip::register::Register; 7 | use crate::chip::AirParameters; 8 | 9 | pub trait MemoryValue: Register { 10 | fn num_challenges() -> usize; 11 | 12 | fn compress( 13 | &self, 14 | builder: &mut AirBuilder, 15 | ptr: RawPointer, 16 | time: &Time, 17 | challenges: &ArrayRegister, 18 | ) -> CubicRegister; 19 | } 20 | -------------------------------------------------------------------------------- /starkyx/src/chip/memory/watch.rs: -------------------------------------------------------------------------------- 1 | use log::debug; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | use super::pointer::raw::RawPointer; 5 | use crate::air::parser::AirParser; 6 | use crate::air::AirConstraint; 7 | use crate::chip::instruction::Instruction; 8 | use crate::chip::trace::writer::{AirWriter, TraceWriter}; 9 | use crate::math::prelude::*; 10 | 11 | #[derive(Debug, Clone, Serialize, Deserialize)] 12 | pub struct WatchInstruction { 13 | ptr: RawPointer, 14 | name: String, 15 | } 16 | 17 | impl AirConstraint for WatchInstruction { 18 | // No constraints for this instruction. 19 | fn eval(&self, _parser: &mut AP) {} 20 | } 21 | 22 | impl Instruction for WatchInstruction { 23 | fn write(&self, writer: &TraceWriter, row_index: usize) { 24 | let mut memory = writer.memory_mut().unwrap(); 25 | let key = self.ptr.read(writer, row_index); 26 | let entry_option = memory.get_mut(&key); 27 | 28 | if let Some(entry) = entry_option { 29 | debug!( 30 | "row {}: , {}: value: {:?}, multiplicities: {:?}", 31 | row_index, self.name, entry.value, entry.multiplicity 32 | ); 33 | } else { 34 | debug!( 35 | "row {:?}: , {}: value: Uninitialized, multiplicities: Uninitialized", 36 | row_index, self.name, 37 | ); 38 | } 39 | } 40 | 41 | fn write_to_air(&self, writer: &mut impl AirWriter) { 42 | let key = self.ptr.read_from_air(writer); 43 | let row_num = if let Some(row_index) = writer.row_index() { 44 | row_index.to_string() 45 | } else { 46 | "None".to_string() 47 | }; 48 | let entry_option = writer.memory_mut().get_mut(&key); 49 | 50 | if let Some(entry) = entry_option { 51 | debug!( 52 | "row {}: , {}: value: {:?}, multiplicities: {:?}", 53 | row_num, self.name, entry.value, entry.multiplicity 54 | ); 55 | } else { 56 | debug!( 57 | "row {}: , {}: value: Uninitialized, multiplicities: Uninitialized", 58 | row_num, self.name, 59 | ) 60 | }; 61 | } 62 | } 63 | 64 | impl WatchInstruction { 65 | pub fn new(ptr: RawPointer, name: String) -> Self { 66 | Self { ptr, name } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /starkyx/src/chip/mod.rs: -------------------------------------------------------------------------------- 1 | use serde::de::DeserializeOwned; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | use self::constraint::Constraint; 5 | use self::instruction::Instruction; 6 | use crate::math::prelude::*; 7 | use crate::plonky2::stark::Starky; 8 | 9 | pub mod air; 10 | pub mod arithmetic; 11 | pub mod bool; 12 | pub mod builder; 13 | pub mod constraint; 14 | pub mod ec; 15 | pub mod field; 16 | pub mod instruction; 17 | pub mod memory; 18 | pub mod register; 19 | pub mod table; 20 | pub mod trace; 21 | pub mod uint; 22 | pub mod utils; 23 | 24 | use core::fmt::Debug; 25 | pub trait AirParameters: 26 | 'static + Clone + Send + Sync + Sized + Debug + Serialize + DeserializeOwned 27 | { 28 | type Field: PrimeField64; 29 | 30 | type CubicParams: CubicParameters; 31 | 32 | /// The number of columns that need to be ranged-checked to range 0..num_rows 33 | /// 34 | /// If NUM_ARITHMETIC_COLUMNS > 0 is used for field operations with 2^16 bit limbs 35 | /// the number of rows should be 2^16. 36 | const NUM_ARITHMETIC_COLUMNS: usize = 0; 37 | 38 | /// The number of columns that are not range checked. 39 | const NUM_FREE_COLUMNS: usize = 0; 40 | 41 | const EXTENDED_COLUMNS: usize = 0; 42 | 43 | /// The type of instruction that the chip supports 44 | type Instruction: Instruction; 45 | 46 | fn num_columns() -> usize { 47 | Self::NUM_ARITHMETIC_COLUMNS + Self::NUM_FREE_COLUMNS + Self::EXTENDED_COLUMNS 48 | } 49 | 50 | /// a unique identifier for the air parameters. 51 | /// 52 | /// by default, this method uses the type name of the air parameters. In case the Rust 53 | /// 'TypeId' is not functioning properly, this method should be overridden. 54 | fn id() -> String { 55 | format!("{:?}", std::any::TypeId::of::()).to_string() 56 | } 57 | } 58 | 59 | #[derive(Debug, Clone, Serialize, Deserialize)] 60 | #[serde(bound = "")] 61 | pub struct Chip { 62 | constraints: Vec>, 63 | global_constraints: Vec>, 64 | pub execution_trace_length: usize, 65 | pub num_challenges: usize, 66 | pub num_public_values: usize, 67 | pub num_global_values: usize, 68 | } 69 | 70 | impl Starky> { 71 | pub fn from_chip(chip: Chip) -> Self { 72 | Self::new(chip) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /starkyx/src/chip/register/bit.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use super::array::ArrayRegister; 4 | use super::cell::CellType; 5 | use super::cubic::CubicRegister; 6 | use super::element::ElementRegister; 7 | use super::memory::MemorySlice; 8 | use super::{Register, RegisterSerializable, RegisterSized}; 9 | use crate::chip::arithmetic::expression::ArithmeticExpression; 10 | use crate::chip::builder::AirBuilder; 11 | use crate::chip::memory::pointer::raw::RawPointer; 12 | use crate::chip::memory::time::Time; 13 | use crate::chip::memory::value::MemoryValue; 14 | use crate::machine::builder::ops::{Add, And, Mul, Not, Or}; 15 | use crate::machine::builder::Builder; 16 | use crate::math::prelude::*; 17 | 18 | /// A register for a single element/column in the trace that is supposed to represent a bit. The 19 | /// value is automatically constrained to be 0 or 1 via the quadratic constraint x * (x - 1) == 0. 20 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] 21 | pub struct BitRegister(MemorySlice); 22 | 23 | impl BitRegister { 24 | pub fn not_expr(&self) -> ArithmeticExpression { 25 | ArithmeticExpression::one() - self.expr() 26 | } 27 | 28 | #[inline] 29 | pub fn as_element(&self) -> ElementRegister { 30 | ElementRegister::from_register_unsafe(self.0) 31 | } 32 | } 33 | 34 | impl RegisterSerializable for BitRegister { 35 | const CELL: CellType = CellType::Bit; 36 | 37 | fn register(&self) -> &MemorySlice { 38 | &self.0 39 | } 40 | 41 | fn from_register_unsafe(register: MemorySlice) -> Self { 42 | BitRegister(register) 43 | } 44 | } 45 | 46 | impl RegisterSized for BitRegister { 47 | fn size_of() -> usize { 48 | 1 49 | } 50 | } 51 | 52 | impl Register for BitRegister { 53 | type Value = T; 54 | 55 | fn value_from_slice(slice: &[T]) -> Self::Value { 56 | slice[0] 57 | } 58 | 59 | fn align(value: &Self::Value) -> &[T] { 60 | std::slice::from_ref(value) 61 | } 62 | } 63 | 64 | impl MemoryValue for BitRegister { 65 | fn num_challenges() -> usize { 66 | 0 67 | } 68 | 69 | fn compress( 70 | &self, 71 | builder: &mut AirBuilder, 72 | ptr: RawPointer, 73 | time: &Time, 74 | challenges: &ArrayRegister, 75 | ) -> CubicRegister { 76 | self.as_element().compress(builder, ptr, time, challenges) 77 | } 78 | } 79 | 80 | impl Add for BitRegister { 81 | type Output = Self; 82 | 83 | fn add(self, rhs: Self, builder: &mut B) -> Self::Output { 84 | builder.expression(self.expr() + rhs.expr()) 85 | } 86 | } 87 | 88 | impl Mul for BitRegister { 89 | type Output = Self; 90 | 91 | fn mul(self, rhs: Self, builder: &mut B) -> Self::Output { 92 | builder.expression(self.expr() * rhs.expr()) 93 | } 94 | } 95 | 96 | impl Not for BitRegister { 97 | type Output = Self; 98 | 99 | fn not(self, builder: &mut B) -> Self::Output { 100 | builder.expression(self.not_expr()) 101 | } 102 | } 103 | 104 | impl Or for BitRegister { 105 | type Output = Self; 106 | 107 | fn or(self, rhs: Self, builder: &mut B) -> Self::Output { 108 | builder.expression(self.expr() + rhs.expr() - self.expr() * rhs.expr()) 109 | } 110 | } 111 | 112 | impl And for BitRegister { 113 | type Output = Self; 114 | 115 | fn and(self, rhs: Self, builder: &mut B) -> Self::Output { 116 | builder.mul(self, rhs) 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /starkyx/src/chip/register/cell.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Clone, Copy)] 2 | pub enum CellType { 3 | U16, 4 | Bit, 5 | Element, 6 | } 7 | -------------------------------------------------------------------------------- /starkyx/src/chip/register/cubic.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use super::array::ArrayRegister; 4 | use super::cell::CellType; 5 | use super::element::ElementRegister; 6 | use super::memory::MemorySlice; 7 | use super::{Register, RegisterSerializable, RegisterSized}; 8 | use crate::air::parser::AirParser; 9 | use crate::chip::arithmetic::expression::ArithmeticExpression; 10 | use crate::math::extension::cubic::element::CubicElement; 11 | use crate::math::prelude::*; 12 | 13 | /// A register for a single element/column in the trace. The value is not constrainted. 14 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] 15 | pub struct CubicRegister(MemorySlice); 16 | 17 | pub trait EvalCubic: Register { 18 | fn value_as_cubic(value: Self::Value, zero: T) -> CubicElement; 19 | 20 | fn eval_cubic(&self, parser: &mut AP) -> CubicElement { 21 | let value = self.eval(parser); 22 | let zero = parser.zero(); 23 | Self::value_as_cubic(value, zero) 24 | } 25 | 26 | fn trace_value_as_cubic(value: Self::Value) -> CubicElement { 27 | let zero = F::ZERO; 28 | Self::value_as_cubic(value, zero) 29 | } 30 | } 31 | 32 | impl RegisterSerializable for CubicRegister { 33 | const CELL: CellType = CellType::Element; 34 | 35 | fn register(&self) -> &MemorySlice { 36 | &self.0 37 | } 38 | 39 | fn from_register_unsafe(register: MemorySlice) -> Self { 40 | CubicRegister(register) 41 | } 42 | } 43 | 44 | impl RegisterSized for CubicRegister { 45 | fn size_of() -> usize { 46 | 3 47 | } 48 | } 49 | 50 | impl Register for CubicRegister { 51 | type Value = CubicElement; 52 | 53 | fn value_from_slice(slice: &[T]) -> Self::Value { 54 | debug_assert!( 55 | slice.len() == 3, 56 | "Slice length mismatch for cubic register (expected 3, got {})", 57 | slice.len() 58 | ); 59 | CubicElement(core::array::from_fn(|i| slice[i])) 60 | } 61 | 62 | fn align(value: &Self::Value) -> &[T] { 63 | &value.0 64 | } 65 | 66 | fn expr(&self) -> crate::chip::arithmetic::expression::ArithmeticExpression { 67 | unimplemented!( 68 | "Cannot create expression from cubic register, use the method ext_expr() instead" 69 | ) 70 | } 71 | } 72 | 73 | impl CubicRegister { 74 | pub fn as_base_array(&self) -> [ElementRegister; 3] { 75 | let array = ArrayRegister::::from_register_unsafe(*self.register()); 76 | [array.get(0), array.get(1), array.get(2)] 77 | } 78 | 79 | pub fn ext_expr(&self) -> CubicElement> { 80 | let array = ArrayRegister::::from_register_unsafe(*self.register()); 81 | CubicElement::new( 82 | array.get(0).expr(), 83 | array.get(1).expr(), 84 | array.get(2).expr(), 85 | ) 86 | } 87 | } 88 | 89 | impl EvalCubic for CubicRegister { 90 | fn value_as_cubic(value: Self::Value, _zero: T) -> CubicElement { 91 | value 92 | } 93 | } 94 | 95 | impl EvalCubic for ElementRegister { 96 | fn value_as_cubic(value: Self::Value, zero: T) -> CubicElement { 97 | CubicElement::from_base(value, zero) 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /starkyx/src/chip/register/element.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use super::array::ArrayRegister; 4 | use super::cell::CellType; 5 | use super::cubic::CubicRegister; 6 | use super::memory::MemorySlice; 7 | use super::{Register, RegisterSerializable, RegisterSized}; 8 | use crate::chip::builder::AirBuilder; 9 | use crate::chip::memory::pointer::raw::RawPointer; 10 | use crate::chip::memory::time::Time; 11 | use crate::chip::memory::value::MemoryValue; 12 | use crate::machine::builder::ops::{Add, Mul}; 13 | use crate::machine::builder::Builder; 14 | use crate::math::prelude::cubic::element::CubicElement; 15 | use crate::math::prelude::*; 16 | 17 | /// A register for a single element/column in the trace. The value is not constrainted. 18 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)] 19 | pub struct ElementRegister(MemorySlice); 20 | 21 | impl RegisterSerializable for ElementRegister { 22 | const CELL: CellType = CellType::Element; 23 | 24 | fn register(&self) -> &MemorySlice { 25 | &self.0 26 | } 27 | 28 | fn from_register_unsafe(register: MemorySlice) -> Self { 29 | ElementRegister(register) 30 | } 31 | } 32 | 33 | impl RegisterSized for ElementRegister { 34 | fn size_of() -> usize { 35 | 1 36 | } 37 | } 38 | 39 | impl Register for ElementRegister { 40 | type Value = T; 41 | 42 | fn align(value: &Self::Value) -> &[T] { 43 | std::slice::from_ref(value) 44 | } 45 | 46 | fn value_from_slice(slice: &[T]) -> Self::Value { 47 | slice[0].clone() 48 | } 49 | } 50 | 51 | impl MemoryValue for ElementRegister { 52 | fn num_challenges() -> usize { 53 | 0 54 | } 55 | 56 | fn compress( 57 | &self, 58 | builder: &mut AirBuilder, 59 | ptr: RawPointer, 60 | time: &Time, 61 | _: &ArrayRegister, 62 | ) -> CubicRegister { 63 | let value = CubicElement([time.expr(), self.expr(), L::Field::ZERO.into()]); 64 | ptr.accumulate_cubic(builder, value) 65 | } 66 | } 67 | 68 | impl Add for ElementRegister { 69 | type Output = Self; 70 | 71 | fn add(self, rhs: Self, builder: &mut B) -> Self::Output { 72 | builder.expression(self.expr() + rhs.expr()) 73 | } 74 | } 75 | 76 | impl Mul for ElementRegister { 77 | type Output = Self; 78 | 79 | fn mul(self, rhs: Self, builder: &mut B) -> Self::Output { 80 | builder.expression(self.expr() * rhs.expr()) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /starkyx/src/chip/register/slice.rs: -------------------------------------------------------------------------------- 1 | use core::borrow::Borrow; 2 | use core::fmt::Debug; 3 | 4 | use super::array::{ArrayIterator, ArrayRegister}; 5 | use super::Register; 6 | 7 | /// A trait that enables the interface of a slice uniform on both `ArrayRegister` such as a 8 | /// vector `Vec`, array, or slice `&[T]`. 9 | pub trait RegisterSlice: Debug + Clone + Send + Sync { 10 | type Item<'a>: Borrow 11 | where 12 | Self: 'a; 13 | type Iterator<'a>: Iterator> 14 | where 15 | Self: 'a; 16 | 17 | fn get_value(&self, index: usize) -> T; 18 | 19 | fn len(&self) -> usize; 20 | 21 | fn first_value(&self) -> Option; 22 | 23 | fn last_value(&self) -> Option; 24 | 25 | fn value_iter(&self) -> Self::Iterator<'_>; 26 | 27 | fn is_empty(&self) -> bool { 28 | self.len() == 0 29 | } 30 | } 31 | 32 | impl RegisterSlice for ArrayRegister { 33 | type Item<'a> = T; 34 | type Iterator<'a> = ArrayIterator; 35 | 36 | fn get_value(&self, index: usize) -> T { 37 | self.get(index) 38 | } 39 | 40 | fn first_value(&self) -> Option { 41 | self.first() 42 | } 43 | 44 | fn last_value(&self) -> Option { 45 | self.last() 46 | } 47 | 48 | fn value_iter(&self) -> Self::Iterator<'_> { 49 | self.iter() 50 | } 51 | 52 | fn len(&self) -> usize { 53 | self.len() 54 | } 55 | } 56 | 57 | impl RegisterSlice for Vec { 58 | type Item<'a> = &'a T; 59 | type Iterator<'a> = core::slice::Iter<'a, T>; 60 | 61 | fn get_value(&self, index: usize) -> T { 62 | self[index] 63 | } 64 | 65 | fn first_value(&self) -> Option { 66 | self.first().copied() 67 | } 68 | 69 | fn last_value(&self) -> Option { 70 | self.last().copied() 71 | } 72 | 73 | fn value_iter(&self) -> Self::Iterator<'_> { 74 | self.iter() 75 | } 76 | 77 | fn len(&self) -> usize { 78 | self.len() 79 | } 80 | } 81 | 82 | impl<'b, T: Register> RegisterSlice for &'b [T] { 83 | type Item<'a> = &'a T where Self: 'a; 84 | type Iterator<'a> = core::slice::Iter<'a, T> where Self: 'a; 85 | 86 | fn get_value(&self, index: usize) -> T { 87 | self[index] 88 | } 89 | 90 | fn first_value(&self) -> Option { 91 | self.first().copied() 92 | } 93 | 94 | fn last_value(&self) -> Option { 95 | self.last().copied() 96 | } 97 | 98 | fn value_iter(&self) -> Self::Iterator<'_> { 99 | self.iter() 100 | } 101 | 102 | fn len(&self) -> usize { 103 | (*self).len() 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /starkyx/src/chip/register/u16.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use super::cell::CellType; 4 | use super::memory::MemorySlice; 5 | use super::{Register, RegisterSerializable, RegisterSized}; 6 | 7 | /// A register for a single element/column in the trace that is supposed to represent a u16. The 8 | /// value is automatically range checked via the lookup table if the register is allocated through 9 | /// the builder. 10 | #[derive(Debug, Clone, Copy, Serialize, Deserialize)] 11 | pub struct U16Register(MemorySlice); 12 | 13 | impl RegisterSerializable for U16Register { 14 | const CELL: CellType = CellType::U16; 15 | 16 | fn register(&self) -> &MemorySlice { 17 | &self.0 18 | } 19 | 20 | fn from_register_unsafe(register: MemorySlice) -> Self { 21 | Self(register) 22 | } 23 | } 24 | 25 | impl RegisterSized for U16Register { 26 | fn size_of() -> usize { 27 | 1 28 | } 29 | } 30 | 31 | impl Register for U16Register { 32 | type Value = T; 33 | 34 | fn value_from_slice(slice: &[T]) -> Self::Value { 35 | slice[0] 36 | } 37 | 38 | fn align(value: &Self::Value) -> &[T] { 39 | std::slice::from_ref(value) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /starkyx/src/chip/table/accumulator/constraint.rs: -------------------------------------------------------------------------------- 1 | use itertools::Itertools; 2 | 3 | use super::Accumulator; 4 | use crate::air::extension::cubic::CubicParser; 5 | use crate::air::AirConstraint; 6 | use crate::chip::register::Register; 7 | use crate::math::extension::CubicParameters; 8 | 9 | impl, AP: CubicParser> AirConstraint 10 | for Accumulator 11 | { 12 | fn eval(&self, parser: &mut AP) { 13 | let digest = self.digest.eval(parser); 14 | 15 | let values = self 16 | .values 17 | .iter() 18 | .flat_map(|x| x.eval(parser)) 19 | .collect::>(); 20 | 21 | let acc = values.iter().zip_eq(self.challenges.iter()).fold( 22 | parser.zero_extension(), 23 | |acc, (val, alpha)| { 24 | let alpha_val = alpha.eval(parser); 25 | let val_ext = parser.element_from_base_field(*val); 26 | let alpha_times_val = parser.mul_extension(alpha_val, val_ext); 27 | parser.add_extension(acc, alpha_times_val) 28 | }, 29 | ); 30 | 31 | parser.assert_eq_extension(acc, digest); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /starkyx/src/chip/table/accumulator/trace.rs: -------------------------------------------------------------------------------- 1 | use super::Accumulator; 2 | use crate::chip::register::memory::MemorySlice; 3 | use crate::chip::register::RegisterSerializable; 4 | use crate::chip::trace::writer::TraceWriter; 5 | use crate::math::extension::cubic::element::CubicElement; 6 | use crate::math::prelude::*; 7 | 8 | impl TraceWriter { 9 | pub(crate) fn write_accumulation>( 10 | &self, 11 | accumulator: &Accumulator, 12 | ) { 13 | let challenges = self.read_vec(&accumulator.challenges, 0); 14 | 15 | // Write accumulation to the digest 16 | match accumulator.digest.register() { 17 | MemorySlice::Global(..) => { 18 | let acc = accumulator 19 | .values 20 | .iter() 21 | .flat_map(|x| self.read_expression(x, 0)) 22 | .zip(challenges.iter()) 23 | .map(|(val, alpha)| *alpha * val) 24 | .sum::>(); 25 | self.write(&accumulator.digest, &acc, 0); 26 | } 27 | _ => { 28 | let num_rows = self.height; 29 | (0..num_rows).for_each(|row| { 30 | let acc = accumulator 31 | .values 32 | .iter() 33 | .flat_map(|x| self.read_expression(x, row)) 34 | .zip(challenges.iter()) 35 | .map(|(val, alpha)| *alpha * val) 36 | .sum::>(); 37 | self.write(&accumulator.digest, &acc, row); 38 | }); 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /starkyx/src/chip/table/bus/channel/constraint.rs: -------------------------------------------------------------------------------- 1 | use super::BusChannel; 2 | use crate::air::extension::cubic::CubicParser; 3 | use crate::air::AirConstraint; 4 | use crate::chip::register::cubic::EvalCubic; 5 | use crate::chip::register::Register; 6 | use crate::chip::table::log_derivative::constraints::LogConstraints; 7 | use crate::math::extension::cubic::parameters::CubicParameters; 8 | 9 | impl, E: CubicParameters> AirConstraint 10 | for BusChannel 11 | { 12 | fn eval(&self, parser: &mut AP) { 13 | let beta = self.challenge.eval(parser); 14 | 15 | // Constrain the trace accumulation of the bus channel. 16 | LogConstraints::log_trace_accumulation( 17 | parser, 18 | beta, 19 | &self.entries, 20 | &self.row_accumulators, 21 | self.table_accumulator, 22 | ); 23 | 24 | // Constrain the out channel to the last row of the bus column. 25 | let bus_value = self.table_accumulator.eval(parser); 26 | let out_channel = self.out_channel.eval(parser); 27 | let out_minus_bus = parser.sub_extension(out_channel, bus_value); 28 | parser.constraint_extension_last_row(out_minus_bus); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /starkyx/src/chip/table/bus/channel/trace.rs: -------------------------------------------------------------------------------- 1 | use super::BusChannel; 2 | use crate::chip::register::cubic::EvalCubic; 3 | use crate::chip::trace::writer::TraceWriter; 4 | use crate::math::extension::cubic::extension::CubicExtension; 5 | use crate::math::prelude::*; 6 | 7 | impl TraceWriter { 8 | pub fn write_bus_channel>( 9 | &self, 10 | channel: &BusChannel, 11 | ) { 12 | let beta = CubicExtension::::from(self.read(&channel.challenge, 0)); 13 | 14 | // Accumulate bus values in the trace. 15 | let accumulated_bus_value = self.write_log_trace_accumulation( 16 | beta, 17 | &channel.entries, 18 | &channel.row_accumulators, 19 | channel.table_accumulator, 20 | ); 21 | 22 | // Write the final accumulated value to the output channel 23 | self.write( 24 | &channel.out_channel, 25 | &accumulated_bus_value.0, 26 | self.height - 1, 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /starkyx/src/chip/table/bus/global/constraint.rs: -------------------------------------------------------------------------------- 1 | use super::Bus; 2 | use crate::air::extension::cubic::CubicParser; 3 | use crate::air::AirConstraint; 4 | use crate::chip::register::cubic::EvalCubic; 5 | use crate::chip::register::Register; 6 | use crate::chip::table::log_derivative::constraints::LogConstraints; 7 | use crate::math::extension::cubic::parameters::CubicParameters; 8 | 9 | impl, AP: CubicParser> AirConstraint 10 | for Bus 11 | { 12 | fn eval(&self, parser: &mut AP) { 13 | let beta = self.challenge.eval(parser); 14 | 15 | // Accumulate the global entries. 16 | LogConstraints::log_global_accumulation( 17 | parser, 18 | beta, 19 | &self.global_entries, 20 | &self.global_accumulators, 21 | self.global_value, 22 | ); 23 | 24 | // Constrain the sum of all entries to be zero. 25 | let mut sum = self.global_value.eval(parser); 26 | for value in self.channels.iter() { 27 | let val = value.eval(parser); 28 | sum = parser.add_extension(sum, val); 29 | } 30 | parser.constraint_extension(sum); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /starkyx/src/chip/table/bus/global/trace.rs: -------------------------------------------------------------------------------- 1 | use super::Bus; 2 | use crate::chip::register::cubic::EvalCubic; 3 | use crate::chip::trace::writer::TraceWriter; 4 | use crate::math::extension::cubic::extension::CubicExtension; 5 | use crate::math::prelude::*; 6 | 7 | impl TraceWriter { 8 | pub fn write_global_bus>(&self, bus: &Bus) { 9 | let beta = CubicExtension::::from(self.read(&bus.challenge, 0)); 10 | 11 | // Accumulate bus entries for global values 12 | self.write_log_global_accumulation( 13 | beta, 14 | &bus.global_entries, 15 | &bus.global_accumulators, 16 | bus.global_value, 17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /starkyx/src/chip/table/bus/mod.rs: -------------------------------------------------------------------------------- 1 | //! An implementation of an input/output bus based on a permutaton argument 2 | //! 3 | //! The consistency constraints on the bus mean that the every output from the bus has 4 | //! been read from some input. 5 | //! 6 | 7 | pub mod channel; 8 | pub mod global; 9 | -------------------------------------------------------------------------------- /starkyx/src/chip/table/log_derivative/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod constraints; 2 | pub mod entry; 3 | pub mod trace; 4 | -------------------------------------------------------------------------------- /starkyx/src/chip/table/log_derivative/trace.rs: -------------------------------------------------------------------------------- 1 | use super::entry::LogEntry; 2 | use crate::chip::register::cubic::{CubicRegister, EvalCubic}; 3 | use crate::chip::register::slice::RegisterSlice; 4 | use crate::chip::register::Register; 5 | use crate::chip::trace::writer::TraceWriter; 6 | use crate::math::prelude::cubic::extension::CubicExtension; 7 | use crate::math::prelude::*; 8 | use crate::maybe_rayon::*; 9 | 10 | impl TraceWriter { 11 | pub fn write_log_trace_accumulation>( 12 | &self, 13 | beta: CubicExtension, 14 | entries: &[LogEntry], 15 | intermediate_values: &impl RegisterSlice, 16 | trace_accumulator: CubicRegister, 17 | ) -> CubicExtension { 18 | // Accumulate lookup values in the trace 19 | let accumulators = self 20 | .write_trace() 21 | .unwrap() 22 | .rows_par_mut() 23 | .map(|row| { 24 | let entry_chunks = entries.chunks_exact(2); 25 | let last_element = entry_chunks 26 | .remainder() 27 | .first() 28 | .map(|reg| reg.read_from_slice(row).evaluate(beta)) 29 | .unwrap_or(CubicExtension::ZERO); 30 | let mut accumumulator = CubicExtension::ZERO; 31 | let accumulators = intermediate_values; 32 | for (k, pair) in entry_chunks.enumerate() { 33 | let a = pair[0].read_from_slice(row); 34 | let b = pair[1].read_from_slice(row); 35 | accumumulator += a.evaluate(beta) + b.evaluate(beta); 36 | accumulators 37 | .get_value(k) 38 | .assign_to_raw_slice(row, &accumumulator.0); 39 | } 40 | accumumulator + last_element 41 | }) 42 | .collect::>(); 43 | 44 | let mut value = CubicExtension::ZERO; 45 | for (i, acc) in accumulators.into_iter().enumerate() { 46 | value += acc; 47 | self.write(&trace_accumulator, &value.0, i); 48 | } 49 | // Write the local digest 50 | self.write(&trace_accumulator, &value.0, self.height - 1); 51 | 52 | value 53 | } 54 | 55 | pub fn write_log_global_accumulation>( 56 | &self, 57 | beta: CubicExtension, 58 | entries: &[LogEntry], 59 | intermediate_values: &impl RegisterSlice, 60 | global_accumulator: CubicRegister, 61 | ) -> CubicExtension { 62 | let value_chunks = entries.chunks_exact(2); 63 | let last_element = value_chunks 64 | .remainder() 65 | .last() 66 | .map(|reg| self.read_log_entry(reg, 0).evaluate(beta)) 67 | .unwrap_or(CubicExtension::ZERO); 68 | let mut accumumulator = CubicExtension::ZERO; 69 | for (k, pair) in value_chunks.enumerate() { 70 | let a = self.read_log_entry(&pair[0], 0); 71 | let b = self.read_log_entry(&pair[1], 0); 72 | accumumulator += a.evaluate(beta) + b.evaluate(beta); 73 | self.write(&intermediate_values.get_value(k), &accumumulator.0, 0); 74 | } 75 | let value = accumumulator + last_element; 76 | self.write(&global_accumulator, &value.0, 0); 77 | 78 | value 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /starkyx/src/chip/table/lookup/constraint.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use super::{LogLookupTable, LogLookupValues}; 4 | use crate::air::extension::cubic::CubicParser; 5 | use crate::air::AirConstraint; 6 | use crate::chip::register::cubic::{CubicRegister, EvalCubic}; 7 | use crate::chip::register::element::ElementRegister; 8 | use crate::chip::register::Register; 9 | use crate::math::prelude::*; 10 | 11 | #[derive(Debug, Clone, Serialize, Deserialize)] 12 | #[serde(bound = "")] 13 | pub enum LookupConstraint> { 14 | Table(LogLookupTable), 15 | ValuesLocal(LogLookupValues), 16 | ValuesGlobal(LogLookupValues), 17 | ValuesDigest(CubicRegister, CubicRegister, Option), 18 | Digest(CubicRegister, Vec), 19 | } 20 | 21 | #[derive(Debug, Clone, Serialize, Deserialize)] 22 | #[serde(bound = "")] 23 | pub enum LookupChipConstraint> { 24 | Element(LookupConstraint), 25 | CubicElement(LookupConstraint), 26 | } 27 | 28 | impl, AP: CubicParser> AirConstraint 29 | for LookupConstraint 30 | { 31 | fn eval(&self, parser: &mut AP) { 32 | match self { 33 | LookupConstraint::Table(table) => table.eval(parser), 34 | LookupConstraint::ValuesLocal(values) => values.eval(parser), 35 | LookupConstraint::ValuesGlobal(values) => values.eval_global(parser), 36 | LookupConstraint::ValuesDigest(digest, local_digest, global_digest) => { 37 | let digest = digest.eval(parser); 38 | let local_digest = local_digest.eval(parser); 39 | let global_digest = global_digest 40 | .map(|d| d.eval(parser)) 41 | .unwrap_or_else(|| parser.zero_extension()); 42 | 43 | let mut digest_constraint = parser.add_extension(local_digest, global_digest); 44 | digest_constraint = parser.sub_extension(digest_constraint, digest); 45 | parser.constraint_extension_last_row(digest_constraint); 46 | } 47 | LookupConstraint::Digest(table_digest, element_digests) => { 48 | let table = table_digest.eval_cubic(parser); 49 | let elements = element_digests 50 | .iter() 51 | .map(|b| b.eval_cubic(parser)) 52 | .collect::>(); 53 | let mut elem_sum = parser.zero_extension(); 54 | for e in elements { 55 | elem_sum = parser.add_extension(elem_sum, e); 56 | } 57 | let difference = parser.sub_extension(table, elem_sum); 58 | parser.constraint_extension_last_row(difference); 59 | } 60 | } 61 | } 62 | } 63 | 64 | impl, AP: CubicParser> AirConstraint 65 | for LookupChipConstraint 66 | { 67 | fn eval(&self, parser: &mut AP) { 68 | match self { 69 | LookupChipConstraint::Element(log) => log.eval(parser), 70 | LookupChipConstraint::CubicElement(log) => log.eval(parser), 71 | } 72 | } 73 | } 74 | 75 | impl> From> 76 | for LookupChipConstraint 77 | { 78 | fn from(constraint: LookupConstraint) -> Self { 79 | Self::Element(constraint) 80 | } 81 | } 82 | 83 | impl> From> 84 | for LookupChipConstraint 85 | { 86 | fn from(constraint: LookupConstraint) -> Self { 87 | Self::CubicElement(constraint) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /starkyx/src/chip/table/lookup/mod.rs: -------------------------------------------------------------------------------- 1 | //! This module implements a lookup argument based on the logarithmic derivative as in 2 | //! https://eprint.iacr.org/2022/1530.pdf 3 | //! 4 | 5 | use self::table::LogLookupTable; 6 | use self::values::LogLookupValues; 7 | 8 | pub mod constraint; 9 | pub mod table; 10 | pub mod trace; 11 | pub mod values; 12 | -------------------------------------------------------------------------------- /starkyx/src/chip/table/lookup/table/constraint.rs: -------------------------------------------------------------------------------- 1 | use itertools::Itertools; 2 | 3 | use super::{LogLookupTable, LookupTable}; 4 | use crate::air::extension::cubic::CubicParser; 5 | use crate::air::AirConstraint; 6 | use crate::chip::register::cubic::EvalCubic; 7 | use crate::chip::register::{Register, RegisterSerializable}; 8 | use crate::chip::table::log_derivative::constraints::LogConstraints; 9 | use crate::chip::table::log_derivative::entry::LogEntry; 10 | use crate::math::prelude::*; 11 | 12 | impl, AP: CubicParser> AirConstraint 13 | for LookupTable 14 | { 15 | fn eval(&self, parser: &mut AP) { 16 | match self { 17 | LookupTable::Element(t) => t.eval(parser), 18 | LookupTable::Cubic(t) => t.eval(parser), 19 | } 20 | } 21 | } 22 | 23 | impl, AP: CubicParser> AirConstraint 24 | for LogLookupTable 25 | { 26 | fn eval(&self, parser: &mut AP) { 27 | let beta = self.challenge.eval(parser); 28 | 29 | // Constrain multiplicities_table_log = sum(mult_i * log(beta - table_i)) 30 | for ((mult_table_log, table), mult) in self 31 | .multiplicities_table_log 32 | .iter() 33 | .zip_eq(self.table.iter()) 34 | .zip_eq(self.multiplicities) 35 | { 36 | let mult_table_log = mult_table_log.eval(parser); 37 | let table = LogEntry::input_with_multiplicity(*table, mult).eval(parser); 38 | let mult_table_constraint = LogConstraints::log(parser, beta, table, mult_table_log); 39 | parser.constraint_extension(mult_table_constraint); 40 | } 41 | 42 | // Constrain the accumulation 43 | let mult_table_log_sum = self.multiplicities_table_log.iter().fold( 44 | parser.zero_extension(), 45 | |acc, mult_table_log| { 46 | let mult_table_log = mult_table_log.eval(parser); 47 | parser.add_extension(acc, mult_table_log) 48 | }, 49 | ); 50 | 51 | let accumulator = self.table_accumulator.eval(parser); 52 | 53 | let first_row_acc = parser.sub_extension(accumulator, mult_table_log_sum); 54 | parser.constraint_extension_first_row(first_row_acc); 55 | 56 | let mult_table_log_sum_next = self.multiplicities_table_log.iter().fold( 57 | parser.zero_extension(), 58 | |acc, mult_table_log| { 59 | let value = mult_table_log.next().eval(parser); 60 | parser.add_extension(acc, value) 61 | }, 62 | ); 63 | 64 | let acuumulator_next = self.table_accumulator.next().eval(parser); 65 | 66 | let acc_next_expected = parser.add_extension(accumulator, mult_table_log_sum_next); 67 | let acc_next_constraint = parser.sub_extension(acuumulator_next, acc_next_expected); 68 | parser.constraint_extension_transition(acc_next_constraint); 69 | 70 | // Constraint the digest 71 | let digest = self.digest.eval(parser); 72 | let digest_constraint = parser.sub_extension(digest, accumulator); 73 | parser.constraint_extension_last_row(digest_constraint); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /starkyx/src/chip/table/lookup/table/trace.rs: -------------------------------------------------------------------------------- 1 | use itertools::Itertools; 2 | 3 | use super::{LogLookupTable, LookupTable}; 4 | use crate::chip::register::cubic::EvalCubic; 5 | use crate::chip::register::Register; 6 | use crate::chip::trace::writer::TraceWriter; 7 | use crate::math::prelude::cubic::extension::CubicExtension; 8 | use crate::math::prelude::*; 9 | use crate::maybe_rayon::*; 10 | 11 | impl TraceWriter { 12 | /// Writes the table lookups and accumulate assumes multiplicities have been written 13 | pub(crate) fn write_log_lookup_table>( 14 | &self, 15 | table_data: &LogLookupTable, 16 | ) -> Vec> { 17 | let beta = CubicExtension::::from(self.read(&table_data.challenge, 0)); 18 | assert_eq!( 19 | table_data.table.len(), 20 | table_data.multiplicities_table_log.len() 21 | ); 22 | assert_eq!(table_data.table.len(), table_data.multiplicities.len()); 23 | let mult_table_log_entries = self 24 | .write_trace() 25 | .unwrap() 26 | .rows_par_mut() 27 | .map(|row| { 28 | let mut sum = CubicExtension::ZERO; 29 | for ((table, multiplicity), table_log_register) in table_data 30 | .table 31 | .iter() 32 | .zip_eq(table_data.multiplicities.iter()) 33 | .zip_eq(table_data.multiplicities_table_log.iter()) 34 | { 35 | let table_val = table.read_from_slice(row); 36 | let mult_val = multiplicity.read_from_slice(row); 37 | let table = CubicExtension::from(T::trace_value_as_cubic(table_val)); 38 | let mult = CubicExtension::from(mult_val); 39 | let table_log = mult / (beta - table); 40 | table_log_register.assign_to_raw_slice(row, &table_log.0); 41 | sum += table_log; 42 | } 43 | sum 44 | }) 45 | .collect::>(); 46 | 47 | // Write accumulation 48 | let mut acc = CubicExtension::ZERO; 49 | for (i, mult_table) in mult_table_log_entries.iter().enumerate() { 50 | acc += *mult_table; 51 | self.write(&table_data.table_accumulator, &acc.0, i); 52 | } 53 | 54 | // Write the digest value 55 | self.write(&table_data.digest, &acc.0, self.height - 1); 56 | 57 | mult_table_log_entries 58 | } 59 | 60 | pub(crate) fn write_lookup_table>(&self, table_data: &LookupTable) { 61 | match table_data { 62 | LookupTable::Element(table) => { 63 | self.write_log_lookup_table(table); 64 | } 65 | LookupTable::Cubic(table) => { 66 | self.write_log_lookup_table(table); 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /starkyx/src/chip/table/lookup/values/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod constraint; 2 | pub mod trace; 3 | 4 | use serde::{Deserialize, Serialize}; 5 | 6 | use crate::chip::register::array::ArrayRegister; 7 | use crate::chip::register::cubic::{CubicRegister, EvalCubic}; 8 | use crate::chip::register::element::ElementRegister; 9 | use crate::chip::table::log_derivative::entry::LogEntry; 10 | 11 | #[derive(Debug, Clone, Serialize, Deserialize)] 12 | #[serde(bound = "")] 13 | pub enum LookupValues { 14 | Element(LogLookupValues), 15 | Cubic(LogLookupValues), 16 | } 17 | 18 | /// Currently, only supports an even number of values 19 | #[derive(Debug, Clone, Serialize, Deserialize)] 20 | #[serde(bound = "")] 21 | pub struct LogLookupValues { 22 | pub(crate) challenge: CubicRegister, 23 | pub(crate) trace_values: Vec>, 24 | pub(crate) public_values: Vec>, 25 | pub(crate) row_accumulators: ArrayRegister, 26 | pub(crate) global_accumulators: ArrayRegister, 27 | pub local_digest: CubicRegister, 28 | pub global_digest: Option, 29 | pub digest: CubicRegister, 30 | pub(crate) _marker: core::marker::PhantomData<(F, E)>, 31 | } 32 | -------------------------------------------------------------------------------- /starkyx/src/chip/table/lookup/values/trace.rs: -------------------------------------------------------------------------------- 1 | use super::{LogLookupValues, LookupValues}; 2 | use crate::chip::register::cubic::EvalCubic; 3 | use crate::chip::trace::writer::TraceWriter; 4 | use crate::math::prelude::cubic::extension::CubicExtension; 5 | use crate::math::prelude::*; 6 | 7 | impl TraceWriter { 8 | pub(crate) fn write_log_lookup_values>( 9 | &self, 10 | values_data: &LogLookupValues, 11 | ) { 12 | let beta = CubicExtension::::from(self.read(&values_data.challenge, 0)); 13 | 14 | // Accumulate lookup values in the trace 15 | let trace_accumulated_value = self.write_log_trace_accumulation( 16 | beta, 17 | &values_data.trace_values, 18 | &values_data.row_accumulators, 19 | values_data.local_digest, 20 | ); 21 | 22 | // Accumulate lookups for public inputs 23 | let global_accumulated_value = if let Some(global_digest) = values_data.global_digest { 24 | self.write_log_global_accumulation( 25 | beta, 26 | &values_data.public_values, 27 | &values_data.global_accumulators, 28 | global_digest, 29 | ) 30 | } else { 31 | CubicExtension::ZERO 32 | }; 33 | 34 | let value = trace_accumulated_value + global_accumulated_value; 35 | 36 | // Write the total digest value 37 | self.write(&values_data.digest, &value.0, self.height - 1); 38 | } 39 | 40 | pub(crate) fn write_lookup_values>( 41 | &self, 42 | values_data: &LookupValues, 43 | ) { 44 | match values_data { 45 | LookupValues::Element(values) => { 46 | self.write_log_lookup_values(values); 47 | } 48 | LookupValues::Cubic(values) => { 49 | self.write_log_lookup_values(values); 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /starkyx/src/chip/table/mod.rs: -------------------------------------------------------------------------------- 1 | //! Table level constraints for inter-column claims. 2 | //! 3 | //! Currently, we have three types of claims: 4 | //! 5 | //! - Lookup: A column is a lookup table for another column. Namely, a lookup argument allows for 6 | //! a proof that all the values of columns `a_1`, ..., `a_n` are contained in the entries 7 | //! of column `b` (the table). 8 | //! 9 | //! - Permutation: A column is a permutation of another column. 10 | //! 11 | //! - Evaluation: The values of a column (or a subset of its rows) is the same as that 12 | //! of another column (or the same subset of its rows). 13 | //! 14 | //! 15 | 16 | pub mod accumulator; 17 | pub mod bus; 18 | pub mod log_derivative; 19 | pub mod lookup; 20 | pub mod powers; 21 | -------------------------------------------------------------------------------- /starkyx/src/chip/table/powers.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use crate::air::extension::cubic::CubicParser; 4 | use crate::chip::builder::AirBuilder; 5 | use crate::chip::register::array::ArrayRegister; 6 | use crate::chip::register::cubic::CubicRegister; 7 | use crate::chip::register::Register; 8 | use crate::chip::trace::writer::TraceWriter; 9 | use crate::math::prelude::*; 10 | use crate::prelude::cubic::element::CubicElement; 11 | use crate::prelude::{AirConstraint, AirParameters}; 12 | 13 | /// Powers of a challenge element. 14 | /// 15 | /// This struct defines the constraints and writing method to get the powers of a challenge element 16 | /// meant for Reed-Solomon fingerprinting. 17 | #[derive(Debug, Clone, Serialize, Deserialize)] 18 | pub struct Powers { 19 | element: CubicRegister, 20 | values: ArrayRegister, 21 | _marker: std::marker::PhantomData<(F, E)>, 22 | } 23 | 24 | impl AirBuilder { 25 | /// Get an array of powers 1, `gamma`,..., `gamma^{len-1}` of a verifier challenge. 26 | pub fn challenge_powers(&mut self, len: usize) -> ArrayRegister { 27 | let challenge = self.alloc_challenge(); 28 | let power_values = self.alloc_array_global(len); 29 | 30 | let powers = Powers { 31 | element: challenge, 32 | values: power_values, 33 | _marker: std::marker::PhantomData, 34 | }; 35 | 36 | self.powers.push(powers.clone()); 37 | self.global_constraints.push(powers.into()); 38 | 39 | power_values 40 | } 41 | } 42 | 43 | impl, AP: CubicParser> AirConstraint for Powers { 44 | fn eval(&self, parser: &mut AP) { 45 | let element = self.element.eval(parser); 46 | let powers = self.values.eval_vec(parser); 47 | 48 | if powers.is_empty() { 49 | return; 50 | } 51 | 52 | let one = parser.one_extension(); 53 | parser.assert_eq_extension(powers[0], one); 54 | 55 | for window in powers.windows(2) { 56 | let left = window[0]; 57 | let right = window[1]; 58 | let left_times_element = parser.mul_extension(left, element); 59 | parser.assert_eq_extension(left_times_element, right); 60 | } 61 | } 62 | } 63 | 64 | impl TraceWriter { 65 | pub fn write_powers>(&self, powers: &Powers) { 66 | let elememt = self.read(&powers.element, 0); 67 | 68 | let mut power = CubicElement::ONE; 69 | for power_reg in powers.values.iter() { 70 | self.write(&power_reg, &power, 0); 71 | power *= elememt; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /starkyx/src/chip/trace/data.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use super::writer::{AirWriter, TraceWriter}; 4 | use crate::chip::instruction::set::AirInstruction; 5 | use crate::chip::memory::pointer::accumulate::PointerAccumulator; 6 | use crate::chip::register::cubic::CubicRegister; 7 | use crate::chip::table::accumulator::Accumulator; 8 | use crate::chip::table::bus::channel::BusChannel; 9 | use crate::chip::table::bus::global::Bus; 10 | use crate::chip::table::lookup::table::LookupTable; 11 | use crate::chip::table::lookup::values::LookupValues; 12 | use crate::chip::table::powers::Powers; 13 | use crate::chip::AirParameters; 14 | 15 | #[derive(Debug, Clone, Serialize, Deserialize)] 16 | #[allow(clippy::type_complexity)] 17 | pub struct AirTraceData { 18 | pub num_challenges: usize, 19 | pub num_public_inputs: usize, 20 | pub num_global_values: usize, 21 | pub execution_trace_length: usize, 22 | pub instructions: Vec>, 23 | pub global_instructions: Vec>, 24 | pub powers: Vec>, 25 | pub accumulators: Vec>, 26 | pub pointer_row_accumulators: Vec>, 27 | pub pointer_global_accumulators: Vec>, 28 | pub bus_channels: Vec>, 29 | pub buses: Vec>, 30 | pub lookup_values: Vec>, 31 | pub lookup_tables: Vec>, 32 | pub range_data: Option<( 33 | LookupTable, 34 | LookupValues, 35 | )>, 36 | } 37 | 38 | impl AirTraceData { 39 | #[inline] 40 | pub fn write_trace_instructions(&self, writer: &mut impl AirWriter) { 41 | for instruction in self.instructions.iter() { 42 | writer.write_instruction(instruction); 43 | } 44 | } 45 | 46 | #[inline] 47 | pub fn write_global_instructions(&self, writer: &mut impl AirWriter) { 48 | for instruction in self.global_instructions.iter() { 49 | writer.write_instruction(instruction); 50 | } 51 | } 52 | 53 | pub fn write_extended_trace(&self, writer: &TraceWriter) { 54 | let num_rows = writer.read_trace().unwrap().height(); 55 | 56 | // Fill in the challenge powers. 57 | for power in self.powers.iter() { 58 | writer.write_powers(power); 59 | } 60 | 61 | // Write accumulations. 62 | for acc in self.accumulators.iter() { 63 | writer.write_accumulation(acc); 64 | } 65 | 66 | // Write pointer accumulations. 67 | for acc in self.pointer_global_accumulators.iter() { 68 | writer.write_ptr_accumulation(acc, 0); 69 | } 70 | 71 | for i in 0..num_rows { 72 | for acc in self.pointer_row_accumulators.iter() { 73 | writer.write_ptr_accumulation(acc, i); 74 | } 75 | } 76 | 77 | // Write bus channels. 78 | for channel in self.bus_channels.iter() { 79 | writer.write_bus_channel(channel); 80 | } 81 | 82 | // Write bus values. 83 | for bus in self.buses.iter() { 84 | writer.write_global_bus(bus); 85 | } 86 | 87 | // Write lookup tables. 88 | for table in self.lookup_tables.iter() { 89 | writer.write_lookup_table(table); 90 | } 91 | 92 | // Write lookup values. 93 | for value_data in self.lookup_values.iter() { 94 | writer.write_lookup_values(value_data); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /starkyx/src/chip/trace/mod.rs: -------------------------------------------------------------------------------- 1 | //! Generating the trace for the AIR given by a Chip. 2 | //! 3 | 4 | pub mod data; 5 | pub mod generator; 6 | pub mod writer; 7 | -------------------------------------------------------------------------------- /starkyx/src/chip/trace/writer/public.rs: -------------------------------------------------------------------------------- 1 | use core::hash::Hash; 2 | 3 | use super::AirWriter; 4 | use crate::chip::memory::map::MemoryMap; 5 | use crate::chip::register::memory::MemorySlice; 6 | use crate::math::prelude::*; 7 | 8 | pub struct PublicWriter<'a, F: PartialEq + Eq + Hash> { 9 | public_values: &'a mut [F], 10 | memory: &'a mut MemoryMap, 11 | height: usize, 12 | } 13 | 14 | impl<'a, F: PartialEq + Eq + Hash> PublicWriter<'a, F> { 15 | pub fn new(public_values: &'a mut [F], memory: &'a mut MemoryMap, height: usize) -> Self { 16 | Self { 17 | public_values, 18 | memory, 19 | height, 20 | } 21 | } 22 | } 23 | 24 | impl<'a, F: Field> AirWriter for PublicWriter<'a, F> { 25 | type Field = F; 26 | 27 | fn read_slice(&self, memory_slice: &MemorySlice) -> &[F] { 28 | match memory_slice { 29 | MemorySlice::Public(index, length) => &self.public_values[*index..*index + *length], 30 | _ => panic!("Invalid memory slice for reading from public writer"), 31 | } 32 | } 33 | 34 | fn write_slice(&mut self, memory_slice: &MemorySlice, value: &[F]) { 35 | match memory_slice { 36 | MemorySlice::Public(index, length) => { 37 | self.public_values[*index..*index + *length].copy_from_slice(value); 38 | } 39 | _ => panic!("Can only write to public memory with public writer"), 40 | } 41 | } 42 | 43 | fn memory(&self) -> &MemoryMap { 44 | self.memory 45 | } 46 | 47 | fn memory_mut(&mut self) -> &mut MemoryMap { 48 | self.memory 49 | } 50 | 51 | fn row_index(&self) -> Option { 52 | None 53 | } 54 | 55 | fn height(&self) -> usize { 56 | self.height 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /starkyx/src/chip/trace/writer/row.rs: -------------------------------------------------------------------------------- 1 | use core::hash::Hash; 2 | 3 | use super::AirWriter; 4 | use crate::chip::memory::map::MemoryMap; 5 | use crate::chip::register::memory::MemorySlice; 6 | use crate::math::prelude::*; 7 | 8 | pub struct RowWriter<'a, F: PartialEq + Eq + Hash> { 9 | row: &'a mut [F], 10 | public_values: &'a [F], 11 | memory: &'a mut MemoryMap, 12 | row_index: usize, 13 | height: usize, 14 | } 15 | 16 | impl<'a, F: PartialEq + Eq + Hash> RowWriter<'a, F> { 17 | pub fn new( 18 | row: &'a mut [F], 19 | public_values: &'a [F], 20 | memory: &'a mut MemoryMap, 21 | row_index: usize, 22 | height: usize, 23 | ) -> Self { 24 | Self { 25 | row, 26 | public_values, 27 | memory, 28 | row_index, 29 | height, 30 | } 31 | } 32 | } 33 | 34 | impl<'a, F: Field> AirWriter for RowWriter<'a, F> { 35 | type Field = F; 36 | 37 | fn read_slice(&self, memory_slice: &MemorySlice) -> &[F] { 38 | match memory_slice { 39 | MemorySlice::Local(index, length) => &self.row[*index..*index + *length], 40 | MemorySlice::Public(index, length) => &self.public_values[*index..*index + *length], 41 | _ => panic!("Invalid memory slice for reading from row writer"), 42 | } 43 | } 44 | 45 | fn write_slice(&mut self, memory_slice: &MemorySlice, value: &[F]) { 46 | match memory_slice { 47 | MemorySlice::Local(index, length) => { 48 | self.row[*index..*index + *length].copy_from_slice(value); 49 | } 50 | _ => panic!("Invalid memory slice for writing with row writer"), 51 | } 52 | } 53 | 54 | fn memory(&self) -> &MemoryMap { 55 | self.memory 56 | } 57 | 58 | fn memory_mut(&mut self) -> &mut MemoryMap { 59 | self.memory 60 | } 61 | 62 | fn row_index(&self) -> Option { 63 | Some(self.row_index) 64 | } 65 | 66 | fn height(&self) -> usize { 67 | self.height 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /starkyx/src/chip/trace/writer/window.rs: -------------------------------------------------------------------------------- 1 | use core::hash::Hash; 2 | 3 | use super::AirWriter; 4 | use crate::chip::memory::map::MemoryMap; 5 | use crate::chip::register::memory::MemorySlice; 6 | use crate::math::prelude::*; 7 | use crate::trace::window::TraceWindowMut; 8 | 9 | pub struct WindowWriter<'a, F: PartialEq + Eq + Hash> { 10 | pub(crate) window: TraceWindowMut<'a, F>, 11 | public_values: &'a [F], 12 | memory: &'a mut MemoryMap, 13 | current_row: usize, 14 | height: usize, 15 | } 16 | 17 | impl<'a, F: PartialEq + Eq + Hash> WindowWriter<'a, F> { 18 | pub fn new( 19 | window: TraceWindowMut<'a, F>, 20 | public_values: &'a [F], 21 | memory: &'a mut MemoryMap, 22 | current_row: usize, 23 | height: usize, 24 | ) -> Self { 25 | Self { 26 | window, 27 | public_values, 28 | memory, 29 | current_row, 30 | height, 31 | } 32 | } 33 | } 34 | impl<'a, F: Field> AirWriter for WindowWriter<'a, F> { 35 | type Field = F; 36 | 37 | fn read_slice(&self, memory_slice: &MemorySlice) -> &[F] { 38 | match memory_slice { 39 | MemorySlice::Local(index, length) => &self.window.local_slice[*index..*index + *length], 40 | MemorySlice::Public(index, length) => &self.public_values[*index..*index + *length], 41 | _ => panic!("Can only read from local and public registers using window writer"), 42 | } 43 | } 44 | 45 | fn write_slice(&mut self, memory_slice: &MemorySlice, value: &[F]) { 46 | match memory_slice { 47 | MemorySlice::Local(index, length) => { 48 | self.window.local_slice[*index..*index + *length].copy_from_slice(value); 49 | } 50 | MemorySlice::Next(index, length) => { 51 | if !self.window.is_last_row { 52 | self.window.next_slice[*index..*index + *length].copy_from_slice(value); 53 | } 54 | } 55 | _ => panic!("Window writer cannot write to non trace values"), 56 | } 57 | } 58 | 59 | fn memory(&self) -> &MemoryMap { 60 | self.memory 61 | } 62 | 63 | fn memory_mut(&mut self) -> &mut MemoryMap { 64 | self.memory 65 | } 66 | 67 | fn row_index(&self) -> Option { 68 | Some(self.current_row) 69 | } 70 | 71 | fn height(&self) -> usize { 72 | self.height 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /starkyx/src/chip/uint/bytes/bit_operations/mod.rs: -------------------------------------------------------------------------------- 1 | pub use crate::math::prelude::*; 2 | 3 | pub mod and; 4 | pub mod not; 5 | pub mod rotate; 6 | pub mod shift; 7 | pub mod xor; 8 | 9 | pub mod util { 10 | 11 | #[inline] 12 | pub fn u8_to_bits_le(x: u8) -> [u8; 8] { 13 | core::array::from_fn(|i| (x >> i) & 1) 14 | } 15 | 16 | #[inline] 17 | pub fn bits_u8_to_val(bits: &[u8]) -> u8 { 18 | bits.iter().enumerate().map(|(i, b)| b << i).sum::() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /starkyx/src/chip/uint/bytes/decode.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use super::register::ByteRegister; 4 | use crate::air::parser::AirParser; 5 | use crate::air::AirConstraint; 6 | use crate::chip::builder::AirBuilder; 7 | use crate::chip::instruction::ConstraintInstruction; 8 | use crate::chip::register::array::ArrayRegister; 9 | use crate::chip::register::bit::BitRegister; 10 | use crate::chip::register::Register; 11 | use crate::chip::AirParameters; 12 | use crate::math::prelude::*; 13 | 14 | #[derive(Debug, Clone, Copy, Serialize, Deserialize)] 15 | pub struct ByteDecodeInstruction { 16 | byte: ByteRegister, 17 | bits: ArrayRegister, 18 | } 19 | 20 | impl ByteDecodeInstruction { 21 | pub fn new(byte: ByteRegister, bits: ArrayRegister) -> Self { 22 | Self { byte, bits } 23 | } 24 | } 25 | 26 | impl AirBuilder { 27 | pub fn decode_byte(&mut self, byte: &ByteRegister, bits: &ArrayRegister) 28 | where 29 | L::Instruction: From, 30 | { 31 | let instruction = ByteDecodeInstruction::new(*byte, *bits); 32 | self.register_instruction(instruction); 33 | } 34 | } 35 | 36 | impl AirConstraint for ByteDecodeInstruction { 37 | fn eval(&self, parser: &mut AP) { 38 | let byte = self.byte.eval(parser); 39 | let bits = self.bits.eval_array::<_, 8>(parser); 40 | 41 | let mut acc = parser.zero(); 42 | for (i, bit) in bits.into_iter().enumerate() { 43 | let two_i = parser.constant(AP::Field::from_canonical_u32(1 << i as u32)); 44 | let two_i_bit = parser.mul(two_i, bit); 45 | acc = parser.add(acc, two_i_bit); 46 | } 47 | parser.assert_eq(byte, acc); 48 | } 49 | } 50 | 51 | impl ConstraintInstruction for ByteDecodeInstruction {} 52 | 53 | #[cfg(test)] 54 | mod tests { 55 | use rand::{thread_rng, Rng}; 56 | 57 | use super::*; 58 | pub use crate::chip::builder::tests::*; 59 | 60 | #[derive(Debug, Clone, Copy, Serialize, Deserialize)] 61 | struct DecodeTest; 62 | 63 | impl AirParameters for DecodeTest { 64 | type Field = GoldilocksField; 65 | type CubicParams = GoldilocksCubicParameters; 66 | 67 | type Instruction = ByteDecodeInstruction; 68 | 69 | const NUM_FREE_COLUMNS: usize = 16; 70 | } 71 | 72 | #[test] 73 | fn test_byte_decode() { 74 | type F = GoldilocksField; 75 | type L = DecodeTest; 76 | type SC = PoseidonGoldilocksStarkConfig; 77 | 78 | let mut builder = AirBuilder::::new(); 79 | 80 | let byte = builder.alloc::(); 81 | let bits = builder.alloc_array::(8); 82 | 83 | builder.decode_byte(&byte, &bits); 84 | 85 | let (air, trace_data) = builder.build(); 86 | 87 | let num_rows = 1 << 9; 88 | let generator = ArithmeticGenerator::::new(trace_data, num_rows); 89 | 90 | let writer = generator.new_writer(); 91 | let mut rng = thread_rng(); 92 | for i in 0..num_rows { 93 | let byte_val: u8 = rng.gen(); 94 | writer.write(&byte, &F::from_canonical_u8(byte_val), i); 95 | for (j, bit) in bits.into_iter().enumerate() { 96 | let bit_val = (byte_val >> j) & 1; 97 | writer.write(&bit, &F::from_canonical_u8(bit_val), i); 98 | } 99 | } 100 | let stark = Starky::new(air); 101 | let config = SC::standard_fast_config(num_rows); 102 | 103 | // Generate proof and verify as a stark 104 | test_starky(&stark, &config, &generator, &[]); 105 | 106 | // Test the recursive proof. 107 | test_recursive_starky(stark, config, generator, &[]); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /starkyx/src/chip/uint/bytes/lookup_table/builder_operations.rs: -------------------------------------------------------------------------------- 1 | use crate::chip::uint::bytes::operations::value::ByteOperation; 2 | use crate::chip::uint::bytes::register::ByteRegister; 3 | 4 | #[derive(Debug, Clone)] 5 | pub struct ByteLookupOperations { 6 | pub trace_operations: Vec>, 7 | pub public_operations: Vec>, 8 | } 9 | 10 | impl ByteLookupOperations { 11 | pub fn new() -> Self { 12 | ByteLookupOperations { 13 | trace_operations: Vec::new(), 14 | public_operations: Vec::new(), 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /starkyx/src/chip/uint/bytes/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod bit_operations; 2 | pub mod decode; 3 | pub mod lookup_table; 4 | pub mod operations; 5 | pub mod register; 6 | pub mod util; 7 | -------------------------------------------------------------------------------- /starkyx/src/chip/uint/bytes/operations/instruction.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use super::value::ByteOperation; 4 | use crate::air::parser::AirParser; 5 | use crate::air::AirConstraint; 6 | use crate::chip::instruction::Instruction; 7 | use crate::chip::trace::writer::{AirWriter, TraceWriter}; 8 | use crate::chip::uint::bytes::register::ByteRegister; 9 | use crate::math::prelude::*; 10 | 11 | #[derive(Debug, Clone, Serialize, Deserialize)] 12 | pub struct ByteOperationInstruction { 13 | inner: ByteOperation, 14 | global: bool, 15 | } 16 | 17 | impl ByteOperationInstruction { 18 | pub fn new(inner: ByteOperation, global: bool) -> Self { 19 | ByteOperationInstruction { inner, global } 20 | } 21 | } 22 | 23 | impl AirConstraint for ByteOperationInstruction { 24 | fn eval(&self, _parser: &mut AP) {} 25 | } 26 | 27 | impl Instruction for ByteOperationInstruction { 28 | fn write(&self, writer: &TraceWriter, row_index: usize) { 29 | if self.global && row_index != 0 { 30 | return; 31 | } 32 | self.inner.write(writer, row_index); 33 | } 34 | 35 | fn write_to_air(&self, writer: &mut impl AirWriter) { 36 | if let Some(r) = writer.row_index() { 37 | if self.global && r != 0 { 38 | return; 39 | } 40 | } 41 | self.inner.write_to_air(writer); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /starkyx/src/chip/uint/bytes/operations/mod.rs: -------------------------------------------------------------------------------- 1 | //! Byte operations with a lookup table 2 | 3 | use self::instruction::ByteOperationInstruction; 4 | use self::value::ByteOperation; 5 | use super::lookup_table::builder_operations::ByteLookupOperations; 6 | use super::register::ByteRegister; 7 | use crate::chip::builder::AirBuilder; 8 | use crate::chip::AirParameters; 9 | 10 | pub mod instruction; 11 | pub mod value; 12 | 13 | pub const OPCODE_AND: u8 = 101; 14 | pub const OPCODE_XOR: u8 = 102; 15 | pub const OPCODE_SHR: u8 = 103; 16 | pub const OPCODE_ROT: u8 = 104; 17 | pub const OPCODE_NOT: u8 = 105; 18 | pub const OPCODE_RANGE: u8 = 106; 19 | pub const OPCODE_SHR_CARRY: u8 = 107; 20 | 21 | pub const NUM_BIT_OPPS: usize = 6; 22 | 23 | pub const OPCODE_INDICES: [u8; NUM_BIT_OPPS + 1] = [ 24 | OPCODE_AND, 25 | OPCODE_XOR, 26 | OPCODE_SHR, 27 | OPCODE_SHR_CARRY, 28 | OPCODE_ROT, 29 | OPCODE_NOT, 30 | OPCODE_RANGE, 31 | ]; 32 | 33 | impl AirBuilder { 34 | pub fn set_byte_operation( 35 | &mut self, 36 | op: &ByteOperation, 37 | lookup: &mut ByteLookupOperations, 38 | ) where 39 | L::Instruction: From, 40 | { 41 | let instr = ByteOperationInstruction::new(*op, false); 42 | lookup.trace_operations.push(*op); 43 | self.register_instruction(instr); 44 | } 45 | 46 | pub fn set_public_inputs_byte_operation( 47 | &mut self, 48 | op: &ByteOperation, 49 | lookup: &mut ByteLookupOperations, 50 | ) where 51 | L::Instruction: From, 52 | { 53 | let instr = ByteOperationInstruction::new(*op, true); 54 | lookup.public_operations.push(*op); 55 | self.register_global_instruction(instr); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /starkyx/src/chip/uint/bytes/register.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use crate::chip::register::cell::CellType; 4 | use crate::chip::register::element::ElementRegister; 5 | use crate::chip::register::memory::MemorySlice; 6 | use crate::chip::register::{Register, RegisterSerializable, RegisterSized}; 7 | 8 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] 9 | pub struct ByteRegister(MemorySlice); 10 | 11 | impl ByteRegister { 12 | pub fn element(&self) -> ElementRegister { 13 | ElementRegister::from_register_unsafe(self.0) 14 | } 15 | } 16 | 17 | impl RegisterSerializable for ByteRegister { 18 | const CELL: CellType = CellType::Element; 19 | 20 | fn register(&self) -> &MemorySlice { 21 | &self.0 22 | } 23 | 24 | fn from_register_unsafe(register: MemorySlice) -> Self { 25 | Self(register) 26 | } 27 | } 28 | 29 | impl RegisterSized for ByteRegister { 30 | fn size_of() -> usize { 31 | 1 32 | } 33 | } 34 | 35 | impl Register for ByteRegister { 36 | type Value = T; 37 | 38 | fn value_from_slice(slice: &[T]) -> Self::Value { 39 | slice[0] 40 | } 41 | 42 | fn align(value: &Self::Value) -> &[T] { 43 | core::slice::from_ref(value) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /starkyx/src/chip/uint/bytes/util.rs: -------------------------------------------------------------------------------- 1 | use crate::air::parser::AirParser; 2 | use crate::math::prelude::*; 3 | 4 | pub fn byte_decomposition( 5 | element: AP::Var, 6 | values: &[AP::Var; N], 7 | parser: &mut AP, 8 | ) -> AP::Var { 9 | let two_powers: [_; N] = core::array::from_fn(|i| AP::Field::from_canonical_u32(1 << (8 * i))); 10 | 11 | let mut element_value = parser.zero(); 12 | for (value, power) in values.iter().zip(two_powers) { 13 | let value_two_pow = parser.mul_const(*value, power); 14 | element_value = parser.add(element_value, value_two_pow); 15 | } 16 | 17 | parser.sub(element, element_value) 18 | } 19 | 20 | pub fn byte_decomposition_value( 21 | element: AP::Var, 22 | values: &[AP::Var; N], 23 | parser: &mut AP, 24 | ) -> AP::Var { 25 | let two_powers: [_; N] = core::array::from_fn(|i| AP::Field::from_canonical_u32(1 << (8 * i))); 26 | 27 | let mut element_value = parser.zero(); 28 | for (value, power) in values.iter().zip(two_powers) { 29 | let value_two_pow = parser.mul_const(*value, power); 30 | element_value = parser.add(element_value, value_two_pow); 31 | } 32 | 33 | parser.sub(element, element_value) 34 | } 35 | -------------------------------------------------------------------------------- /starkyx/src/chip/uint/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod bytes; 2 | pub mod operations; 3 | pub mod register; 4 | pub mod util; 5 | -------------------------------------------------------------------------------- /starkyx/src/chip/uint/operations/and.rs: -------------------------------------------------------------------------------- 1 | use crate::chip::builder::AirBuilder; 2 | use crate::chip::uint::bytes::lookup_table::builder_operations::ByteLookupOperations; 3 | use crate::chip::uint::bytes::operations::instruction::ByteOperationInstruction; 4 | use crate::chip::uint::bytes::operations::value::ByteOperation; 5 | use crate::chip::uint::register::ByteArrayRegister; 6 | use crate::chip::AirParameters; 7 | 8 | impl AirBuilder { 9 | pub fn set_bitwise_and( 10 | &mut self, 11 | a: &ByteArrayRegister, 12 | b: &ByteArrayRegister, 13 | result: &ByteArrayRegister, 14 | operations: &mut ByteLookupOperations, 15 | ) where 16 | L::Instruction: From, 17 | { 18 | for ((a_byte, b_byte), result_byte) in a 19 | .to_le_bytes() 20 | .iter() 21 | .zip(b.to_le_bytes().iter()) 22 | .zip(result.to_le_bytes().iter()) 23 | { 24 | let and = ByteOperation::And(a_byte, b_byte, result_byte); 25 | self.set_byte_operation(&and, operations); 26 | } 27 | } 28 | 29 | pub fn bitwise_and( 30 | &mut self, 31 | a: &ByteArrayRegister, 32 | b: &ByteArrayRegister, 33 | operations: &mut ByteLookupOperations, 34 | ) -> ByteArrayRegister 35 | where 36 | L::Instruction: From, 37 | { 38 | let result = self.alloc::>(); 39 | self.set_bitwise_and(a, b, &result, operations); 40 | result 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /starkyx/src/chip/uint/operations/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod add; 2 | pub mod and; 3 | pub mod instruction; 4 | pub mod not; 5 | pub mod rotate; 6 | pub mod shr; 7 | pub mod xor; 8 | -------------------------------------------------------------------------------- /starkyx/src/chip/uint/operations/not.rs: -------------------------------------------------------------------------------- 1 | use crate::chip::builder::AirBuilder; 2 | use crate::chip::uint::bytes::lookup_table::builder_operations::ByteLookupOperations; 3 | use crate::chip::uint::bytes::operations::instruction::ByteOperationInstruction; 4 | use crate::chip::uint::bytes::operations::value::ByteOperation; 5 | use crate::chip::uint::register::ByteArrayRegister; 6 | use crate::chip::AirParameters; 7 | 8 | impl AirBuilder { 9 | pub fn set_bitwise_not( 10 | &mut self, 11 | a: &ByteArrayRegister, 12 | result: &ByteArrayRegister, 13 | operations: &mut ByteLookupOperations, 14 | ) where 15 | L::Instruction: From, 16 | { 17 | for (a_byte, result_byte) in a.to_le_bytes().iter().zip(result.to_le_bytes().iter()) { 18 | let not = ByteOperation::Not(a_byte, result_byte); 19 | self.set_byte_operation(¬, operations); 20 | } 21 | } 22 | 23 | pub fn bitwise_not( 24 | &mut self, 25 | a: &ByteArrayRegister, 26 | operations: &mut ByteLookupOperations, 27 | ) -> ByteArrayRegister 28 | where 29 | L::Instruction: From, 30 | { 31 | let result = self.alloc::>(); 32 | self.set_bitwise_not(a, &result, operations); 33 | result 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /starkyx/src/chip/uint/operations/rotate.rs: -------------------------------------------------------------------------------- 1 | use core::array::from_fn; 2 | 3 | use crate::chip::builder::AirBuilder; 4 | use crate::chip::register::Register; 5 | use crate::chip::uint::bytes::lookup_table::builder_operations::ByteLookupOperations; 6 | use crate::chip::uint::bytes::operations::instruction::ByteOperationInstruction; 7 | use crate::chip::uint::bytes::operations::value::ByteOperation; 8 | use crate::chip::uint::bytes::register::ByteRegister; 9 | use crate::chip::uint::register::ByteArrayRegister; 10 | use crate::chip::AirParameters; 11 | use crate::math::prelude::*; 12 | 13 | impl AirBuilder { 14 | pub fn set_bit_rotate_right( 15 | &mut self, 16 | a: &ByteArrayRegister, 17 | rotation: usize, 18 | result: &ByteArrayRegister, 19 | operations: &mut ByteLookupOperations, 20 | ) where 21 | L::Instruction: From, 22 | { 23 | let result_bytes = result.to_le_bytes(); 24 | 25 | let rotation = rotation % (N * 8); 26 | let byte_rotation = rotation / 8; 27 | let bit_rotation = rotation % 8; 28 | 29 | let mult = L::Field::from_canonical_u32(1 << (8 - bit_rotation)); 30 | 31 | let a_bytes = a.to_le_bytes(); 32 | let a_bytes_rotated: [_; N] = from_fn(|i| a_bytes.get((i + byte_rotation) % N)); 33 | 34 | let (last_rot, last_carry) = (self.alloc::(), self.alloc::()); 35 | let shr_carry = ByteOperation::ShrCarry( 36 | a_bytes_rotated[N - 1], 37 | bit_rotation as u8, 38 | last_rot, 39 | last_carry, 40 | ); 41 | self.set_byte_operation(&shr_carry, operations); 42 | 43 | let mut carry = last_carry.expr(); 44 | for i in (0..N - 1).rev() { 45 | let (shift_res, next_carry) = 46 | (self.alloc::(), self.alloc::()); 47 | let shr_carry = ByteOperation::ShrCarry( 48 | a_bytes_rotated[i], 49 | bit_rotation as u8, 50 | shift_res, 51 | next_carry, 52 | ); 53 | self.set_byte_operation(&shr_carry, operations); 54 | let expected_res = shift_res.expr() + carry.clone() * mult; 55 | self.set_to_expression(&result_bytes.get(i), expected_res); 56 | carry = next_carry.expr(); 57 | } 58 | 59 | // Constraint the last byte with the carry from the first 60 | let expected_res = last_rot.expr() + carry.clone() * mult; 61 | self.set_to_expression(&result_bytes.get(N - 1), expected_res); 62 | } 63 | 64 | pub fn bit_rotate_right( 65 | &mut self, 66 | a: &ByteArrayRegister, 67 | rotation: usize, 68 | operations: &mut ByteLookupOperations, 69 | ) -> ByteArrayRegister 70 | where 71 | L::Instruction: From, 72 | { 73 | let result = self.alloc::>(); 74 | self.set_bit_rotate_right(a, rotation, &result, operations); 75 | result 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /starkyx/src/chip/uint/operations/shr.rs: -------------------------------------------------------------------------------- 1 | use crate::chip::arithmetic::expression::ArithmeticExpression; 2 | use crate::chip::builder::AirBuilder; 3 | use crate::chip::register::Register; 4 | use crate::chip::uint::bytes::lookup_table::builder_operations::ByteLookupOperations; 5 | use crate::chip::uint::bytes::operations::instruction::ByteOperationInstruction; 6 | use crate::chip::uint::bytes::operations::value::ByteOperation; 7 | use crate::chip::uint::bytes::register::ByteRegister; 8 | use crate::chip::uint::register::ByteArrayRegister; 9 | use crate::chip::AirParameters; 10 | use crate::math::prelude::*; 11 | 12 | impl AirBuilder { 13 | pub fn bit_shr( 14 | &mut self, 15 | a: &ByteArrayRegister, 16 | shift: usize, 17 | operations: &mut ByteLookupOperations, 18 | ) -> ByteArrayRegister 19 | where 20 | L::Instruction: From, 21 | { 22 | let result = self.alloc::>(); 23 | self.set_bit_shr(a, shift, &result, operations); 24 | result 25 | } 26 | 27 | pub fn set_bit_shr( 28 | &mut self, 29 | a: &ByteArrayRegister, 30 | shift: usize, 31 | result: &ByteArrayRegister, 32 | operations: &mut ByteLookupOperations, 33 | ) where 34 | L::Instruction: From, 35 | { 36 | let a_bytes = a.to_le_bytes(); 37 | let result_bytes = result.to_le_bytes(); 38 | 39 | let shift = shift % (N * 8); 40 | let byte_shift = shift / 8; 41 | let bit_shift = shift % 8; 42 | 43 | for i in (N - byte_shift)..N { 44 | self.assert_zero(&result_bytes.get(i)); 45 | } 46 | 47 | let mult = L::Field::from_canonical_u32(1 << (8 - bit_shift)); 48 | let mut carry = ArithmeticExpression::zero(); 49 | for i in (0..N - byte_shift).rev() { 50 | let (shift_res, next_carry) = 51 | (self.alloc::(), self.alloc::()); 52 | let shr_carry = ByteOperation::ShrCarry( 53 | a_bytes.get(i + byte_shift), 54 | bit_shift as u8, 55 | shift_res, 56 | next_carry, 57 | ); 58 | self.set_byte_operation(&shr_carry, operations); 59 | let expected_res = shift_res.expr() + carry.clone() * mult; 60 | self.set_to_expression(&result_bytes.get(i), expected_res); 61 | carry = next_carry.expr(); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /starkyx/src/chip/uint/operations/xor.rs: -------------------------------------------------------------------------------- 1 | use crate::chip::builder::AirBuilder; 2 | use crate::chip::uint::bytes::lookup_table::builder_operations::ByteLookupOperations; 3 | use crate::chip::uint::bytes::operations::instruction::ByteOperationInstruction; 4 | use crate::chip::uint::bytes::operations::value::ByteOperation; 5 | use crate::chip::uint::register::ByteArrayRegister; 6 | use crate::chip::AirParameters; 7 | 8 | impl AirBuilder { 9 | pub fn set_bitwise_xor( 10 | &mut self, 11 | a: &ByteArrayRegister, 12 | b: &ByteArrayRegister, 13 | result: &ByteArrayRegister, 14 | operations: &mut ByteLookupOperations, 15 | ) where 16 | L::Instruction: From, 17 | { 18 | for ((a_byte, b_byte), result_byte) in a 19 | .to_le_bytes() 20 | .iter() 21 | .zip(b.to_le_bytes().iter()) 22 | .zip(result.to_le_bytes().iter()) 23 | { 24 | let xor = ByteOperation::Xor(a_byte, b_byte, result_byte); 25 | self.set_byte_operation(&xor, operations); 26 | } 27 | } 28 | 29 | pub fn bitwise_xor( 30 | &mut self, 31 | a: &ByteArrayRegister, 32 | b: &ByteArrayRegister, 33 | operations: &mut ByteLookupOperations, 34 | ) -> ByteArrayRegister 35 | where 36 | L::Instruction: From, 37 | { 38 | let result = self.alloc::>(); 39 | self.set_bitwise_xor(a, b, &result, operations); 40 | result 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /starkyx/src/chip/uint/util.rs: -------------------------------------------------------------------------------- 1 | use crate::math::field::{Field, PrimeField64}; 2 | 3 | #[inline] 4 | pub fn u32_to_le_field_bytes(value: u32) -> [F; 4] { 5 | value.to_le_bytes().map(F::from_canonical_u8) 6 | } 7 | 8 | #[inline] 9 | pub fn u32_from_le_field_bytes(bytes: &[F; 4]) -> u32 { 10 | u32::from_le_bytes(bytes.map(|x| x.as_canonical_u64() as u8)) 11 | } 12 | 13 | #[inline] 14 | pub fn u64_to_le_field_bytes(value: u64) -> [F; 8] { 15 | value.to_le_bytes().map(F::from_canonical_u8) 16 | } 17 | 18 | #[inline] 19 | pub fn u64_from_le_field_bytes(bytes: &[F; 8]) -> u64 { 20 | u64::from_le_bytes(bytes.map(|x| x.as_canonical_u64() as u8)) 21 | } 22 | -------------------------------------------------------------------------------- /starkyx/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "std"), no_std)] 2 | #![allow(clippy::new_without_default)] 3 | #![feature(bigint_helper_methods)] 4 | 5 | extern crate alloc; 6 | 7 | pub mod air; 8 | pub mod chip; 9 | pub mod machine; 10 | pub mod math; 11 | pub mod maybe_rayon; 12 | pub mod polynomial; 13 | pub mod trace; 14 | pub mod utils; 15 | 16 | #[cfg(feature = "plonky2")] 17 | pub mod plonky2; 18 | 19 | pub mod prelude { 20 | pub use crate::air::parser::AirParser; 21 | pub use crate::air::AirConstraint; 22 | pub use crate::chip::instruction::empty::EmptyInstruction; 23 | pub use crate::chip::trace::writer::data::AirWriterData; 24 | pub use crate::chip::trace::writer::AirWriter; 25 | pub use crate::chip::AirParameters; 26 | pub use crate::machine::builder::Builder; 27 | pub use crate::machine::bytes::builder::BytesBuilder; 28 | pub use crate::machine::bytes::stark::ByteStark; 29 | pub use crate::machine::emulated::builder::EmulatedBuilder; 30 | pub use crate::machine::emulated::stark::EmulatedStark; 31 | pub use crate::machine::stark::builder::StarkBuilder; 32 | pub use crate::machine::stark::Stark; 33 | pub use crate::math::prelude::*; 34 | pub use crate::maybe_rayon::*; 35 | } 36 | -------------------------------------------------------------------------------- /starkyx/src/machine/bytes/air.rs: -------------------------------------------------------------------------------- 1 | use core::marker::PhantomData; 2 | 3 | use plonky2::field::extension::Extendable; 4 | use plonky2::fri::oracle::PolynomialBatch; 5 | use plonky2::hash::hash_types::RichField; 6 | use plonky2::util::timing::TimingTree; 7 | use serde::{Deserialize, Serialize}; 8 | 9 | use crate::air::{RAir, RAirData, RoundDatum}; 10 | use crate::chip::trace::writer::TraceWriter; 11 | use crate::chip::uint::bytes::operations::NUM_BIT_OPPS; 12 | use crate::chip::uint::operations::instruction::UintInstruction; 13 | use crate::chip::{AirParameters, Chip}; 14 | use crate::math::prelude::*; 15 | use crate::maybe_rayon::*; 16 | use crate::plonky2::stark::config::{CurtaConfig, StarkyConfig}; 17 | use crate::plonky2::stark::Starky; 18 | use crate::prelude::AirParser; 19 | use crate::trace::AirTrace; 20 | 21 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] 22 | pub struct ByteParameters(pub PhantomData<(F, E)>); 23 | 24 | impl> AirParameters for ByteParameters { 25 | type Field = F; 26 | type CubicParams = E; 27 | 28 | type Instruction = UintInstruction; 29 | 30 | const NUM_ARITHMETIC_COLUMNS: usize = 0; 31 | const NUM_FREE_COLUMNS: usize = 15; 32 | const EXTENDED_COLUMNS: usize = 45; 33 | } 34 | 35 | #[derive(Debug, Clone, Serialize, Deserialize)] 36 | #[serde(bound = "")] 37 | pub struct ByteAir>(pub(crate) Chip>); 38 | 39 | impl> RAirData for ByteAir { 40 | fn width(&self) -> usize { 41 | self.0.width() 42 | } 43 | 44 | fn constraint_degree(&self) -> usize { 45 | self.0.constraint_degree() 46 | } 47 | 48 | fn round_data(&self) -> Vec { 49 | let total = ByteParameters::::num_columns(); 50 | let execution_trace_length = self.0.execution_trace_length; 51 | let extended_trace_length = total - execution_trace_length; 52 | 53 | vec![ 54 | RoundDatum::new(NUM_BIT_OPPS + 1, (0, 0), 0), 55 | RoundDatum::new( 56 | execution_trace_length - (NUM_BIT_OPPS + 1), 57 | (0, 0), 58 | self.0.num_challenges, 59 | ), 60 | RoundDatum::new(extended_trace_length, (0, self.0.num_global_values), 0), 61 | ] 62 | } 63 | 64 | fn num_public_inputs(&self) -> usize { 65 | self.0.num_public_inputs() 66 | } 67 | } 68 | 69 | impl> RAir for ByteAir 70 | where 71 | Chip>: RAir, 72 | { 73 | fn eval(&self, parser: &mut AP) { 74 | self.0.eval(parser) 75 | } 76 | 77 | fn eval_global(&self, parser: &mut AP) { 78 | self.0.eval_global(parser) 79 | } 80 | } 81 | 82 | pub fn get_preprocessed_byte_trace( 83 | lookup_writer: &TraceWriter, 84 | lookup_config: &StarkyConfig, 85 | lookup_stark: &Starky>, 86 | ) -> PolynomialBatch 87 | where 88 | F: RichField + Extendable, 89 | E: CubicParameters, 90 | C: CurtaConfig, 91 | { 92 | let lookup_execution_trace_values = lookup_writer 93 | .read_trace() 94 | .unwrap() 95 | .rows_par() 96 | .flat_map(|row| row[(NUM_BIT_OPPS + 1)..lookup_stark.air.0.execution_trace_length].to_vec()) 97 | .collect::>(); 98 | 99 | let lookup_execution_trace = AirTrace { 100 | values: lookup_execution_trace_values, 101 | width: (lookup_stark.air.0.execution_trace_length - (NUM_BIT_OPPS + 1)), 102 | }; 103 | 104 | lookup_config.commit(&lookup_execution_trace, &mut TimingTree::default()) 105 | } 106 | -------------------------------------------------------------------------------- /starkyx/src/machine/bytes/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod air; 2 | pub mod builder; 3 | pub mod ops; 4 | pub mod proof; 5 | pub mod stark; 6 | -------------------------------------------------------------------------------- /starkyx/src/machine/bytes/proof.rs: -------------------------------------------------------------------------------- 1 | use plonky2::field::extension::Extendable; 2 | use plonky2::hash::hash_types::RichField; 3 | use plonky2::iop::target::Target; 4 | 5 | use crate::plonky2::stark::config::CurtaConfig; 6 | use crate::plonky2::stark::proof::{ 7 | AirProof, AirProofTarget, StarkProofChallenges, StarkProofChallengesTarget, 8 | }; 9 | 10 | #[derive(Debug, Clone, PartialEq, Eq)] 11 | pub struct ByteStarkProof, C: CurtaConfig, const D: usize> { 12 | pub main_proof: AirProof, 13 | pub lookup_proof: AirProof, 14 | pub global_values: Vec, 15 | } 16 | 17 | #[derive(Debug, Clone, PartialEq, Eq)] 18 | pub struct ByteStarkProofTarget { 19 | pub main_proof: AirProofTarget, 20 | pub lookup_proof: AirProofTarget, 21 | pub global_values: Vec, 22 | } 23 | 24 | pub struct ByteStarkChallenges, const D: usize> { 25 | pub(crate) main_challenges: StarkProofChallenges, 26 | pub(crate) lookup_challenges: StarkProofChallenges, 27 | } 28 | 29 | pub struct ByteStarkChallengesTarget { 30 | pub(crate) main_challenges: StarkProofChallengesTarget, 31 | pub(crate) lookup_challenges: StarkProofChallengesTarget, 32 | } 33 | -------------------------------------------------------------------------------- /starkyx/src/machine/ec/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod builder; 2 | pub mod scalar_mul; 3 | -------------------------------------------------------------------------------- /starkyx/src/machine/ec/scalar_mul.rs: -------------------------------------------------------------------------------- 1 | use crate::chip::ec::EllipticCurve; 2 | use crate::chip::field::register::FieldRegister; 3 | use crate::chip::memory::pointer::slice::Slice; 4 | use crate::chip::register::bit::BitRegister; 5 | use crate::chip::register::element::ElementRegister; 6 | 7 | pub struct DoubleAddData { 8 | pub process_id: ElementRegister, 9 | pub temp_x_ptr: Slice>, 10 | pub temp_y_ptr: Slice>, 11 | pub bit: BitRegister, 12 | pub start_bit: BitRegister, 13 | pub end_bit: BitRegister, 14 | } 15 | -------------------------------------------------------------------------------- /starkyx/src/machine/emulated/builder.rs: -------------------------------------------------------------------------------- 1 | use super::stark::EmulatedStark; 2 | use super::RangeParameters; 3 | use crate::chip::builder::AirBuilder; 4 | use crate::chip::register::array::ArrayRegister; 5 | use crate::chip::register::element::ElementRegister; 6 | use crate::chip::register::memory::MemorySlice; 7 | use crate::chip::register::RegisterSerializable; 8 | use crate::chip::AirParameters; 9 | use crate::machine::builder::Builder; 10 | use crate::plonky2::stark::config::{CurtaConfig, StarkyConfig}; 11 | use crate::plonky2::stark::Starky; 12 | 13 | pub(crate) const NUM_LOOKUP_ROWS: usize = 1 << 16; 14 | 15 | pub struct EmulatedBuilder { 16 | pub api: AirBuilder, 17 | pub clk: ElementRegister, 18 | } 19 | 20 | impl Builder for EmulatedBuilder { 21 | type Field = L::Field; 22 | type CubicParams = L::CubicParams; 23 | type Parameters = L; 24 | type Instruction = L::Instruction; 25 | 26 | fn api(&mut self) -> &mut AirBuilder { 27 | &mut self.api 28 | } 29 | 30 | fn clk(&mut self) -> ElementRegister { 31 | self.clk 32 | } 33 | } 34 | 35 | impl EmulatedBuilder { 36 | pub fn new() -> Self { 37 | let mut api = AirBuilder::::new(); 38 | let clk = api.clock(); 39 | api.internal_range_check = false; 40 | api.init_local_memory(); 41 | EmulatedBuilder { api, clk } 42 | } 43 | 44 | pub fn build, const D: usize>( 45 | self, 46 | num_rows: usize, 47 | ) -> EmulatedStark { 48 | let EmulatedBuilder { mut api, .. } = self; 49 | let shared_memory = api.shared_memory.clone(); 50 | let mut lookup_builder = 51 | AirBuilder::>::init(shared_memory); 52 | 53 | // Lookup table entry. 54 | let lookup_table = lookup_builder.clock(); 55 | // Allocate multiplicities. 56 | let multiplicity = lookup_builder.alloc_array::(1); 57 | 58 | let values = ArrayRegister::::from_register_unsafe(MemorySlice::Local( 59 | 0, 60 | L::NUM_ARITHMETIC_COLUMNS, 61 | )) 62 | .into_iter() 63 | .chain(api.global_arithmetic.iter().copied()) 64 | .collect::>(); 65 | 66 | let mut table_data = lookup_builder.new_lookup(&[lookup_table], &multiplicity); 67 | let lookup_values = table_data.register_lookup_values(&mut api, &values); 68 | lookup_builder.constrain_element_lookup_table(table_data); 69 | 70 | let config = StarkyConfig::::standard_fast_config(num_rows); 71 | let (air, trace_data) = api.build(); 72 | let stark = Starky::new(air); 73 | 74 | let lookup_config = StarkyConfig::::standard_fast_config(NUM_LOOKUP_ROWS); 75 | let (lookup_air, lookup_trace_data) = lookup_builder.build(); 76 | let lookup_stark = Starky::new(lookup_air); 77 | 78 | EmulatedStark { 79 | config, 80 | stark, 81 | air_data: trace_data, 82 | lookup_config, 83 | lookup_stark, 84 | lookup_air_data: lookup_trace_data, 85 | lookup_values, 86 | lookup_table, 87 | multiplicity, 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /starkyx/src/machine/emulated/mod.rs: -------------------------------------------------------------------------------- 1 | use core::marker::PhantomData; 2 | 3 | use serde::{Deserialize, Serialize}; 4 | 5 | use crate::chip::instruction::empty::EmptyInstruction; 6 | use crate::chip::AirParameters; 7 | use crate::math::prelude::*; 8 | 9 | pub mod builder; 10 | pub mod proof; 11 | pub mod stark; 12 | 13 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] 14 | pub struct RangeParameters(pub PhantomData<(F, E)>); 15 | 16 | impl> AirParameters for RangeParameters { 17 | type Field = F; 18 | type CubicParams = E; 19 | 20 | type Instruction = EmptyInstruction; 21 | 22 | const NUM_ARITHMETIC_COLUMNS: usize = 0; 23 | const NUM_FREE_COLUMNS: usize = 2; 24 | const EXTENDED_COLUMNS: usize = 6; 25 | } 26 | -------------------------------------------------------------------------------- /starkyx/src/machine/emulated/proof.rs: -------------------------------------------------------------------------------- 1 | use plonky2::field::extension::Extendable; 2 | use plonky2::hash::hash_types::RichField; 3 | use plonky2::iop::target::Target; 4 | 5 | use crate::plonky2::stark::config::CurtaConfig; 6 | use crate::plonky2::stark::proof::{ 7 | AirProof, AirProofTarget, StarkProofChallenges, StarkProofChallengesTarget, 8 | }; 9 | 10 | #[derive(Debug, Clone, PartialEq, Eq)] 11 | pub struct EmulatedStarkProof< 12 | F: RichField + Extendable, 13 | C: CurtaConfig, 14 | const D: usize, 15 | > { 16 | pub main_proof: AirProof, 17 | pub lookup_proof: AirProof, 18 | pub global_values: Vec, 19 | } 20 | 21 | #[derive(Debug, Clone, PartialEq, Eq)] 22 | pub struct EmulatedStarkProofTarget { 23 | pub main_proof: AirProofTarget, 24 | pub lookup_proof: AirProofTarget, 25 | pub global_values: Vec, 26 | } 27 | 28 | pub struct EmulatedStarkChallenges, const D: usize> { 29 | pub(crate) main_challenges: StarkProofChallenges, 30 | pub(crate) lookup_challenges: StarkProofChallenges, 31 | } 32 | 33 | pub struct EmulatedStarkChallengesTarget { 34 | pub(crate) main_challenges: StarkProofChallengesTarget, 35 | pub(crate) lookup_challenges: StarkProofChallengesTarget, 36 | } 37 | -------------------------------------------------------------------------------- /starkyx/src/machine/hash/blake/blake2b/mod.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | pub mod air; 4 | pub mod builder; 5 | pub mod data; 6 | pub mod pure; 7 | pub mod register; 8 | pub mod utils; 9 | 10 | #[derive(Debug, Clone, Copy, Serialize, Deserialize)] 11 | pub struct BLAKE2B; 12 | 13 | const NUM_MIX_ROUNDS: usize = 12; 14 | const MIX_LENGTH: usize = 8; 15 | const MSG_ARRAY_SIZE: usize = 16; 16 | const STATE_SIZE: usize = 8; 17 | const WORK_VECTOR_SIZE: usize = 16; 18 | const COMPRESS_LENGTH: usize = MIX_LENGTH * NUM_MIX_ROUNDS; 19 | 20 | pub const IV: [u64; STATE_SIZE] = [ 21 | 0x6a09e667f2bdc928, 22 | 0xbb67ae8584caa73b, 23 | 0x3c6ef372fe94f82b, 24 | 0xa54ff53a5f1d36f1, 25 | 0x510e527fade682d1, 26 | 0x9b05688c2b3e6c1f, 27 | 0x1f83d9abfb41bd6b, 28 | 0x5be0cd19137e2179, 29 | ]; 30 | 31 | // Note that for this blake2b implementation, we don't support a key input and 32 | // we assume that the output is 32 bytes 33 | // So that means the initial hash entry to be 34 | // 0x6a09e667f3bcc908 xor 0x01010020 35 | const COMPRESS_IV: [u64; STATE_SIZE] = [ 36 | 0x6a09e667f3bcc908, 37 | 0xbb67ae8584caa73b, 38 | 0x3c6ef372fe94f82b, 39 | 0xa54ff53a5f1d36f1, 40 | 0x510e527fade682d1, 41 | 0x9b05688c2b3e6c1f, 42 | 0x1f83d9abfb41bd6b, 43 | 0x5be0cd19137e2179, 44 | ]; 45 | 46 | const V_INDICES: [[u8; 4]; MIX_LENGTH] = [ 47 | [0, 4, 8, 12], 48 | [1, 5, 9, 13], 49 | [2, 6, 10, 14], 50 | [3, 7, 11, 15], 51 | [0, 5, 10, 15], 52 | [1, 6, 11, 12], 53 | [2, 7, 8, 13], 54 | [3, 4, 9, 14], 55 | ]; 56 | 57 | const V_LAST_WRITE_AGES: [[u8; 4]; MIX_LENGTH] = [ 58 | [4, 1, 2, 3], 59 | [4, 5, 2, 3], 60 | [4, 5, 6, 3], 61 | [4, 5, 6, 7], 62 | [4, 3, 2, 1], 63 | [4, 3, 2, 5], 64 | [4, 3, 6, 5], 65 | [4, 7, 6, 5], 66 | ]; 67 | 68 | const SIGMA_PERMUTATIONS: [[u8; MSG_ARRAY_SIZE]; NUM_MIX_ROUNDS] = [ 69 | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], 70 | [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3], 71 | [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4], 72 | [7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8], 73 | [9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13], 74 | [2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9], 75 | [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11], 76 | [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10], 77 | [6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5], 78 | [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0], 79 | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], 80 | [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3], 81 | ]; 82 | -------------------------------------------------------------------------------- /starkyx/src/machine/hash/blake/blake2b/register.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use crate::chip::register::array::{ArrayIterator, ArrayRegister}; 4 | use crate::chip::register::cell::CellType; 5 | use crate::chip::register::memory::MemorySlice; 6 | use crate::chip::register::{Register, RegisterSerializable, RegisterSized}; 7 | use crate::chip::uint::register::U64Register; 8 | 9 | #[derive(Debug, Clone, Copy, Serialize, Deserialize)] 10 | pub struct BLAKE2BDigestRegister(ArrayRegister); 11 | 12 | impl RegisterSerializable for BLAKE2BDigestRegister { 13 | const CELL: CellType = CellType::Element; 14 | fn register(&self) -> &MemorySlice { 15 | self.0.register() 16 | } 17 | 18 | fn from_register_unsafe(register: MemorySlice) -> Self { 19 | Self(ArrayRegister::from_register_unsafe(register)) 20 | } 21 | } 22 | 23 | impl RegisterSized for BLAKE2BDigestRegister { 24 | fn size_of() -> usize { 25 | U64Register::size_of() * 4 26 | } 27 | } 28 | 29 | impl Register for BLAKE2BDigestRegister { 30 | type Value = [T; 32]; 31 | 32 | fn align(value: &Self::Value) -> &[T] { 33 | value 34 | } 35 | 36 | fn value_from_slice(slice: &[T]) -> Self::Value { 37 | let elem_fn = |i| slice[i]; 38 | core::array::from_fn(elem_fn) 39 | } 40 | } 41 | 42 | impl BLAKE2BDigestRegister { 43 | pub fn as_array(&self) -> ArrayRegister { 44 | self.0 45 | } 46 | pub fn split(&self) -> [U64Register; 4] { 47 | core::array::from_fn(|i| U64Register::from_limbs(&self.0.get_subarray(2 * i..2 * i + 2))) 48 | } 49 | 50 | pub fn get(&self, index: usize) -> U64Register { 51 | self.0.get(index) 52 | } 53 | 54 | pub fn iter(&self) -> ArrayIterator { 55 | self.0.iter() 56 | } 57 | 58 | pub fn from_array(array: ArrayRegister) -> Self { 59 | assert_eq!(array.len(), 4); 60 | Self(array) 61 | } 62 | } 63 | 64 | impl From for ArrayRegister { 65 | fn from(value: BLAKE2BDigestRegister) -> Self { 66 | value.0 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /starkyx/src/machine/hash/blake/blake2b/utils.rs: -------------------------------------------------------------------------------- 1 | pub struct BLAKE2BUtil; 2 | 3 | impl BLAKE2BUtil { 4 | pub fn pad(msg: &[u8], max_chunk_size: u64) -> Vec { 5 | let mut msg_chunk_size = msg.len() as u64 / 128; 6 | 7 | if (msg.len() % 128 != 0) || msg.is_empty() { 8 | msg_chunk_size += 1; 9 | } 10 | 11 | assert!(msg_chunk_size <= max_chunk_size, "Message too big"); 12 | 13 | let padlen = max_chunk_size * 128 - msg.len() as u64; 14 | if padlen > 0 { 15 | let mut padded_msg = Vec::new(); 16 | padded_msg.extend_from_slice(msg); 17 | padded_msg.extend_from_slice(&vec![0u8; padlen as usize]); 18 | padded_msg 19 | } else { 20 | msg.to_vec() 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /starkyx/src/machine/hash/blake/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod blake2b; 2 | -------------------------------------------------------------------------------- /starkyx/src/machine/hash/mod.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::Debug; 2 | 3 | use num::Num; 4 | 5 | use super::builder::Builder; 6 | use crate::chip::memory::value::MemoryValue; 7 | use crate::chip::register::array::ArrayRegister; 8 | use crate::chip::register::Register; 9 | 10 | pub mod blake; 11 | pub mod sha; 12 | 13 | pub trait HashPureInteger { 14 | type Integer: Num + Copy + Debug; 15 | } 16 | 17 | pub trait HashInteger { 18 | type IntRegister: MemoryValue + Register = Self::Value>; 19 | type Value; 20 | } 21 | 22 | pub trait HashIntConversion: HashInteger + HashPureInteger { 23 | /// Convert an integer to the `Self::IntRegister` field value. 24 | fn int_to_field_value(int: Self::Integer) -> Self::Value; 25 | 26 | /// Convert a `Self::IntRegister` field value to an integer. 27 | fn field_value_to_int(value: &Self::Value) -> Self::Integer; 28 | } 29 | 30 | pub trait HashDigest: HashInteger { 31 | type DigestRegister: Register + Into>; 32 | } 33 | -------------------------------------------------------------------------------- /starkyx/src/machine/hash/sha/data.rs: -------------------------------------------------------------------------------- 1 | use crate::chip::memory::pointer::slice::Slice; 2 | use crate::chip::register::array::ArrayRegister; 3 | use crate::chip::register::bit::BitRegister; 4 | use crate::chip::register::element::ElementRegister; 5 | 6 | pub struct SHAData { 7 | pub public: SHAPublicData, 8 | pub trace: SHATraceData, 9 | pub memory: SHAMemory, 10 | pub degree: usize, 11 | } 12 | 13 | pub struct SHAPublicData { 14 | pub initial_hash: ArrayRegister, 15 | pub padded_chunks: Vec>, 16 | pub digest_indices: ArrayRegister, 17 | } 18 | 19 | pub struct SHATraceData { 20 | pub(crate) is_preprocessing: BitRegister, 21 | pub(crate) process_id: ElementRegister, 22 | pub(crate) cycle_end_bit: BitRegister, 23 | pub index: ElementRegister, 24 | pub is_dummy: BitRegister, 25 | } 26 | 27 | pub struct SHAMemory { 28 | pub(crate) round_constants: Slice, 29 | pub(crate) w: Slice, 30 | pub shift_read_mult: Slice, 31 | pub end_bit: Slice, 32 | pub digest_bit: Slice, 33 | pub dummy_index: ElementRegister, 34 | } 35 | -------------------------------------------------------------------------------- /starkyx/src/machine/hash/sha/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod algorithm; 2 | pub mod builder; 3 | pub mod data; 4 | pub mod sha256; 5 | pub mod sha512; 6 | -------------------------------------------------------------------------------- /starkyx/src/machine/hash/sha/sha256/mod.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | pub mod air; 4 | pub mod pure; 5 | pub mod register; 6 | 7 | #[derive(Debug, Clone, Copy, Serialize, Deserialize)] 8 | pub struct SHA256; 9 | 10 | pub(crate) const ROUND_CONSTANTS: [u32; 64] = [ 11 | 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 12 | 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 13 | 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 14 | 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 15 | 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 16 | 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 17 | 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 18 | 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, 19 | ]; 20 | 21 | pub(crate) const INITIAL_HASH: [u32; 8] = [ 22 | 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, 23 | ]; 24 | -------------------------------------------------------------------------------- /starkyx/src/machine/hash/sha/sha256/pure.rs: -------------------------------------------------------------------------------- 1 | use super::{INITIAL_HASH, ROUND_CONSTANTS, SHA256}; 2 | use crate::machine::hash::sha::algorithm::SHAPure; 3 | use crate::machine::hash::HashPureInteger; 4 | 5 | impl HashPureInteger for SHA256 { 6 | type Integer = u32; 7 | } 8 | 9 | impl SHAPure<64> for SHA256 { 10 | const INITIAL_HASH: [Self::Integer; 8] = INITIAL_HASH; 11 | const ROUND_CONSTANTS: [Self::Integer; 64] = ROUND_CONSTANTS; 12 | 13 | fn pad(msg: &[u8]) -> Vec { 14 | let mut padded_msg = Vec::new(); 15 | padded_msg.extend_from_slice(msg); 16 | padded_msg.push(1 << 7); 17 | 18 | // Find number of zeros 19 | let mdi = msg.len() % 64; 20 | assert!(mdi < 120); 21 | let padlen = if mdi < 56 { 55 - mdi } else { 119 - mdi }; 22 | // Pad with zeros 23 | padded_msg.extend_from_slice(&vec![0u8; padlen]); 24 | 25 | // add length as 64 bit number 26 | let len = ((msg.len() * 8) as u64).to_be_bytes(); 27 | padded_msg.extend_from_slice(&len); 28 | 29 | padded_msg 30 | .chunks_exact(4) 31 | .map(|slice| u32::from_be_bytes(slice.try_into().unwrap())) 32 | .collect::>() 33 | } 34 | 35 | fn pre_process(chunk: &[u32]) -> [Self::Integer; 64] { 36 | let mut w = [0u32; 64]; 37 | 38 | w[..16].copy_from_slice(&chunk[..16]); 39 | 40 | for i in 16..64 { 41 | let s0 = w[i - 15].rotate_right(7) ^ w[i - 15].rotate_right(18) ^ (w[i - 15] >> 3); 42 | let s1 = w[i - 2].rotate_right(17) ^ w[i - 2].rotate_right(19) ^ (w[i - 2] >> 10); 43 | w[i] = w[i - 16] 44 | .wrapping_add(s0) 45 | .wrapping_add(w[i - 7]) 46 | .wrapping_add(s1); 47 | } 48 | w 49 | } 50 | 51 | fn process(hash: [Self::Integer; 8], w: &[Self::Integer; 64]) -> [Self::Integer; 8] { 52 | let mut msg = hash; 53 | for (&w, &round_constant) in w.iter().zip(Self::ROUND_CONSTANTS.iter()) { 54 | msg = step(msg, w, round_constant); 55 | } 56 | 57 | [ 58 | hash[0].wrapping_add(msg[0]), 59 | hash[1].wrapping_add(msg[1]), 60 | hash[2].wrapping_add(msg[2]), 61 | hash[3].wrapping_add(msg[3]), 62 | hash[4].wrapping_add(msg[4]), 63 | hash[5].wrapping_add(msg[5]), 64 | hash[6].wrapping_add(msg[6]), 65 | hash[7].wrapping_add(msg[7]), 66 | ] 67 | } 68 | 69 | fn decode(digest: &str) -> [Self::Integer; 8] { 70 | hex::decode(digest) 71 | .unwrap() 72 | .chunks_exact(4) 73 | .map(|x| u32::from_be_bytes(x.try_into().unwrap())) 74 | .collect::>() 75 | .try_into() 76 | .unwrap() 77 | } 78 | } 79 | 80 | pub fn step(msg: [u32; 8], w_i: u32, round_constant: u32) -> [u32; 8] { 81 | let mut a = msg[0]; 82 | let mut b = msg[1]; 83 | let mut c = msg[2]; 84 | let mut d = msg[3]; 85 | let mut e = msg[4]; 86 | let mut f = msg[5]; 87 | let mut g = msg[6]; 88 | let mut h = msg[7]; 89 | 90 | let sum_1 = e.rotate_right(6) ^ e.rotate_right(11) ^ e.rotate_right(25); 91 | let ch = (e & f) ^ (!e & g); 92 | let temp_1 = h 93 | .wrapping_add(sum_1) 94 | .wrapping_add(ch) 95 | .wrapping_add(round_constant) 96 | .wrapping_add(w_i); 97 | let sum_0 = a.rotate_right(2) ^ a.rotate_right(13) ^ a.rotate_right(22); 98 | let maj = (a & b) ^ (a & c) ^ (b & c); 99 | let temp_2 = sum_0.wrapping_add(maj); 100 | 101 | h = g; 102 | g = f; 103 | f = e; 104 | e = d.wrapping_add(temp_1); 105 | d = c; 106 | c = b; 107 | b = a; 108 | a = temp_1.wrapping_add(temp_2); 109 | 110 | [a, b, c, d, e, f, g, h] 111 | } 112 | -------------------------------------------------------------------------------- /starkyx/src/machine/hash/sha/sha256/register.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use crate::chip::register::array::{ArrayIterator, ArrayRegister}; 4 | use crate::chip::register::cell::CellType; 5 | use crate::chip::register::memory::MemorySlice; 6 | use crate::chip::register::{Register, RegisterSerializable, RegisterSized}; 7 | use crate::chip::uint::register::{U32Register, U64Register}; 8 | 9 | #[derive(Debug, Clone, Copy, Serialize, Deserialize)] 10 | pub struct SHA256DigestRegister(ArrayRegister); 11 | 12 | impl RegisterSerializable for SHA256DigestRegister { 13 | const CELL: CellType = CellType::Element; 14 | fn register(&self) -> &MemorySlice { 15 | self.0.register() 16 | } 17 | 18 | fn from_register_unsafe(register: MemorySlice) -> Self { 19 | Self(ArrayRegister::from_register_unsafe(register)) 20 | } 21 | } 22 | 23 | impl RegisterSized for SHA256DigestRegister { 24 | fn size_of() -> usize { 25 | U32Register::size_of() * 8 26 | } 27 | } 28 | 29 | impl Register for SHA256DigestRegister { 30 | type Value = [T; 32]; 31 | 32 | fn align(value: &Self::Value) -> &[T] { 33 | value 34 | } 35 | 36 | fn value_from_slice(slice: &[T]) -> Self::Value { 37 | let elem_fn = |i| slice[i]; 38 | core::array::from_fn(elem_fn) 39 | } 40 | } 41 | 42 | impl SHA256DigestRegister { 43 | pub fn as_array(&self) -> ArrayRegister { 44 | self.0 45 | } 46 | pub fn split(&self) -> [U64Register; 4] { 47 | core::array::from_fn(|i| U64Register::from_limbs(&self.0.get_subarray(2 * i..2 * i + 2))) 48 | } 49 | 50 | pub fn get(&self, index: usize) -> U32Register { 51 | self.0.get(index) 52 | } 53 | 54 | pub fn iter(&self) -> ArrayIterator { 55 | self.0.iter() 56 | } 57 | 58 | pub fn from_array(array: ArrayRegister) -> Self { 59 | assert_eq!(array.len(), 8); 60 | Self(array) 61 | } 62 | } 63 | 64 | impl From for ArrayRegister { 65 | fn from(value: SHA256DigestRegister) -> Self { 66 | value.0 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /starkyx/src/machine/hash/sha/sha256/util.rs: -------------------------------------------------------------------------------- 1 | pub struct SHA256Util; 2 | 3 | impl SHA256Util { 4 | pub fn decode(digest: &str) -> [u32; 8] { 5 | hex::decode(digest) 6 | .unwrap() 7 | .chunks_exact(4) 8 | .map(|x| u32::from_be_bytes(x.try_into().unwrap())) 9 | .collect::>() 10 | .try_into() 11 | .unwrap() 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /starkyx/src/machine/hash/sha/sha512/data.rs: -------------------------------------------------------------------------------- 1 | use crate::chip::memory::pointer::slice::Slice; 2 | use crate::chip::register::array::ArrayRegister; 3 | use crate::chip::register::bit::BitRegister; 4 | use crate::chip::register::element::ElementRegister; 5 | use crate::chip::uint::register::U64Register; 6 | 7 | pub struct SHA512Data { 8 | pub public: SHA512PublicData, 9 | pub trace: SHA512TraceData, 10 | pub memory: SHA512Memory, 11 | pub num_chunks: usize, 12 | pub degree: usize, 13 | } 14 | 15 | pub struct SHA512PublicData { 16 | pub initial_hash: ArrayRegister, 17 | pub padded_chunks: Vec>, 18 | pub end_bits: ArrayRegister, 19 | } 20 | 21 | pub struct SHA512TraceData { 22 | pub(crate) is_preprocessing: BitRegister, 23 | pub(crate) process_id: ElementRegister, 24 | pub(crate) cycle_80_end_bit: BitRegister, 25 | pub index: ElementRegister, 26 | pub is_dummy: BitRegister, 27 | } 28 | 29 | pub struct SHA512Memory { 30 | pub(crate) round_constants: Slice, 31 | pub(crate) w: Slice, 32 | pub shift_read_mult: Slice, 33 | pub end_bit: Slice, 34 | pub dummy_index: ElementRegister, 35 | } 36 | -------------------------------------------------------------------------------- /starkyx/src/machine/hash/sha/sha512/mod.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | pub mod air; 4 | pub mod pure; 5 | pub mod register; 6 | 7 | #[derive(Clone, Copy, Debug, Serialize, Deserialize)] 8 | pub struct SHA512; 9 | 10 | pub(crate) const ROUND_CONSTANTS: [u64; 80] = [ 11 | 0x428a2f98d728ae22, 12 | 0x7137449123ef65cd, 13 | 0xb5c0fbcfec4d3b2f, 14 | 0xe9b5dba58189dbbc, 15 | 0x3956c25bf348b538, 16 | 0x59f111f1b605d019, 17 | 0x923f82a4af194f9b, 18 | 0xab1c5ed5da6d8118, 19 | 0xd807aa98a3030242, 20 | 0x12835b0145706fbe, 21 | 0x243185be4ee4b28c, 22 | 0x550c7dc3d5ffb4e2, 23 | 0x72be5d74f27b896f, 24 | 0x80deb1fe3b1696b1, 25 | 0x9bdc06a725c71235, 26 | 0xc19bf174cf692694, 27 | 0xe49b69c19ef14ad2, 28 | 0xefbe4786384f25e3, 29 | 0x0fc19dc68b8cd5b5, 30 | 0x240ca1cc77ac9c65, 31 | 0x2de92c6f592b0275, 32 | 0x4a7484aa6ea6e483, 33 | 0x5cb0a9dcbd41fbd4, 34 | 0x76f988da831153b5, 35 | 0x983e5152ee66dfab, 36 | 0xa831c66d2db43210, 37 | 0xb00327c898fb213f, 38 | 0xbf597fc7beef0ee4, 39 | 0xc6e00bf33da88fc2, 40 | 0xd5a79147930aa725, 41 | 0x06ca6351e003826f, 42 | 0x142929670a0e6e70, 43 | 0x27b70a8546d22ffc, 44 | 0x2e1b21385c26c926, 45 | 0x4d2c6dfc5ac42aed, 46 | 0x53380d139d95b3df, 47 | 0x650a73548baf63de, 48 | 0x766a0abb3c77b2a8, 49 | 0x81c2c92e47edaee6, 50 | 0x92722c851482353b, 51 | 0xa2bfe8a14cf10364, 52 | 0xa81a664bbc423001, 53 | 0xc24b8b70d0f89791, 54 | 0xc76c51a30654be30, 55 | 0xd192e819d6ef5218, 56 | 0xd69906245565a910, 57 | 0xf40e35855771202a, 58 | 0x106aa07032bbd1b8, 59 | 0x19a4c116b8d2d0c8, 60 | 0x1e376c085141ab53, 61 | 0x2748774cdf8eeb99, 62 | 0x34b0bcb5e19b48a8, 63 | 0x391c0cb3c5c95a63, 64 | 0x4ed8aa4ae3418acb, 65 | 0x5b9cca4f7763e373, 66 | 0x682e6ff3d6b2b8a3, 67 | 0x748f82ee5defb2fc, 68 | 0x78a5636f43172f60, 69 | 0x84c87814a1f0ab72, 70 | 0x8cc702081a6439ec, 71 | 0x90befffa23631e28, 72 | 0xa4506cebde82bde9, 73 | 0xbef9a3f7b2c67915, 74 | 0xc67178f2e372532b, 75 | 0xca273eceea26619c, 76 | 0xd186b8c721c0c207, 77 | 0xeada7dd6cde0eb1e, 78 | 0xf57d4f7fee6ed178, 79 | 0x06f067aa72176fba, 80 | 0x0a637dc5a2c898a6, 81 | 0x113f9804bef90dae, 82 | 0x1b710b35131c471b, 83 | 0x28db77f523047d84, 84 | 0x32caab7b40c72493, 85 | 0x3c9ebe0a15c9bebc, 86 | 0x431d67c49c100d4c, 87 | 0x4cc5d4becb3e42b6, 88 | 0x597f299cfc657e2a, 89 | 0x5fcb6fab3ad6faec, 90 | 0x6c44198c4a475817, 91 | ]; 92 | 93 | pub(crate) const INITIAL_HASH: [u64; 8] = [ 94 | 0x6a09e667f3bcc908, 95 | 0xbb67ae8584caa73b, 96 | 0x3c6ef372fe94f82b, 97 | 0xa54ff53a5f1d36f1, 98 | 0x510e527fade682d1, 99 | 0x9b05688c2b3e6c1f, 100 | 0x1f83d9abfb41bd6b, 101 | 0x5be0cd19137e2179, 102 | ]; 103 | -------------------------------------------------------------------------------- /starkyx/src/machine/hash/sha/sha512/register.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use crate::chip::register::array::{ArrayIterator, ArrayRegister}; 4 | use crate::chip::register::cell::CellType; 5 | use crate::chip::register::memory::MemorySlice; 6 | use crate::chip::register::{Register, RegisterSerializable, RegisterSized}; 7 | use crate::chip::uint::register::U64Register; 8 | 9 | #[derive(Debug, Clone, Copy, Serialize, Deserialize)] 10 | pub struct SHA512DigestRegister(ArrayRegister); 11 | 12 | impl RegisterSerializable for SHA512DigestRegister { 13 | const CELL: CellType = CellType::Element; 14 | fn register(&self) -> &MemorySlice { 15 | self.0.register() 16 | } 17 | 18 | fn from_register_unsafe(register: MemorySlice) -> Self { 19 | Self(ArrayRegister::from_register_unsafe(register)) 20 | } 21 | } 22 | 23 | impl RegisterSized for SHA512DigestRegister { 24 | fn size_of() -> usize { 25 | U64Register::size_of() * 8 26 | } 27 | } 28 | 29 | impl Register for SHA512DigestRegister { 30 | type Value = [T; 64]; 31 | 32 | fn align(value: &Self::Value) -> &[T] { 33 | value 34 | } 35 | 36 | fn value_from_slice(slice: &[T]) -> Self::Value { 37 | let elem_fn = |i| slice[i]; 38 | core::array::from_fn(elem_fn) 39 | } 40 | } 41 | 42 | impl SHA512DigestRegister { 43 | pub fn as_array(&self) -> ArrayRegister { 44 | self.0 45 | } 46 | 47 | pub fn get(&self, index: usize) -> U64Register { 48 | self.0.get(index) 49 | } 50 | 51 | pub fn iter(&self) -> ArrayIterator { 52 | self.0.iter() 53 | } 54 | 55 | pub fn from_array(array: ArrayRegister) -> Self { 56 | assert_eq!(array.len(), 8); 57 | Self(array) 58 | } 59 | } 60 | 61 | impl From for ArrayRegister { 62 | fn from(register: SHA512DigestRegister) -> Self { 63 | register.0 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /starkyx/src/machine/hash/sha/sha512/util.rs: -------------------------------------------------------------------------------- 1 | pub struct SHA512Util; 2 | 3 | impl SHA512Util { 4 | pub fn pad(msg: &[u8]) -> Vec { 5 | let mut padded_msg = Vec::new(); 6 | padded_msg.extend_from_slice(msg); 7 | padded_msg.push(1 << 7); 8 | 9 | // Find number of zeros 10 | let mdi = msg.len() % 128; 11 | assert!(mdi < 240); 12 | let padlen = if mdi < 112 { 112 - mdi } else { 240 - mdi }; 13 | // Pad with zeros 14 | padded_msg.extend_from_slice(&vec![0u8; padlen]); 15 | 16 | // add length as 64 bit number 17 | let len = ((msg.len() * 8) as u128).to_be_bytes(); 18 | padded_msg.extend_from_slice(&len); 19 | 20 | padded_msg 21 | } 22 | 23 | pub fn pre_process(chunk: &[u8]) -> [u64; 80] { 24 | let chunk_u64 = chunk 25 | .chunks_exact(8) 26 | .map(|x| u64::from_be_bytes(x.try_into().unwrap())) 27 | .collect::>(); 28 | let mut w = [0u64; 80]; 29 | 30 | w[..16].copy_from_slice(&chunk_u64[..16]); 31 | 32 | for i in 16..80 { 33 | let s0 = w[i - 15].rotate_right(1) ^ w[i - 15].rotate_right(8) ^ (w[i - 15] >> 7); 34 | let s1 = w[i - 2].rotate_right(19) ^ w[i - 2].rotate_right(61) ^ (w[i - 2] >> 6); 35 | w[i] = w[i - 16] 36 | .wrapping_add(s0) 37 | .wrapping_add(w[i - 7]) 38 | .wrapping_add(s1); 39 | } 40 | w 41 | } 42 | 43 | pub fn decode(digest: &str) -> [u64; 8] { 44 | hex::decode(digest) 45 | .unwrap() 46 | .chunks_exact(8) 47 | .map(|x| u64::from_be_bytes(x.try_into().unwrap())) 48 | .collect::>() 49 | .try_into() 50 | .unwrap() 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /starkyx/src/machine/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod builder; 2 | pub mod bytes; 3 | pub mod ec; 4 | pub mod emulated; 5 | pub mod hash; 6 | pub mod stark; 7 | -------------------------------------------------------------------------------- /starkyx/src/machine/stark/builder.rs: -------------------------------------------------------------------------------- 1 | use super::Stark; 2 | use crate::chip::builder::AirBuilder; 3 | use crate::chip::register::element::ElementRegister; 4 | use crate::chip::AirParameters; 5 | use crate::machine::builder::Builder; 6 | use crate::plonky2::stark::config::{CurtaConfig, StarkyConfig}; 7 | use crate::plonky2::stark::Starky; 8 | 9 | pub struct StarkBuilder { 10 | pub api: AirBuilder, 11 | pub clk: ElementRegister, 12 | } 13 | 14 | impl Builder for StarkBuilder { 15 | type Field = L::Field; 16 | type CubicParams = L::CubicParams; 17 | type Parameters = L; 18 | type Instruction = L::Instruction; 19 | 20 | fn api(&mut self) -> &mut AirBuilder { 21 | &mut self.api 22 | } 23 | 24 | fn clk(&mut self) -> ElementRegister { 25 | self.clk 26 | } 27 | } 28 | 29 | impl StarkBuilder { 30 | pub fn new() -> Self { 31 | let mut api = AirBuilder::::new(); 32 | let clk = api.clock(); 33 | api.init_local_memory(); 34 | StarkBuilder { api, clk } 35 | } 36 | 37 | pub fn build, const D: usize>( 38 | self, 39 | num_rows: usize, 40 | ) -> Stark { 41 | let api = self.api; 42 | 43 | let config = StarkyConfig::::standard_fast_config(num_rows); 44 | let (air, air_data) = api.build(); 45 | let stark = Starky::new(air); 46 | 47 | Stark { 48 | config, 49 | stark, 50 | air_data, 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /starkyx/src/math/algebra.rs: -------------------------------------------------------------------------------- 1 | use core::ops::{Add, Mul, Sub}; 2 | 3 | use super::field::{Field, Ring}; 4 | 5 | pub trait Algebra: 6 | Ring + From + Add + Sub + Mul 7 | { 8 | } 9 | 10 | impl Algebra for F {} 11 | -------------------------------------------------------------------------------- /starkyx/src/math/extension/cubic/mod.rs: -------------------------------------------------------------------------------- 1 | //! The cubic extension field F[X]/(X^3 - X - 1). 2 | 3 | pub mod element; 4 | pub mod extension; 5 | pub mod parameters; 6 | -------------------------------------------------------------------------------- /starkyx/src/math/extension/cubic/parameters.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::Debug; 2 | 3 | use serde::de::DeserializeOwned; 4 | use serde::Serialize; 5 | 6 | use super::element::CubicElement; 7 | 8 | /// Parameters for the cubic extension F[X]/(X^3 - X - 1) 9 | pub trait CubicParameters: 10 | 'static + Sized + Copy + Clone + Send + Sync + PartialEq + Eq + Debug + Serialize + DeserializeOwned 11 | { 12 | /// The Galois orbit of the generator. 13 | /// 14 | /// These are the roots of X^3 - X - 1 in the extension field not equal to X. 15 | const GALOIS_ORBIT: [CubicElement; 2]; 16 | } 17 | -------------------------------------------------------------------------------- /starkyx/src/math/extension/mod.rs: -------------------------------------------------------------------------------- 1 | use core::slice; 2 | 3 | use super::algebra::Algebra; 4 | use super::field::Field; 5 | 6 | pub mod cubic; 7 | 8 | pub use cubic::parameters::CubicParameters; 9 | /// A ring extension of a field with a fixed basis 10 | pub trait Extension: Algebra { 11 | /// The dimension (i.e. degree) of the extension 12 | const D: usize; 13 | 14 | /// An element from a collection of coefficients in the field 15 | fn from_base_slice(elements: &[F]) -> Self; 16 | 17 | /// The coefficients of the element with respect to the fixed basis 18 | fn as_base_slice(&self) -> &[F]; 19 | } 20 | 21 | /// A ring extension of a field with a fixed basis 22 | pub trait ExtensionField: Extension + Field {} 23 | 24 | impl Extension for F { 25 | const D: usize = 1; 26 | 27 | fn from_base_slice(elements: &[F]) -> Self { 28 | elements[0] 29 | } 30 | 31 | fn as_base_slice(&self) -> &[F] { 32 | slice::from_ref(self) 33 | } 34 | } 35 | 36 | impl ExtensionField for F {} 37 | -------------------------------------------------------------------------------- /starkyx/src/math/goldilocks/cubic.rs: -------------------------------------------------------------------------------- 1 | use plonky2::field::goldilocks_field::GoldilocksField; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | use crate::math::extension::cubic::element::CubicElement; 5 | use crate::math::extension::cubic::extension::CubicExtension; 6 | use crate::math::extension::cubic::parameters::CubicParameters; 7 | 8 | pub type GF3 = CubicExtension; 9 | 10 | /// Galois parameters for the cubic Goldilocks extension field. 11 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] 12 | pub struct GoldilocksCubicParameters; 13 | 14 | impl CubicParameters for GoldilocksCubicParameters { 15 | const GALOIS_ORBIT: [CubicElement; 2] = [ 16 | CubicElement([ 17 | GoldilocksField(7831040667286096068), 18 | GoldilocksField(10050274602728160328), 19 | GoldilocksField(6700183068485440219), 20 | ]), 21 | CubicElement([ 22 | GoldilocksField(10615703402128488253), 23 | GoldilocksField(8396469466686423992), 24 | GoldilocksField(11746561000929144102), 25 | ]), 26 | ]; 27 | } 28 | 29 | #[cfg(test)] 30 | mod tests { 31 | use super::*; 32 | use crate::math::prelude::*; 33 | 34 | #[test] 35 | fn test_gf3_add() { 36 | let num_tests = 100; 37 | 38 | for _ in 0..num_tests { 39 | let a = GF3::rand(); 40 | let b = GF3::rand(); 41 | 42 | let a_rr = a.0.as_array(); 43 | let b_rr = b.0.as_array(); 44 | 45 | assert_eq!(a + b, b + a); 46 | assert_eq!(a, a + GF3::ZERO); 47 | assert_eq!( 48 | (a + b).0.as_array(), 49 | [a_rr[0] + b_rr[0], a_rr[1] + b_rr[1], a_rr[2] + b_rr[2]] 50 | ); 51 | } 52 | } 53 | 54 | #[test] 55 | fn test_gf3_mul() { 56 | let num_tests = 100; 57 | 58 | for _ in 0..num_tests { 59 | let a = GF3::rand(); 60 | let b = GF3::rand(); 61 | let c = GF3::rand(); 62 | 63 | assert_eq!(a * b, b * a); 64 | assert_eq!(a * (b * c), (a * b) * c); 65 | assert_eq!(a * (b + c), a * b + a * c); 66 | assert_eq!(a * GF3::ONE, a); 67 | assert_eq!(a * GF3::ZERO, GF3::ZERO); 68 | } 69 | } 70 | 71 | #[test] 72 | fn test_orbit() { 73 | for &g in GF3::ORBIT.iter() { 74 | assert_eq!(g * g * g, g - GF3::ONE); 75 | } 76 | } 77 | 78 | #[test] 79 | fn test_gf3_inverse() { 80 | let num_tests = 100; 81 | 82 | for _ in 0..num_tests { 83 | let a = GF3::rand(); 84 | 85 | let a_inv = a.inverse(); 86 | 87 | assert_eq!(a * a_inv, GF3::ONE); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /starkyx/src/math/goldilocks/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod cubic; 2 | 3 | // use plonky2::field::goldilocks_field::GoldilocksField; 4 | // use plonky2::field::types::PrimeField64 as PlonkyPrimeField64; 5 | 6 | // use crate::math::prelude::*; 7 | 8 | // impl PrimeField64 for GoldilocksField { 9 | // // const ORDER_U64: u64 = 18446744069414584321; 10 | 11 | // fn as_canonical_u64(&self) -> u64 { 12 | // self.to_canonical_u64() 13 | // } 14 | // } 15 | -------------------------------------------------------------------------------- /starkyx/src/math/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod algebra; 2 | pub mod extension; 3 | pub mod field; 4 | pub mod goldilocks; 5 | 6 | pub mod prelude { 7 | pub use super::algebra::*; 8 | pub use super::extension::*; 9 | pub use super::field::*; 10 | } 11 | -------------------------------------------------------------------------------- /starkyx/src/maybe_rayon.rs: -------------------------------------------------------------------------------- 1 | //! Re-export the `plonky2_maybe_rayon` crate for convenient handeling of parallel iterators via 2 | //! the `parallel` feature. 3 | 4 | pub use plonky2_maybe_rayon::*; 5 | -------------------------------------------------------------------------------- /starkyx/src/plonky2/cubic/builder.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use plonky2::field::extension::Extendable; 4 | use plonky2::hash::hash_types::RichField; 5 | use plonky2::iop::target::Target; 6 | use plonky2::plonk::circuit_builder::CircuitBuilder; 7 | 8 | use super::operations::{CubicBuilderOperations, CubicOperation}; 9 | use crate::math::prelude::cubic::element::CubicElement; 10 | 11 | pub trait CubicCircuitBuilder, const D: usize> { 12 | fn zero_cubic(&mut self) -> CubicElement; 13 | 14 | fn one_cubic(&mut self) -> CubicElement; 15 | 16 | fn cubic_from_base(&mut self, value: Target) -> CubicElement; 17 | 18 | fn arithmetic_cubic( 19 | &mut self, 20 | const_0: F, 21 | const_1: F, 22 | multiplicand_0: CubicElement, 23 | multiplicand_1: CubicElement, 24 | addend: CubicElement, 25 | cubic_results: &mut HashMap, CubicElement>, 26 | ) -> CubicElement; 27 | 28 | fn add_cubic( 29 | &mut self, 30 | a: CubicElement, 31 | b: CubicElement, 32 | cubic_results: &mut HashMap, CubicElement>, 33 | ) -> CubicElement { 34 | let one = self.one_cubic(); 35 | self.arithmetic_cubic(F::ONE, F::ONE, one, a, b, cubic_results) 36 | } 37 | 38 | fn sub_cubic( 39 | &mut self, 40 | a: CubicElement, 41 | b: CubicElement, 42 | cubic_results: &mut HashMap, CubicElement>, 43 | ) -> CubicElement { 44 | let one = self.one_cubic(); 45 | self.arithmetic_cubic(F::ONE, -F::ONE, one, a, b, cubic_results) 46 | } 47 | 48 | fn mul_cubic( 49 | &mut self, 50 | a: CubicElement, 51 | b: CubicElement, 52 | cubic_results: &mut HashMap, CubicElement>, 53 | ) -> CubicElement { 54 | let zero = self.zero_cubic(); 55 | self.arithmetic_cubic(F::ONE, F::ZERO, a, b, zero, cubic_results) 56 | } 57 | 58 | fn scalar_mul_cubic( 59 | &mut self, 60 | a: CubicElement, 61 | b: Target, 62 | cubic_results: &mut HashMap, CubicElement>, 63 | ) -> CubicElement { 64 | let b_ext = self.cubic_from_base(b); 65 | self.mul_cubic(a, b_ext, cubic_results) 66 | } 67 | } 68 | 69 | impl, const D: usize> CubicCircuitBuilder 70 | for CircuitBuilder 71 | { 72 | fn zero_cubic(&mut self) -> CubicElement { 73 | CubicBuilderOperations::zero(self) 74 | } 75 | 76 | fn one_cubic(&mut self) -> CubicElement { 77 | CubicElement([self.one(), self.zero(), self.zero()]) 78 | } 79 | 80 | fn cubic_from_base(&mut self, value: Target) -> CubicElement { 81 | CubicElement([value, self.zero(), self.zero()]) 82 | } 83 | 84 | fn arithmetic_cubic( 85 | &mut self, 86 | const_0: F, 87 | const_1: F, 88 | multiplicand_0: CubicElement, 89 | multiplicand_1: CubicElement, 90 | addend: CubicElement, 91 | cubic_results: &mut HashMap, CubicElement>, 92 | ) -> CubicElement { 93 | CubicBuilderOperations::arithmetic_cubic( 94 | self, 95 | const_0, 96 | const_1, 97 | multiplicand_0, 98 | multiplicand_1, 99 | addend, 100 | cubic_results, 101 | ) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /starkyx/src/plonky2/cubic/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod arithmetic_gate; 2 | pub mod builder; 3 | pub mod mul_gate; 4 | pub mod operations; 5 | pub mod target; 6 | -------------------------------------------------------------------------------- /starkyx/src/plonky2/cubic/target.rs: -------------------------------------------------------------------------------- 1 | use core::ops::Range; 2 | 3 | use plonky2::hash::hash_types::RichField; 4 | use plonky2::iop::generator::GeneratedValues; 5 | use plonky2::iop::target::Target; 6 | use plonky2::iop::witness::{PartitionWitness, Witness, WitnessWrite}; 7 | 8 | use crate::math::prelude::cubic::element::CubicElement; 9 | 10 | impl CubicElement { 11 | pub fn from_range(row: usize, range: Range) -> Self { 12 | debug_assert_eq!(range.end - range.start, 3); 13 | CubicElement([ 14 | Target::wire(row, range.start), 15 | Target::wire(row, range.start + 1), 16 | Target::wire(row, range.start + 2), 17 | ]) 18 | } 19 | 20 | pub fn get(&self, witness: &PartitionWitness) -> CubicElement { 21 | CubicElement(witness.get_targets(&self.0).try_into().unwrap()) 22 | } 23 | 24 | pub fn set(&self, value: &CubicElement, out_buffer: &mut GeneratedValues) { 25 | out_buffer.set_target_arr(&self.0, &value.0); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /starkyx/src/plonky2/field.rs: -------------------------------------------------------------------------------- 1 | use plonky2::field::types::{ 2 | Field as Plonky2Field, PrimeField as Plonky2PrimeField, PrimeField64 as Plonky2PrimeField64, 3 | Sample as Plonky2Sample, 4 | }; 5 | 6 | use crate::math::prelude::*; 7 | 8 | impl Ring for F { 9 | const ONE: Self = F::ONE; 10 | const ZERO: Self = F::ZERO; 11 | } 12 | 13 | impl Field for F { 14 | fn try_inverse(&self) -> Option { 15 | Some(self.inverse()) 16 | } 17 | fn from_canonical_u8(n: u8) -> Self { 18 | F::from_canonical_u8(n) 19 | } 20 | fn from_canonical_u16(n: u16) -> Self { 21 | F::from_canonical_u16(n) 22 | } 23 | fn from_canonical_u32(n: u32) -> Self { 24 | F::from_canonical_u32(n) 25 | } 26 | fn from_canonical_u64(n: u64) -> Self { 27 | F::from_canonical_u64(n) 28 | } 29 | fn from_canonical_usize(n: usize) -> Self { 30 | F::from_canonical_usize(n) 31 | } 32 | 33 | fn from_noncanonical_biguint(n: num::BigUint) -> Self { 34 | F::from_noncanonical_biguint(n) 35 | } 36 | 37 | fn primitive_root_of_unity(n_log: usize) -> Self { 38 | F::primitive_root_of_unity(n_log) 39 | } 40 | 41 | fn two_adic_subgroup(n_log: usize) -> Vec { 42 | F::two_adic_subgroup(n_log) 43 | } 44 | } 45 | 46 | impl Sample for F { 47 | fn sample(rng: &mut R) -> Self 48 | where 49 | R: rand::RngCore + ?Sized, 50 | { 51 | F::sample(rng) 52 | } 53 | } 54 | 55 | impl PrimeField for F {} 56 | 57 | impl PrimeField64 for F { 58 | fn as_canonical_u64(&self) -> u64 { 59 | self.to_canonical_u64() 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /starkyx/src/plonky2/mod.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::Debug; 2 | 3 | use plonky2::field::extension::Extendable; 4 | use plonky2::field::packable::Packable; 5 | use plonky2::hash::hash_types::RichField; 6 | use serde::de::DeserializeOwned; 7 | use serde::Serialize; 8 | 9 | use self::parser::global::{GlobalRecursiveStarkParser, GlobalStarkParser}; 10 | use self::parser::{RecursiveStarkParser, StarkParser}; 11 | use crate::air::RAir; 12 | 13 | pub mod cubic; 14 | pub mod field; 15 | pub mod parser; 16 | pub mod stark; 17 | pub mod trace; 18 | 19 | /// an air that can generate constraints for the Starky proving system. 20 | pub trait StarkyAir, const D: usize>: 21 | for<'a> RAir::Packing, D, 1>> 22 | + for<'a> RAir> 23 | + for<'a> RAir> 24 | + 'static 25 | + Debug 26 | + Send 27 | + Sync 28 | + Serialize 29 | + DeserializeOwned 30 | { 31 | } 32 | 33 | /// an air that can be verified recursively inside a Plonky2 circuit. 34 | pub trait Plonky2Air, const D: usize>: 35 | StarkyAir 36 | + for<'a> RAir> 37 | + for<'a> RAir> 38 | { 39 | } 40 | 41 | impl, const D: usize, T> StarkyAir for T where 42 | T: for<'a> RAir::Packing, D, 1>> 43 | + for<'a> RAir> 44 | + for<'a> RAir> 45 | + 'static 46 | + Debug 47 | + Send 48 | + Sync 49 | + Serialize 50 | + DeserializeOwned 51 | { 52 | } 53 | 54 | impl, const D: usize, T> Plonky2Air for T where 55 | T: StarkyAir 56 | + for<'a> RAir> 57 | + for<'a> RAir> 58 | { 59 | } 60 | -------------------------------------------------------------------------------- /starkyx/src/plonky2/stark/gadget.rs: -------------------------------------------------------------------------------- 1 | use plonky2::field::extension::Extendable; 2 | use plonky2::hash::hash_types::RichField; 3 | use plonky2::iop::target::Target; 4 | use plonky2::plonk::circuit_builder::CircuitBuilder; 5 | 6 | use super::config::{CurtaConfig, StarkyConfig}; 7 | use super::proof::StarkProofTarget; 8 | use super::verifier::{add_virtual_stark_proof, StarkyVerifier}; 9 | use super::Starky; 10 | use crate::plonky2::Plonky2Air; 11 | 12 | pub trait StarkGadget< 13 | F: RichField + Extendable, 14 | C: CurtaConfig, 15 | const D: usize, 16 | > 17 | { 18 | fn add_virtual_stark_proof( 19 | &mut self, 20 | stark: &Starky, 21 | config: &StarkyConfig, 22 | ) -> StarkProofTarget 23 | where 24 | A: Plonky2Air; 25 | 26 | fn verify_stark_proof( 27 | &mut self, 28 | config: &StarkyConfig, 29 | stark: &Starky, 30 | proof: &StarkProofTarget, 31 | public_inputs: &[Target], 32 | ) where 33 | A: Plonky2Air; 34 | } 35 | 36 | impl, C: CurtaConfig, const D: usize> 37 | StarkGadget for CircuitBuilder 38 | { 39 | fn add_virtual_stark_proof( 40 | &mut self, 41 | stark: &Starky, 42 | config: &StarkyConfig, 43 | ) -> StarkProofTarget 44 | where 45 | A: Plonky2Air, 46 | { 47 | add_virtual_stark_proof(self, stark, config) 48 | } 49 | 50 | fn verify_stark_proof( 51 | &mut self, 52 | config: &StarkyConfig, 53 | stark: &Starky, 54 | proof: &StarkProofTarget, 55 | public_inputs: &[Target], 56 | ) where 57 | A: Plonky2Air, 58 | { 59 | StarkyVerifier::verify_circuit(self, config, stark, proof, public_inputs) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /starkyx/src/plonky2/stark/generator/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod simple; 2 | -------------------------------------------------------------------------------- /starkyx/src/plonky2/stark/generator/simple.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::Debug; 2 | 3 | use plonky2::field::extension::Extendable; 4 | use plonky2::hash::hash_types::RichField; 5 | use plonky2::iop::generator::{GeneratedValues, SimpleGenerator}; 6 | use plonky2::iop::target::Target; 7 | use plonky2::iop::witness::{PartitionWitness, Witness}; 8 | use plonky2::plonk::circuit_data::CommonCircuitData; 9 | use plonky2::util::serialization::{Buffer, IoResult}; 10 | use serde::{Deserialize, Serialize}; 11 | 12 | use super::super::config::StarkyConfig; 13 | use super::super::proof::StarkProofTarget; 14 | use super::super::prover::StarkyProver; 15 | use super::super::verifier::set_stark_proof_target; 16 | use crate::chip::trace::generator::ArithmeticGenerator; 17 | use crate::chip::{AirParameters, Chip}; 18 | use crate::plonky2::stark::config::CurtaConfig; 19 | use crate::plonky2::stark::Starky; 20 | use crate::plonky2::Plonky2Air; 21 | use crate::utils::serde::{BufferRead, BufferWrite}; 22 | 23 | #[derive(Debug, Clone, Serialize, Deserialize)] 24 | #[serde(bound = "")] 25 | pub struct SimpleStarkWitnessGenerator { 26 | pub config: StarkyConfig, 27 | pub stark: Starky>, 28 | pub proof_target: StarkProofTarget, 29 | pub public_input_targets: Vec, 30 | pub trace_generator: ArithmeticGenerator, 31 | } 32 | 33 | impl SimpleStarkWitnessGenerator { 34 | pub fn new( 35 | config: StarkyConfig, 36 | stark: Starky>, 37 | proof_target: StarkProofTarget, 38 | public_input_targets: Vec, 39 | trace_generator: ArithmeticGenerator, 40 | ) -> Self { 41 | Self { 42 | config, 43 | stark, 44 | proof_target, 45 | public_input_targets, 46 | trace_generator, 47 | } 48 | } 49 | 50 | pub fn id() -> String { 51 | format!( 52 | "SimpleStarkWitnessGenerator, air parameters: {}, D = {}", 53 | L::id(), 54 | D 55 | ) 56 | .to_string() 57 | } 58 | } 59 | 60 | impl SimpleGenerator 61 | for SimpleStarkWitnessGenerator 62 | where 63 | L::Field: RichField + Extendable, 64 | Chip: Plonky2Air, 65 | C: CurtaConfig, 66 | { 67 | fn id(&self) -> String { 68 | Self::id() 69 | } 70 | 71 | fn dependencies(&self) -> Vec { 72 | self.public_input_targets.clone() 73 | } 74 | 75 | fn run_once( 76 | &self, 77 | witness: &PartitionWitness, 78 | out_buffer: &mut GeneratedValues, 79 | ) { 80 | let public_inputs = witness.get_targets(&self.public_input_targets); 81 | 82 | let proof = StarkyProver::::prove( 83 | &self.config, 84 | &self.stark, 85 | &self.trace_generator, 86 | &public_inputs, 87 | ) 88 | .unwrap(); 89 | 90 | set_stark_proof_target(out_buffer, &self.proof_target, &proof); 91 | 92 | self.trace_generator.reset(); 93 | } 94 | 95 | fn serialize( 96 | &self, 97 | dst: &mut Vec, 98 | _common_data: &CommonCircuitData, 99 | ) -> IoResult<()> { 100 | let data = bincode::serialize(&self).unwrap(); 101 | dst.write_bytes(&data) 102 | } 103 | 104 | fn deserialize( 105 | src: &mut Buffer, 106 | _common_data: &CommonCircuitData, 107 | ) -> IoResult 108 | where 109 | Self: Sized, 110 | { 111 | let bytes = src.read_bytes()?; 112 | let data = bincode::deserialize(&bytes).unwrap(); 113 | Ok(data) 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /starkyx/src/plonky2/trace.rs: -------------------------------------------------------------------------------- 1 | // use plonky2::hash::hash_types::RichField; 2 | 3 | // use crate::trace::AirTrace; 4 | 5 | // use super::stark::config::StarkyConfig; 6 | 7 | // impl AirTrace { 8 | // fn commit(&self, config: &StarkyConfig) { 9 | // let trace_cols = self.as_columns() 10 | // .into_par_iter() 11 | // .map(PolynomialValues::from) 12 | // .collect::>(); 13 | // } 14 | // } 15 | -------------------------------------------------------------------------------- /starkyx/src/trace/generator.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::Debug; 2 | 3 | use anyhow::{anyhow, Error}; 4 | use serde::de::DeserializeOwned; 5 | use serde::{Deserialize, Serialize}; 6 | 7 | use super::AirTrace; 8 | use crate::math::prelude::*; 9 | 10 | pub trait TraceGenerator: 11 | 'static + Debug + Send + Sync + Serialize + DeserializeOwned 12 | { 13 | type Error; 14 | fn generate_round( 15 | &self, 16 | air: &A, 17 | round: usize, 18 | challenges: &[F], 19 | global_values: &mut [F], 20 | public_inputs: &[F], 21 | ) -> Result, Self::Error>; 22 | } 23 | 24 | #[derive(Debug, Clone, Serialize, Deserialize)] 25 | #[serde(bound = "")] 26 | pub struct ConstantGenerator { 27 | trace: AirTrace, 28 | } 29 | 30 | impl ConstantGenerator { 31 | pub fn new(trace: AirTrace) -> Self { 32 | Self { trace } 33 | } 34 | } 35 | 36 | impl TraceGenerator for ConstantGenerator { 37 | type Error = Error; 38 | 39 | fn generate_round( 40 | &self, 41 | _air: &A, 42 | round: usize, 43 | _challenges: &[F], 44 | _global_values: &mut [F], 45 | _public_inputs: &[F], 46 | ) -> Result, Self::Error> { 47 | match round { 48 | 0 => Ok(self.trace.clone()), 49 | _ => Err(anyhow!("Constant generator has only one round")), 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /starkyx/src/trace/window.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Clone)] 2 | pub struct TraceWindow<'a, T> { 3 | pub local_slice: &'a [T], 4 | pub next_slice: &'a [T], 5 | pub row: usize, 6 | pub is_first_row: bool, 7 | pub is_last_row: bool, 8 | } 9 | 10 | impl<'a, T> TraceWindow<'a, T> { 11 | pub const fn empty() -> Self { 12 | Self { 13 | local_slice: &[], 14 | next_slice: &[], 15 | row: 0, 16 | is_first_row: false, 17 | is_last_row: false, 18 | } 19 | } 20 | } 21 | 22 | #[derive(Debug)] 23 | pub struct TraceWindowMut<'a, T> { 24 | pub local_slice: &'a mut [T], 25 | pub next_slice: &'a mut [T], 26 | pub row: usize, 27 | pub is_first_row: bool, 28 | pub is_last_row: bool, 29 | } 30 | 31 | impl<'a, T> TraceWindowMut<'a, T> { 32 | pub fn empty() -> Self { 33 | Self { 34 | local_slice: &mut [], 35 | next_slice: &mut [], 36 | row: 0, 37 | is_first_row: false, 38 | is_last_row: false, 39 | } 40 | } 41 | 42 | pub fn as_window(&self) -> TraceWindow<'_, T> { 43 | TraceWindow { 44 | local_slice: self.local_slice, 45 | next_slice: self.next_slice, 46 | row: self.row, 47 | is_first_row: self.is_first_row, 48 | is_last_row: self.is_last_row, 49 | } 50 | } 51 | } 52 | 53 | #[derive(Debug)] 54 | pub struct TraceWindowsMutIter<'a, T> { 55 | values: &'a mut [T], 56 | width: usize, 57 | height: usize, 58 | current_row: usize, 59 | } 60 | 61 | impl<'a, T> TraceWindowsMutIter<'a, T> { 62 | pub(crate) fn new(values: &'a mut [T], width: usize, height: usize) -> Self { 63 | Self { 64 | values, 65 | width, 66 | height, 67 | current_row: 0, 68 | } 69 | } 70 | } 71 | 72 | impl<'a, T> Iterator for TraceWindowsMutIter<'a, T> { 73 | type Item = TraceWindowMut<'a, T>; 74 | 75 | fn next(&mut self) -> Option { 76 | if self.current_row >= self.height - 1 { 77 | return None; 78 | } 79 | let slice = core::mem::take(&mut self.values); 80 | let (local_row, rest) = slice.split_at_mut(self.width); 81 | let (next_row, new_values) = rest.split_at_mut(self.width); 82 | self.values = new_values; 83 | let current_row = self.current_row; 84 | self.current_row += 1; 85 | Some(TraceWindowMut { 86 | local_slice: local_row, 87 | next_slice: next_row, 88 | row: self.current_row, 89 | is_first_row: current_row == 0, 90 | is_last_row: current_row == self.height - 1, 91 | }) 92 | } 93 | } 94 | 95 | impl<'a, T> ExactSizeIterator for TraceWindowsMutIter<'a, T> { 96 | fn len(&self) -> usize { 97 | self.height - 1 - self.current_row 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /starkyx/src/trace/window_parser.rs: -------------------------------------------------------------------------------- 1 | use super::window::TraceWindow; 2 | use crate::air::extension::cubic::CubicParser; 3 | use crate::air::parser::AirParser; 4 | use crate::math::prelude::*; 5 | use crate::polynomial::parser::PolynomialParser; 6 | 7 | #[derive(Debug, Clone)] 8 | pub struct TraceWindowParser<'a, T> { 9 | window: TraceWindow<'a, T>, 10 | challenge_slice: &'a [T], 11 | global_slice: &'a [T], 12 | public_slice: &'a [T], 13 | } 14 | 15 | impl<'a, T> TraceWindowParser<'a, T> { 16 | pub fn new( 17 | window: TraceWindow<'a, T>, 18 | challenge_slice: &'a [T], 19 | global_slice: &'a [T], 20 | public_slice: &'a [T], 21 | ) -> Self { 22 | Self { 23 | window, 24 | challenge_slice, 25 | global_slice, 26 | public_slice, 27 | } 28 | } 29 | } 30 | 31 | impl<'a, F: Field> AirParser for TraceWindowParser<'a, F> { 32 | type Field = F; 33 | 34 | type Var = F; 35 | 36 | fn local_slice(&self) -> &[Self::Var] { 37 | self.window.local_slice 38 | } 39 | 40 | fn next_slice(&self) -> &[Self::Var] { 41 | self.window.next_slice 42 | } 43 | 44 | fn challenge_slice(&self) -> &[Self::Var] { 45 | self.challenge_slice 46 | } 47 | 48 | fn global_slice(&self) -> &[Self::Var] { 49 | self.global_slice 50 | } 51 | 52 | fn public_slice(&self) -> &[Self::Var] { 53 | self.public_slice 54 | } 55 | 56 | fn constraint(&mut self, constraint: Self::Var) { 57 | assert_eq!( 58 | constraint, 59 | F::ZERO, 60 | "Nonzero constraint: {:?} at row: {}", 61 | constraint, 62 | self.window.row 63 | ); 64 | } 65 | 66 | fn constraint_transition(&mut self, constraint: Self::Var) { 67 | if !self.window.is_last_row { 68 | assert_eq!( 69 | constraint, 70 | F::ZERO, 71 | "Nonzero constraint: {:?} at row: {}", 72 | constraint, 73 | self.window.row 74 | ); 75 | } 76 | } 77 | 78 | fn constraint_first_row(&mut self, constraint: Self::Var) { 79 | if self.window.is_first_row { 80 | assert_eq!( 81 | constraint, 82 | F::ZERO, 83 | "Nonzero constraint at first row: {constraint:?}" 84 | ); 85 | } 86 | } 87 | 88 | fn constraint_last_row(&mut self, constraint: Self::Var) { 89 | if self.window.is_last_row { 90 | assert_eq!( 91 | constraint, 92 | F::ZERO, 93 | "Nonzero constraint at last row: {constraint:?}" 94 | ); 95 | } 96 | } 97 | 98 | fn constant(&mut self, value: Self::Field) -> Self::Var { 99 | value 100 | } 101 | 102 | fn add(&mut self, a: Self::Var, b: Self::Var) -> Self::Var { 103 | a + b 104 | } 105 | 106 | fn sub(&mut self, a: Self::Var, b: Self::Var) -> Self::Var { 107 | a - b 108 | } 109 | 110 | fn neg(&mut self, a: Self::Var) -> Self::Var { 111 | -a 112 | } 113 | 114 | fn mul(&mut self, a: Self::Var, b: Self::Var) -> Self::Var { 115 | a * b 116 | } 117 | } 118 | 119 | impl<'a, F: Field> PolynomialParser for TraceWindowParser<'a, F> {} 120 | 121 | impl<'a, F: Field, E: CubicParameters> CubicParser for TraceWindowParser<'a, F> {} 122 | -------------------------------------------------------------------------------- /starkyx/src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod serde; 2 | pub mod watcher; 3 | -------------------------------------------------------------------------------- /starkyx/src/utils/watcher.rs: -------------------------------------------------------------------------------- 1 | use log::{log, Level}; 2 | use plonky2::field::extension::Extendable; 3 | use plonky2::hash::hash_types::RichField; 4 | use plonky2::iop::generator::{GeneratedValues, SimpleGenerator}; 5 | use plonky2::iop::target::Target; 6 | use plonky2::iop::witness::{PartitionWitness, Witness}; 7 | use plonky2::plonk::circuit_builder::CircuitBuilder; 8 | use plonky2::plonk::circuit_data::CommonCircuitData; 9 | use plonky2::util::serialization::IoResult; 10 | 11 | use crate::math::field::PrimeField64; 12 | 13 | pub trait Watcher { 14 | fn watch(&mut self, log: &str, targets: Vec); 15 | } 16 | 17 | impl, const D: usize> Watcher for CircuitBuilder { 18 | fn watch(&mut self, log: &str, targets: Vec) { 19 | let log = String::from(log); 20 | 21 | let generator: WatchGenerator = WatchGenerator { targets, log }; 22 | self.add_simple_generator(generator); 23 | } 24 | } 25 | 26 | #[derive(Debug, Clone)] 27 | pub struct WatchGenerator { 28 | pub targets: Vec, 29 | pub log: String, 30 | } 31 | 32 | impl WatchGenerator { 33 | pub fn id() -> String { 34 | "WatchGenerator".to_string() 35 | } 36 | } 37 | 38 | impl, const D: usize> SimpleGenerator for WatchGenerator { 39 | fn id(&self) -> String { 40 | Self::id() 41 | } 42 | 43 | fn dependencies(&self) -> Vec { 44 | self.targets.clone() 45 | } 46 | 47 | #[allow(unused_variables)] 48 | fn serialize(&self, dst: &mut Vec, _common_data: &CommonCircuitData) -> IoResult<()> { 49 | unimplemented!() 50 | } 51 | 52 | #[allow(unused_variables)] 53 | fn deserialize( 54 | src: &mut plonky2::util::serialization::Buffer, 55 | _common_data: &CommonCircuitData, 56 | ) -> IoResult 57 | where 58 | Self: Sized, 59 | { 60 | unimplemented!() 61 | } 62 | 63 | fn run_once(&self, witness: &PartitionWitness, _out_buffer: &mut GeneratedValues) { 64 | let values: Vec = self 65 | .targets 66 | .iter() 67 | .map(|x| witness.get_target(*x).as_canonical_u64()) 68 | .collect(); 69 | let formatted_log = if values.len() == 1 { 70 | format!("[Watch] {}: {:?}", self.log, values[0]) 71 | } else { 72 | format!("[Watch] {}: {:?}", self.log, values) 73 | }; 74 | log!(Level::Info, "{}", formatted_log); 75 | } 76 | } 77 | --------------------------------------------------------------------------------