diff --git a/.gitignore b/.gitignore index eb6b410bed..b8c715a199 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,7 @@ pgo-data.profdata .DS_Store **/generated -docs/**bench** \ No newline at end of file +docs/**bench** + +**/*/libposeidon-permute-c-mac.a +**/*/go-iden3-crypto/ \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 3c68216515..dc862bf2bb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ -[submodule "cryptography_cuda"] - path = cryptography_cuda - url = git@github.com:okx/cryptography_cuda.git \ No newline at end of file +[submodule "depends/cryptography_cuda"] + path = depends/cryptography_cuda + url = git@github.com:okx/cryptography_cuda.git diff --git a/Cargo.toml b/Cargo.toml index f4f0087aca..5bb77ff700 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,5 +9,4 @@ incremental = true #codegen-units = 1 [profile.bench] -opt-level = 3 - +opt-level = 3 \ No newline at end of file diff --git a/cryptography_cuda b/cryptography_cuda deleted file mode 160000 index 4a900ea12f..0000000000 --- a/cryptography_cuda +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4a900ea12f275b2b2e3aa1004e2ed549697dd31c diff --git a/depends/cryptography_cuda b/depends/cryptography_cuda new file mode 160000 index 0000000000..f22980c5eb --- /dev/null +++ b/depends/cryptography_cuda @@ -0,0 +1 @@ +Subproject commit f22980c5eb81301d56fa39baf777c57c19a97102 diff --git a/docs/rust.md b/docs/rust.md new file mode 100644 index 0000000000..9983bf91e7 --- /dev/null +++ b/docs/rust.md @@ -0,0 +1,9 @@ +rustc 1.78.0-nightly will throw below error +``` +error[E0635]: unknown feature `stdsimd` +``` + +``` +rustup toolchian install nightly-2024-02-04 +rustup override set nightly-2024-02-04 +``` \ No newline at end of file diff --git a/evm/Cargo.toml b/evm/Cargo.toml index e328aa0c97..e59df23509 100644 --- a/evm/Cargo.toml +++ b/evm/Cargo.toml @@ -38,7 +38,7 @@ hashbrown = { version = "0.14.0" } tiny-keccak = "2.0.2" serde_json = "1.0" -[target.'cfg(not(target_env = "msvc"))'.dependencies] +[target.'cfg(not(target_os = "macos"))'.dependencies] jemallocator = "0.5.0" [dev-dependencies] diff --git a/evm/src/lib.rs b/evm/src/lib.rs index b678ec58e4..06013ae172 100644 --- a/evm/src/lib.rs +++ b/evm/src/lib.rs @@ -35,10 +35,10 @@ pub mod witness; use eth_trie_utils::partial_trie::HashedPartialTrie; // Set up Jemalloc -#[cfg(not(target_env = "msvc"))] +#[cfg(not(target_os = "macos"))] use jemallocator::Jemalloc; -#[cfg(not(target_env = "msvc"))] +#[cfg(not(target_os = "macos"))] #[global_allocator] static GLOBAL: Jemalloc = Jemalloc; diff --git a/field/Cargo.toml b/field/Cargo.toml index 2396d31ca3..2eeafc0454 100644 --- a/field/Cargo.toml +++ b/field/Cargo.toml @@ -10,7 +10,6 @@ edition = "2021" name = "fft" [dependencies] -cryptography_cuda = {path="../cryptography_cuda", optional = true} anyhow = { version = "1.0.40", default-features = false } itertools = { version = "0.11.0", default-features = false, features = ["use_alloc"] } num = { version = "0.4", default-features = false, features = ["alloc", "rand"] } @@ -20,6 +19,7 @@ serde = { version = "1.0", default-features = false, features = ["alloc", "deriv static_assertions = { version = "1.1.0", default-features = false } unroll = { version = "0.1.5", default-features = false } lazy_static = "1.4.0" +cryptography_cuda ={path="../depends/cryptography_cuda", optional=true} [dev-dependencies] rand = "*" @@ -32,5 +32,7 @@ proc-macro2 = "1" quote = "1" [features] -default = [] -cuda = ["cryptography_cuda"] \ No newline at end of file +default = ["no_cuda"] +cuda = ["cryptography_cuda"] +precompile = [] +no_cuda = ["cryptography_cuda/no_cuda"] \ No newline at end of file diff --git a/field/build.rs b/field/build.rs index 73911ea223..e5af9f880d 100644 --- a/field/build.rs +++ b/field/build.rs @@ -1,16 +1,23 @@ use std::env; use std::fs::write; -use std::path::{Path, PathBuf}; +use std::path::{Path}; +#[cfg(feature = "precompile")] +use std::Path::{PathBuf}; use std::process::Command; +#[cfg(feature = "precompile")] use std::str::FromStr; use anyhow::Context; +#[cfg(feature = "precompile")] use plonky2_util::pre_compute::{get_pre_compute_size, PRE_COMPUTE_END, PRE_COMPUTE_START}; use proc_macro2::TokenStream; +#[cfg(feature = "precompile")] use quote::quote; +#[cfg(feature = "precompile")] use syn::Lit; -fn main() { +#[cfg(feature = "precompile")] +fn build_precompile() { println!("cargo:rerun-if-changed=generated"); let cargo_manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); @@ -21,7 +28,13 @@ fn main() { let token_stream = build_token_stream(&path).expect("build token stream error"); _ = write_generated_file(token_stream, "goldilock_root_of_unity.rs"); } +fn main() { + + #[cfg(feature = "precompile")] + build_precompile(); +} +#[cfg(feature = "precompile")] fn build_token_stream(path: &PathBuf) -> anyhow::Result { let size = get_pre_compute_size(PRE_COMPUTE_START, PRE_COMPUTE_END); let token = syn::parse_str::(&format!("{}", size)).unwrap(); diff --git a/field/src/goldilocks_field.rs b/field/src/goldilocks_field.rs index 0233d012a8..a9d6804648 100644 --- a/field/src/goldilocks_field.rs +++ b/field/src/goldilocks_field.rs @@ -4,12 +4,17 @@ use core::iter::{Product, Sum}; use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; use num::{BigUint, Integer}; -use plonky2_util::{assume, branch_hint, log2_strict}; +use plonky2_util::{assume, branch_hint}; use serde::{Deserialize, Serialize}; +#[cfg(feature = "precompile")] +use plonky2_util::{log2_strict}; +#[cfg(feature = "precompile")] use crate::fft::FftRootTable; use crate::ops::Square; use crate::types::{Field, Field64, PrimeField, PrimeField64, Sample}; + +#[cfg(feature = "precompile")] use crate::PRE_COMPUTE_ROOT_TABLES; const EPSILON: u64 = (1 << 32) - 1; @@ -98,6 +103,7 @@ impl Field for GoldilocksField { Self::order() } + #[cfg(feature = "precompile")] fn pre_compute_fft_root_table(input_len: usize) -> Option<&'static FftRootTable> { let lg_n = log2_strict(input_len); PRE_COMPUTE_ROOT_TABLES.get(&lg_n) diff --git a/field/src/lib.rs b/field/src/lib.rs index dca90c497c..e0454e52d4 100644 --- a/field/src/lib.rs +++ b/field/src/lib.rs @@ -33,15 +33,21 @@ mod field_testing; #[cfg(test)] mod prime_field_testing; +#[cfg(feature = "precompile")] include!(concat!(env!("OUT_DIR"), "/goldilock_root_of_unity.rs")); +#[cfg(feature = "precompile")] use std::collections::HashMap; - +#[cfg(feature = "precompile")] use fft::FftRootTable; +#[cfg(feature = "precompile")] use goldilocks_field::GoldilocksField; +#[cfg(feature = "precompile")] use lazy_static::lazy_static; +#[cfg(feature = "precompile")] use plonky2_util::pre_compute::{PRE_COMPUTE_END, PRE_COMPUTE_START}; +#[cfg(feature = "precompile")] lazy_static! { pub static ref PRE_COMPUTE_ROOT_TABLES: HashMap> = { let mut map = HashMap::new(); @@ -75,6 +81,8 @@ lazy_static! { #[cfg(test)] mod test { use super::*; + + #[cfg(feature = "precompile")] #[test] fn test_pre_compute() { for lgn_size in (PRE_COMPUTE_START..=PRE_COMPUTE_END) { diff --git a/gen/build.rs b/gen/build.rs index 529066d712..2a1e364e9b 100644 --- a/gen/build.rs +++ b/gen/build.rs @@ -1,3 +1,5 @@ +#![allow(incomplete_features)] + #![feature(generic_const_exprs)] use std::fs::File; diff --git a/plonky2/Cargo.toml b/plonky2/Cargo.toml index 654ad1a205..70f99d81d8 100644 --- a/plonky2/Cargo.toml +++ b/plonky2/Cargo.toml @@ -17,6 +17,7 @@ parallel = ["hashbrown/rayon", "plonky2_maybe_rayon/parallel"] std = ["anyhow/std", "rand/std", "itertools/use_std"] timing = ["std"] cuda =["cryptography_cuda"] +no_cuda = ["cryptography_cuda/no_cuda"] batch =[] [dependencies] @@ -36,7 +37,7 @@ serde = { version = "1.0", default-features = false, features = ["derive", "rc"] serde_json = "1.0" static_assertions = { version = "1.1.0", default-features = false } unroll = { version = "0.1.5", default-features = false } -cryptography_cuda ={path="../cryptography_cuda", optional=true} +cryptography_cuda ={path="../depends/cryptography_cuda", optional=true} [target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies] getrandom = { version = "0.2", default-features = false, features = ["js"] } @@ -51,9 +52,12 @@ serde_cbor = { version = "0.11.2" } structopt = { version = "0.3.26", default-features = false } tynm = { version = "0.1.6", default-features = false } -[target.'cfg(not(target_env = "msvc"))'.dev-dependencies] +[target.'cfg(not(target_os = "macos"))'.dev-dependencies] jemallocator = "0.5.0" +[build-dependencies] +bindgen = "*" + [[bin]] name = "generate_constants" required-features = ["rand_chacha"] diff --git a/plonky2/benches/allocator/mod.rs b/plonky2/benches/allocator/mod.rs index 441e5dc500..67c0a3bd3b 100644 --- a/plonky2/benches/allocator/mod.rs +++ b/plonky2/benches/allocator/mod.rs @@ -1,7 +1,7 @@ // Set up Jemalloc -#[cfg(not(target_env = "msvc"))] +#[cfg(not(target_os = "macos"))] use jemallocator::Jemalloc; -#[cfg(not(target_env = "msvc"))] +#[cfg(not(target_os = "macos"))] #[global_allocator] static GLOBAL: Jemalloc = Jemalloc; diff --git a/plonky2/build.rs b/plonky2/build.rs new file mode 100644 index 0000000000..0a55ee4740 --- /dev/null +++ b/plonky2/build.rs @@ -0,0 +1,84 @@ +extern crate bindgen; + +use std::env; +use std::path::{Path, PathBuf}; + +fn main() { + let dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + + let poseidon_dir = Path::new(&dir).join("poseidon_bn128"); + + if cfg!(target_os = "macos") { + println!("target os is macos"); + let output = std::process::Command::new("go") + .arg("version") + .output() + .expect("Failed to run command"); + + // Check the exit status + if output.status.success() { + // Go is installed + println!( + "Go is installed: {:?}", + String::from_utf8_lossy(&output.stdout) + ); + } else { + // Go is not installed + panic!("Go is not installed"); + } + let poseidon_c_dir = Path::new(&dir).join("go-iden3-crypto"); + println!("poseidon_c_dir: {:?}", poseidon_c_dir); + if poseidon_c_dir.exists() { + std::process::Command::new("sh") + .arg("-c") + .arg("rm") + .arg("-rf") + .arg(poseidon_c_dir.clone()) + .output() + .expect("rm go-iden3-crypto failure"); + } + println!("start clone go iden3"); + std::process::Command::new("git") + .arg("clone") + .arg("https://github.com/polymerdao/go-iden3-crypto.git") + .arg(poseidon_c_dir.clone()) + .output() + .expect("clone go iden3 crypto failure"); + println!("end clone go iden3"); + + let ret = std::process::Command::new("sh") + .arg("-c") + .arg("./compile.sh") + .current_dir(poseidon_c_dir.clone().join("poseidon-permute-c")) + .output() + .expect("compile poseidon permute c failure"); + println!("compile lib ret: {:?}", ret); + + std::process::Command::new("mv") + .arg("libposeidon-permute-c.a") + .arg(poseidon_dir.join("libposeidon-permute-c-mac.a")) + .current_dir(poseidon_c_dir.clone().join("poseidon-permute-c")) + .output() + .expect("mv failure"); + } + println!("cargo:rustc-link-search=native={}", poseidon_dir.display()); + + if cfg!(target_os = "macos") { + println!("link to mac lib"); + println!("cargo:rustc-link-lib=static=poseidon-permute-c-mac"); + } else { + println!("link to linux lib"); + println!("cargo:rustc-link-lib=static=poseidon-permute-c"); + } + + let bindings = bindgen::Builder::default() + .header(poseidon_dir.join("wrapper.h").display().to_string()) + .generate() + .expect("Unable to generate bindings"); + + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + println!("{}", out_path.to_str().unwrap()); + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); +} diff --git a/plonky2/poseidon_bn128/libposeidon-permute-c.a b/plonky2/poseidon_bn128/libposeidon-permute-c.a new file mode 100644 index 0000000000..676bb7b540 Binary files /dev/null and b/plonky2/poseidon_bn128/libposeidon-permute-c.a differ diff --git a/plonky2/poseidon_bn128/libposeidon-permute-c.h b/plonky2/poseidon_bn128/libposeidon-permute-c.h new file mode 100644 index 0000000000..764eea9fab --- /dev/null +++ b/plonky2/poseidon_bn128/libposeidon-permute-c.h @@ -0,0 +1,94 @@ +/* Code generated by cmd/cgo; DO NOT EDIT. */ + +/* package main */ + + +#line 1 "cgo-builtin-export-prolog" + +#include /* for ptrdiff_t below */ + +#ifndef GO_CGO_EXPORT_PROLOGUE_H +#define GO_CGO_EXPORT_PROLOGUE_H + +#ifndef GO_CGO_GOSTRING_TYPEDEF +typedef struct { const char *p; ptrdiff_t n; } _GoString_; +#endif + +#endif + +/* Start of preamble from import "C" comments. */ + + + + +/* End of preamble from import "C" comments. */ + + +/* Start of boilerplate cgo prologue. */ +#line 1 "cgo-gcc-export-header-prolog" + +#ifndef GO_CGO_PROLOGUE_H +#define GO_CGO_PROLOGUE_H + +typedef signed char GoInt8; +typedef unsigned char GoUint8; +typedef short GoInt16; +typedef unsigned short GoUint16; +typedef int GoInt32; +typedef unsigned int GoUint32; +typedef long long GoInt64; +typedef unsigned long long GoUint64; +typedef GoInt64 GoInt; +typedef GoUint64 GoUint; +typedef __SIZE_TYPE__ GoUintptr; +typedef float GoFloat32; +typedef double GoFloat64; +typedef float _Complex GoComplex64; +typedef double _Complex GoComplex128; + +/* + static assertion to make sure the file is being used on architecture + at least with matching size of GoInt. +*/ +typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1]; + +#ifndef GO_CGO_GOSTRING_TYPEDEF +typedef _GoString_ GoString; +#endif +typedef void *GoMap; +typedef void *GoChan; +typedef struct { void *t; void *v; } GoInterface; +typedef struct { void *data; GoInt len; GoInt cap; } GoSlice; + +#endif + +/* End of boilerplate cgo prologue. */ + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Return type for permute */ +struct permute_return { + GoUint64 r0; + GoUint64 r1; + GoUint64 r2; + GoUint64 r3; + GoUint64 r4; + GoUint64 r5; + GoUint64 r6; + GoUint64 r7; + GoUint64 r8; + GoUint64 r9; + GoUint64 r10; + GoUint64 r11; +}; + +// converts three uint64 into one big int and run poseidon +// +extern struct permute_return permute(GoUint64 e0, GoUint64 e1, GoUint64 e2, GoUint64 e3, GoUint64 e4, GoUint64 e5, GoUint64 e6, GoUint64 e7, GoUint64 e8, GoUint64 e9, GoUint64 e10, GoUint64 e11); + +#ifdef __cplusplus +} +#endif diff --git a/plonky2/poseidon_bn128/wrapper.h b/plonky2/poseidon_bn128/wrapper.h new file mode 100644 index 0000000000..7fa0873e72 --- /dev/null +++ b/plonky2/poseidon_bn128/wrapper.h @@ -0,0 +1 @@ +#include "libposeidon-permute-c.h" diff --git a/plonky2/src/gates/arithmetic_base.rs b/plonky2/src/gates/arithmetic_base.rs index 631b1c3715..5c32d45f6c 100644 --- a/plonky2/src/gates/arithmetic_base.rs +++ b/plonky2/src/gates/arithmetic_base.rs @@ -69,6 +69,51 @@ impl, const D: usize> Gate for ArithmeticGate Ok(Self { num_ops }) } + fn export_circom_verification_code(&self) -> String { + let mut template_str = format!( + "template Arithmetic$NUM_OPS() {{ + signal input constants[NUM_OPENINGS_CONSTANTS()][2]; + signal input wires[NUM_OPENINGS_WIRES()][2]; + signal input public_input_hash[4]; + signal input constraints[NUM_GATE_CONSTRAINTS()][2]; + signal output out[NUM_GATE_CONSTRAINTS()][2]; + + signal filter[2]; + $SET_FILTER; + + for (var i = 0; i < $NUM_OPS; i++) {{ + out[i] <== ConstraintPush()(constraints[i], filter, GlExtSub()(wires[4 * i + 3], GlExtAdd()(GlExtMul()(GlExtMul()(wires[4 * i], wires[4 * i + 1]), constants[$NUM_SELECTORS + 0]), GlExtMul()(wires[4 * i + 2], constants[$NUM_SELECTORS + 1])))); + }} + + for (var i = $NUM_OPS; i < NUM_GATE_CONSTRAINTS(); i++) {{ + out[i] <== constraints[i]; + }} +}}" + ).to_string(); + template_str = template_str.replace("$NUM_OPS", &*self.num_ops.to_string()); + template_str + } + fn export_solidity_verification_code(&self) -> String { + let mut template_str = format!( + "library Arithmetic$NUM_OPSLib {{ + using GoldilocksExtLib for uint64[2]; + function set_filter(GatesUtilsLib.EvaluationVars memory ev) internal pure {{ + $SET_FILTER; + }} + function eval(GatesUtilsLib.EvaluationVars memory ev, uint64[2][$NUM_GATE_CONSTRAINTS] memory constraints) internal pure {{ + for (uint32 i = 0; i < $NUM_OPS; i++) {{ + uint64[2] memory constraint; + constraint = ev.wires[4 * i].mul(ev.wires[4 * i + 1]).mul(ev.constants[$NUM_SELECTORS + 0]).add(ev.wires[4 * i + 2].mul(ev.constants[$NUM_SELECTORS + 1])); + GatesUtilsLib.push(constraints, ev.filter, i, ev.wires[4 * i + 3].sub(constraint)); + }} + }} +}}" + ) + .to_string(); + template_str = template_str.replace("$NUM_OPS", &*self.num_ops.to_string()); + template_str + } + fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { let const_0 = vars.local_constants[0]; let const_1 = vars.local_constants[1]; diff --git a/plonky2/src/gates/arithmetic_extension.rs b/plonky2/src/gates/arithmetic_extension.rs index 294c090274..f9ac3a9aaf 100644 --- a/plonky2/src/gates/arithmetic_extension.rs +++ b/plonky2/src/gates/arithmetic_extension.rs @@ -65,6 +65,57 @@ impl, const D: usize> Gate for ArithmeticExte Ok(Self { num_ops }) } + fn export_circom_verification_code(&self) -> String { + let mut template_str = format!( + "template ArithmeticExtension$NUM_OPS() {{ + signal input constants[NUM_OPENINGS_CONSTANTS()][2]; + signal input wires[NUM_OPENINGS_WIRES()][2]; + signal input public_input_hash[4]; + signal input constraints[NUM_GATE_CONSTRAINTS()][2]; + signal output out[NUM_GATE_CONSTRAINTS()][2]; + + signal filter[2]; + $SET_FILTER; + + signal m[$NUM_OPS][2][2]; + for (var i = 0; i < $NUM_OPS; i++) {{ + m[i] <== WiresAlgebraMul(4 * $D * i, 4 * $D * i + $D)(wires); + for (var j = 0; j < $D; j++) {{ + out[i * $D + j] <== ConstraintPush()(constraints[i * $D + j], filter, GlExtSub()(wires[4 * $D * i + 3 * $D + j], GlExtAdd()(GlExtMul()(m[i][j], constants[$NUM_SELECTORS]), GlExtMul()(wires[4 * $D * i + 2 * $D + j], constants[$NUM_SELECTORS + 1])))); + }} + }} + + for (var i = $NUM_OPS * $D; i < NUM_GATE_CONSTRAINTS(); i++) {{ + out[i] <== constraints[i]; + }} +}}" + ).to_string(); + template_str = template_str.replace("$NUM_OPS", &*self.num_ops.to_string()); + template_str = template_str.replace("$D", &*D.to_string()); + template_str + } + fn export_solidity_verification_code(&self) -> String { + let mut template_str = format!( + "library ArithmeticExtension$NUM_OPSLib {{ + using GoldilocksExtLib for uint64[2]; + function set_filter(GatesUtilsLib.EvaluationVars memory ev) internal pure {{ + $SET_FILTER; + }} + function eval(GatesUtilsLib.EvaluationVars memory ev, uint64[2][$NUM_GATE_CONSTRAINTS] memory constraints) internal pure {{ + for (uint32 i = 0; i < $NUM_OPS; i++) {{ + uint64[2][$D] memory m = GatesUtilsLib.wires_algebra_mul(ev.wires, 4 * $D * i, 4 * $D * i + $D); + for (uint32 j = 0; j < $D; j++) {{ + GatesUtilsLib.push(constraints, ev.filter, i * $D + j, ev.wires[4 * $D * i + 3 * $D + j].sub(m[j].mul(ev.constants[$NUM_SELECTORS]).add(ev.wires[4 * $D * i + 2 * $D + j].mul(ev.constants[$NUM_SELECTORS + 1])))); + }} + }} + }} +}}" + ) + .to_string(); + template_str = template_str.replace("$NUM_OPS", &*self.num_ops.to_string()); + template_str + } + fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { let const_0 = vars.local_constants[0]; let const_1 = vars.local_constants[1]; diff --git a/plonky2/src/gates/base_sum.rs b/plonky2/src/gates/base_sum.rs index 181252a2d3..7f50e01acd 100644 --- a/plonky2/src/gates/base_sum.rs +++ b/plonky2/src/gates/base_sum.rs @@ -64,6 +64,76 @@ impl, const D: usize, const B: usize> Gate fo Ok(Self { num_limbs }) } + fn export_circom_verification_code(&self) -> String { + let mut template_str = format!( + "template BaseSum$NUM_LIMBS() {{ + signal input constants[NUM_OPENINGS_CONSTANTS()][2]; + signal input wires[NUM_OPENINGS_WIRES()][2]; + signal input public_input_hash[4]; + signal input constraints[NUM_GATE_CONSTRAINTS()][2]; + signal output out[NUM_GATE_CONSTRAINTS()][2]; + + signal filter[2]; + $SET_FILTER; + + component reduce = Reduce($NUM_LIMBS); + reduce.alpha <== GlExt($B, 0)(); + reduce.old_eval <== GlExt(0, 0)(); + for (var i = 1; i < $NUM_LIMBS + 1; i++) {{ + reduce.in[i - 1] <== wires[i]; + }} + out[0] <== ConstraintPush()(constraints[0], filter, GlExtSub()(reduce.out, wires[0])); + component product[$NUM_LIMBS][$B - 1]; + for (var i = 0; i < $NUM_LIMBS; i++) {{ + for (var j = 0; j < $B - 1; j++) {{ + product[i][j] = GlExtMul(); + if (j == 0) product[i][j].a <== wires[i + 1]; + else product[i][j].a <== product[i][j - 1].out; + product[i][j].b <== GlExtSub()(wires[i + 1], GlExt(j + 1, 0)()); + }} + out[i + 1] <== ConstraintPush()(constraints[i + 1], filter, product[i][$B - 2].out); + }} + for (var i = $NUM_LIMBS + 1; i < NUM_GATE_CONSTRAINTS(); i++) {{ + out[i] <== constraints[i]; + }} +}}" + ) + .to_string(); + template_str = template_str.replace("$NUM_LIMBS", &*self.num_limbs.to_string()); + template_str = template_str.replace("$B", &*B.to_string()); + + template_str + } + fn export_solidity_verification_code(&self) -> String { + let mut template_str = format!("library BaseSum$NUM_LIMBSLib {{ + using GoldilocksExtLib for uint64[2]; + function set_filter(GatesUtilsLib.EvaluationVars memory ev) internal pure {{ + $SET_FILTER; + }} + function eval(GatesUtilsLib.EvaluationVars memory ev, uint64[2][$NUM_GATE_CONSTRAINTS] memory constraints) internal pure {{ + uint64[2] memory sum; + for (uint32 i = $NUM_LIMBS + 1; i > 1; i--) {{ + sum = sum.mul(GatesUtilsLib.field_ext_from($B, 0)).add(ev.wires[i - 1]); + }} + GatesUtilsLib.push(constraints, ev.filter, 0, sum.sub(ev.wires[0])); + for (uint32 i = 1; i < $NUM_LIMBS + 1; i++) {{ + uint64[2] memory product = ev.wires[i]; + for (uint32 j = 1; j < $B; j++) {{ + product = product.mul(ev.wires[i].sub(GatesUtilsLib.field_ext_from(j, 0))); + }} + GatesUtilsLib.push(constraints, ev.filter, i, product); + }} + }} +}}" + ) + .to_string(); + + template_str = template_str.replace("$NUM_LIMBS", &*self.num_limbs.to_string()); + template_str = template_str.replace("$B", &*B.to_string()); + + template_str + } + fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { let sum = vars.local_wires[Self::WIRE_SUM]; let limbs = vars.local_wires[self.limbs()].to_vec(); diff --git a/plonky2/src/gates/constant.rs b/plonky2/src/gates/constant.rs index 965b30b6fb..fdb04843a4 100644 --- a/plonky2/src/gates/constant.rs +++ b/plonky2/src/gates/constant.rs @@ -56,6 +56,50 @@ impl, const D: usize> Gate for ConstantGate { Ok(Self { num_consts }) } + fn export_circom_verification_code(&self) -> String { + let mut template_str = format!( + "template Constant$NUM_CONSTANTS() {{ + signal input constants[NUM_OPENINGS_CONSTANTS()][2]; + signal input wires[NUM_OPENINGS_WIRES()][2]; + signal input public_input_hash[4]; + signal input constraints[NUM_GATE_CONSTRAINTS()][2]; + signal output out[NUM_GATE_CONSTRAINTS()][2]; + + signal filter[2]; + $SET_FILTER; + + for (var i = 0; i < $NUM_CONSTANTS; i++) {{ + out[i] <== ConstraintPush()(constraints[i], filter, GlExtSub()(constants[$NUM_SELECTORS + i], wires[i])); + }} + for (var i = $NUM_CONSTANTS; i < NUM_GATE_CONSTRAINTS(); i++) {{ + out[i] <== constraints[i]; + }} +}}" + ).to_string(); + template_str = template_str.replace("$NUM_CONSTANTS", &*self.num_consts.to_string()); + template_str + } + fn export_solidity_verification_code(&self) -> String { + let mut template_str = format!( + "library Constant$NUM_CONSTANTSLib {{ + function set_filter(GatesUtilsLib.EvaluationVars memory ev) internal pure {{ + $SET_FILTER; + }} + using GoldilocksExtLib for uint64[2]; + function eval(GatesUtilsLib.EvaluationVars memory ev, uint64[2][$NUM_GATE_CONSTRAINTS] memory constraints) internal pure {{ + for (uint32 i = 0; i < $NUM_CONSTANTS; i++) {{ + GatesUtilsLib.push(constraints, ev.filter, i, ev.constants[$NUM_SELECTORS + i].sub(ev.wires[i])); + }} + }} +}}" + ) + .to_string(); + + template_str = template_str.replace("$NUM_CONSTANTS", &*self.num_consts.to_string()); + + template_str + } + fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { (0..self.num_consts) .map(|i| { diff --git a/plonky2/src/gates/coset_interpolation.rs b/plonky2/src/gates/coset_interpolation.rs index c701b8cf7d..ad870d49da 100644 --- a/plonky2/src/gates/coset_interpolation.rs +++ b/plonky2/src/gates/coset_interpolation.rs @@ -189,6 +189,14 @@ impl, const D: usize> Gate for CosetInterpola }) } + fn export_circom_verification_code(&self) -> String { + unimplemented!() + } + + fn export_solidity_verification_code(&self) -> String { + unimplemented!() + } + fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { let mut constraints = Vec::with_capacity(self.num_constraints()); diff --git a/plonky2/src/gates/exponentiation.rs b/plonky2/src/gates/exponentiation.rs index 521520e86a..54ec1a0002 100644 --- a/plonky2/src/gates/exponentiation.rs +++ b/plonky2/src/gates/exponentiation.rs @@ -120,6 +120,77 @@ impl, const D: usize> Gate for Exponentiation constraints } + fn export_circom_verification_code(&self) -> String { + let mut template_str = format!( + "template Exponentiation$NUM_POWER_BITS() {{ + signal input constants[NUM_OPENINGS_CONSTANTS()][2]; + signal input wires[NUM_OPENINGS_WIRES()][2]; + signal input public_input_hash[4]; + signal input constraints[NUM_GATE_CONSTRAINTS()][2]; + signal output out[NUM_GATE_CONSTRAINTS()][2]; + + signal filter[2]; + $SET_FILTER; + + out[0] <== ConstraintPush()(constraints[0], filter, + GlExtSub()(GlExtMul()(GlExt(1, 0)(), + GlExtAdd()(GlExtMul()(wires[$NUM_POWER_BITS], wires[0]), + GlExtSub()(GlExt(1, 0)(), wires[$NUM_POWER_BITS]) + ) + ), + wires[$NUM_POWER_BITS + 2])); + for (var i = 1; i < $NUM_POWER_BITS; i++) {{ + // prev_intermediate_value * (cur_bit * wires[0] + (1 - cur_bit)) - wires[$NUM_POWER_BITS + 2 + i] + out[i] <== ConstraintPush()(constraints[i], filter, + GlExtSub()(GlExtMul()(GlExtSquare()(wires[$NUM_POWER_BITS + 1 + i]), + GlExtAdd()(GlExtMul()(wires[$NUM_POWER_BITS - i], wires[0]), + GlExtSub()(GlExt(1, 0)(), wires[$NUM_POWER_BITS - i]) + ) + ), + wires[$NUM_POWER_BITS + 2 + i])); + }} + out[$NUM_POWER_BITS] <== ConstraintPush()(constraints[$NUM_POWER_BITS], filter, GlExtSub()(wires[$NUM_POWER_BITS + 1], wires[2 * $NUM_POWER_BITS + 1])); + + for (var i = $NUM_POWER_BITS + 1; i < NUM_GATE_CONSTRAINTS(); i++) {{ + out[i] <== constraints[i]; + }} +}}" + ).to_string(); + template_str = template_str.replace("$NUM_POWER_BITS", &*self.num_power_bits.to_string()); + template_str + } + fn export_solidity_verification_code(&self) -> String { + let mut template_str = format!( + "library Exponentiation$NUM_POWER_BITSLib {{ + using GoldilocksExtLib for uint64[2]; + function set_filter(GatesUtilsLib.EvaluationVars memory ev) internal pure {{ + $SET_FILTER; + }} + function eval(GatesUtilsLib.EvaluationVars memory ev, uint64[2][$NUM_GATE_CONSTRAINTS] memory constraints) internal pure {{ + // wire indices + // base: 0 + // power_bits: [1 + i] + // output: num_power_bits + 1 + // intermediate_values: [num_power_bits + 2 + i] + for (uint32 i = 0; i < $NUM_POWER_BITS; i++) {{ + uint64[2] memory prev_intermediate_value; + if (i == 0) {{ + prev_intermediate_value = GoldilocksExtLib.one(); + }} else {{ + prev_intermediate_value = ev.wires[$NUM_POWER_BITS + 1 + i].square(); + }} + uint64[2] memory cur_bit = ev.wires[$NUM_POWER_BITS - i]; + GatesUtilsLib.push(constraints, ev.filter, i, prev_intermediate_value.mul(cur_bit.mul(ev.wires[0]).add(GoldilocksExtLib.one().sub(cur_bit))).sub(ev.wires[$NUM_POWER_BITS + 2 + i])); + }} + GatesUtilsLib.push(constraints, ev.filter, $NUM_POWER_BITS, ev.wires[$NUM_POWER_BITS + 1].sub(ev.wires[2 * $NUM_POWER_BITS + 1])); + }} +}}" + ) + .to_string(); + template_str = template_str.replace("$NUM_POWER_BITS", &*self.num_power_bits.to_string()); + template_str + } + fn eval_unfiltered_base_one( &self, _vars: EvaluationVarsBase, diff --git a/plonky2/src/gates/gate.rs b/plonky2/src/gates/gate.rs index 2f8f7df16a..82fc412f32 100644 --- a/plonky2/src/gates/gate.rs +++ b/plonky2/src/gates/gate.rs @@ -35,6 +35,9 @@ pub trait Gate, const D: usize>: 'static + Send + S where Self: Sized; + fn export_circom_verification_code(&self) -> String; + fn export_solidity_verification_code(&self) -> String; + fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec; /// Like `eval_unfiltered`, but specialized for points in the base field. diff --git a/plonky2/src/gates/lookup.rs b/plonky2/src/gates/lookup.rs index f682be23f2..e2703befab 100644 --- a/plonky2/src/gates/lookup.rs +++ b/plonky2/src/gates/lookup.rs @@ -99,6 +99,14 @@ impl, const D: usize> Gate for LookupGate { }) } + fn export_circom_verification_code(&self) -> String { + unimplemented!() + } + + fn export_solidity_verification_code(&self) -> String { + unimplemented!() + } + fn eval_unfiltered(&self, _vars: EvaluationVars) -> Vec { // No main trace constraints for lookups. vec![] diff --git a/plonky2/src/gates/lookup_table.rs b/plonky2/src/gates/lookup_table.rs index 9f9d967ea0..0074c71feb 100644 --- a/plonky2/src/gates/lookup_table.rs +++ b/plonky2/src/gates/lookup_table.rs @@ -114,6 +114,14 @@ impl, const D: usize> Gate for LookupTableGat }) } + fn export_circom_verification_code(&self) -> String { + unimplemented!() + } + + fn export_solidity_verification_code(&self) -> String { + unimplemented!() + } + fn eval_unfiltered(&self, _vars: EvaluationVars) -> Vec { // No main trace constraints for the lookup table. vec![] diff --git a/plonky2/src/gates/multiplication_extension.rs b/plonky2/src/gates/multiplication_extension.rs index 8f6b27db60..a0dcb77cb1 100644 --- a/plonky2/src/gates/multiplication_extension.rs +++ b/plonky2/src/gates/multiplication_extension.rs @@ -62,6 +62,57 @@ impl, const D: usize> Gate for MulExtensionGa Ok(Self { num_ops }) } + fn export_circom_verification_code(&self) -> String { + let mut template_str = format!( + "template MultiplicationExtension$NUM_OPS() {{ + signal input constants[NUM_OPENINGS_CONSTANTS()][2]; + signal input wires[NUM_OPENINGS_WIRES()][2]; + signal input public_input_hash[4]; + signal input constraints[NUM_GATE_CONSTRAINTS()][2]; + signal output out[NUM_GATE_CONSTRAINTS()][2]; + + signal filter[2]; + $SET_FILTER; + + signal m[$NUM_OPS][2][2]; + for (var i = 0; i < $NUM_OPS; i++) {{ + m[i] <== WiresAlgebraMul(3 * $D * i, 3 * $D * i + $D)(wires); + for (var j = 0; j < $D; j++) {{ + out[i * $D + j] <== ConstraintPush()(constraints[i * $D + j], filter, GlExtSub()(wires[3 * $D * i + 2 * $D + j], GlExtMul()(m[i][j], constants[$NUM_SELECTORS]))); + }} + }} + for (var i = $NUM_OPS * $D; i < NUM_GATE_CONSTRAINTS(); i++) {{ + out[i] <== constraints[i]; + }} +}}" + ).to_string(); + template_str = template_str.replace("$NUM_OPS", &*self.num_ops.to_string()); + template_str = template_str.replace("$D", &*D.to_string()); + template_str + } + fn export_solidity_verification_code(&self) -> String { + let mut template_str = format!( + "library MultiplicationExtension$NUM_OPSLib {{ + using GoldilocksExtLib for uint64[2]; + function set_filter(GatesUtilsLib.EvaluationVars memory ev) internal pure {{ + $SET_FILTER; + }} + function eval(GatesUtilsLib.EvaluationVars memory ev, uint64[2][$NUM_GATE_CONSTRAINTS] memory constraints) internal pure {{ + for (uint32 i = 0; i < $NUM_OPS; i++) {{ + uint64[2][$D] memory m = GatesUtilsLib.wires_algebra_mul(ev.wires, 3 * $D * i, 3 * $D * i + $D); + for (uint32 j = 0; j < $D; j++) {{ + GatesUtilsLib.push(constraints, ev.filter, i * $D + j, ev.wires[3 * $D * i + 2 * $D + j].sub(m[j].mul(ev.constants[$NUM_SELECTORS]))); + }} + }} + }} +}}" + ) + .to_string(); + template_str = template_str.replace("$NUM_OPS", &*self.num_ops.to_string()); + template_str + } + + fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { let const_0 = vars.local_constants[0]; diff --git a/plonky2/src/gates/noop.rs b/plonky2/src/gates/noop.rs index 8752f380b2..f212369c30 100644 --- a/plonky2/src/gates/noop.rs +++ b/plonky2/src/gates/noop.rs @@ -31,6 +31,15 @@ impl, const D: usize> Gate for NoopGate { Ok(Self) } + fn export_circom_verification_code(&self) -> String { + unimplemented!() + } + + fn export_solidity_verification_code(&self) -> String { + unimplemented!() + } + + fn eval_unfiltered(&self, _vars: EvaluationVars) -> Vec { Vec::new() } diff --git a/plonky2/src/gates/poseidon.rs b/plonky2/src/gates/poseidon.rs index f6d0657260..eddc059db0 100644 --- a/plonky2/src/gates/poseidon.rs +++ b/plonky2/src/gates/poseidon.rs @@ -112,6 +112,305 @@ impl, const D: usize> Gate for PoseidonGate String { + let mut template_str = format!( + "template Poseidon12() {{ + signal input constants[NUM_OPENINGS_CONSTANTS()][2]; + signal input wires[NUM_OPENINGS_WIRES()][2]; + signal input public_input_hash[4]; + signal input constraints[NUM_GATE_CONSTRAINTS()][2]; + signal output out[NUM_GATE_CONSTRAINTS()][2]; + + signal filter[2]; + $SET_FILTER; + + var index = 0; + out[index] <== ConstraintPush()(constraints[index], filter, GlExtMul()(wires[$WIRE_SWAP], GlExtSub()(wires[$WIRE_SWAP], GlExt(1, 0)()))); + index++; + + for (var i = 0; i < 4; i++) {{ + out[index] <== ConstraintPush()(constraints[index], filter, GlExtSub()(GlExtMul()(wires[$WIRE_SWAP], GlExtSub()(wires[i + 4], wires[i])), wires[$START_DELTA + i])); + index++; + }} + + // SPONGE_RATE = 8 + // SPONGE_CAPACITY = 4 + // SPONGE_WIDTH = 12 + signal state[12][$HALF_N_FULL_ROUNDS * 8 + 2 + $N_PARTIAL_ROUNDS * 2][2]; + var state_round = 0; + for (var i = 0; i < 4; i++) {{ + state[i][state_round] <== GlExtAdd()(wires[i], wires[$START_DELTA + i]); + state[i + 4][state_round] <== GlExtSub()(wires[i + 4], wires[$START_DELTA + i]); + }} + + for (var i = 8; i < 12; i++) {{ + state[i][state_round] <== wires[i]; + }} + state_round++; + + var round_ctr = 0; + // First set of full rounds. + signal mds_row_shf_field[$HALF_N_FULL_ROUNDS][12][13][2]; + for (var r = 0; r < $HALF_N_FULL_ROUNDS; r ++) {{ + for (var i = 0; i < 12; i++) {{ + state[i][state_round] <== GlExtAdd()(state[i][state_round - 1], GlExt(GL_CONST(i + 12 * round_ctr), 0)()); + }} + state_round++; + if (r != 0 ) {{ + for (var i = 0; i < 12; i++) {{ + state[i][state_round] <== wires[$START_DELTA + 4 + 12 * (r - 1) + i]; + out[index] <== ConstraintPush()(constraints[index], filter, GlExtSub()(state[i][state_round - 1], state[i][state_round])); + index++; + }} + state_round++; + }} + for (var i = 0; i < 12; i++) {{ + state[i][state_round] <== GlExtExpN(3)(state[i][state_round - 1], 7); + }} + state_round++; + for (var i = 0; i < 12; i++) {{ // for r + mds_row_shf_field[r][i][0][0] <== 0; + mds_row_shf_field[r][i][0][1] <== 0; + for (var j = 0; j < 12; j++) {{ // for i, + mds_row_shf_field[r][i][j + 1] <== GlExtAdd()(mds_row_shf_field[r][i][j], GlExtMul()(state[(i + j) < 12 ? (i + j) : (i + j - 12)][state_round - 1], GlExt(MDS_MATRIX_CIRC(j), 0)())); + }} + state[i][state_round] <== GlExtAdd()(mds_row_shf_field[r][i][12], GlExtMul()(state[i][state_round - 1], GlExt(MDS_MATRIX_DIAG(i), 0)())); + }} + state_round++; + round_ctr++; + }} + + // Partial rounds. + for (var i = 0; i < 12; i++) {{ + state[i][state_round] <== GlExtAdd()(state[i][state_round - 1], GlExt(FAST_PARTIAL_FIRST_ROUND_CONSTANT(i), 0)()); + }} + state_round++; + component partial_res[11][11]; + state[0][state_round] <== state[0][state_round - 1]; + for (var r = 0; r < 11; r++) {{ + for (var c = 0; c < 11; c++) {{ + partial_res[r][c] = GlExtAdd(); + if (r == 0) {{ + partial_res[r][c].a <== GlExt(0, 0)(); + }} else {{ + partial_res[r][c].a <== partial_res[r - 1][c].out; + }} + partial_res[r][c].b <== GlExtMul()(state[r + 1][state_round - 1], GlExt(FAST_PARTIAL_ROUND_INITIAL_MATRIX(r, c), 0)()); + }} + }} + for (var i = 1; i < 12; i++) {{ + state[i][state_round] <== partial_res[10][i - 1].out; + }} + state_round++; + + signal partial_d[12][$N_PARTIAL_ROUNDS][2]; + for (var r = 0; r < $N_PARTIAL_ROUNDS; r++) {{ + out[index] <== ConstraintPush()(constraints[index], filter, GlExtSub()(state[0][state_round - 1], wires[$START_PARTIAL + r])); + index++; + if (r == $N_PARTIAL_ROUNDS - 1) {{ + state[0][state_round] <== GlExtExpN(3)(wires[$START_PARTIAL + r], 7); + }} else {{ + state[0][state_round] <== GlExtAdd()(GlExt(FAST_PARTIAL_ROUND_CONSTANTS(r), 0)(), GlExtExpN(3)(wires[$START_PARTIAL + r], 7)); + }} + for (var i = 1; i < 12; i++) {{ + state[i][state_round] <== state[i][state_round - 1]; + }} + partial_d[0][r] <== GlExtMul()(state[0][state_round], GlExt(MDS_MATRIX_CIRC(0) + MDS_MATRIX_DIAG(0), 0)()); + for (var i = 1; i < 12; i++) {{ + partial_d[i][r] <== GlExtAdd()(partial_d[i - 1][r], GlExtMul()(state[i][state_round], GlExt(FAST_PARTIAL_ROUND_W_HATS(r, i - 1), 0)())); + }} + state_round++; + state[0][state_round] <== partial_d[11][r]; + for (var i = 1; i < 12; i++) {{ + state[i][state_round] <== GlExtAdd()(state[i][state_round - 1], GlExtMul()(state[0][state_round - 1], GlExt(FAST_PARTIAL_ROUND_VS(r, i - 1), 0)())); + }} + state_round++; + }} + round_ctr += $N_PARTIAL_ROUNDS; + + // Second set of full rounds. + signal mds_row_shf_field2[$HALF_N_FULL_ROUNDS][12][13][2]; + for (var r = 0; r < $HALF_N_FULL_ROUNDS; r ++) {{ + for (var i = 0; i < 12; i++) {{ + state[i][state_round] <== GlExtAdd()(state[i][state_round - 1], GlExt(GL_CONST(i + 12 * round_ctr), 0)()); + }} + state_round++; + for (var i = 0; i < 12; i++) {{ + state[i][state_round] <== wires[$START_FULL_1 + 12 * r + i]; + out[index] <== ConstraintPush()(constraints[index], filter, GlExtSub()(state[i][state_round - 1], state[i][state_round])); + index++; + }} + state_round++; + for (var i = 0; i < 12; i++) {{ + state[i][state_round] <== GlExtExpN(3)(state[i][state_round - 1], 7); + }} + state_round++; + for (var i = 0; i < 12; i++) {{ // for r + mds_row_shf_field2[r][i][0][0] <== 0; + mds_row_shf_field2[r][i][0][1] <== 0; + for (var j = 0; j < 12; j++) {{ // for i, + mds_row_shf_field2[r][i][j + 1] <== GlExtAdd()(mds_row_shf_field2[r][i][j], GlExtMul()(state[(i + j) < 12 ? (i + j) : (i + j - 12)][state_round - 1], GlExt(MDS_MATRIX_CIRC(j), 0)())); + }} + state[i][state_round] <== GlExtAdd()(mds_row_shf_field2[r][i][12], GlExtMul()(state[i][state_round - 1], GlExt(MDS_MATRIX_DIAG(i), 0)())); + }} + state_round++; + round_ctr++; + }} + + for (var i = 0; i < 12; i++) {{ + out[index] <== ConstraintPush()(constraints[index], filter, GlExtSub()(state[i][state_round - 1], wires[12 + i])); + index++; + }} + + for (var i = index + 1; i < NUM_GATE_CONSTRAINTS(); i++) {{ + out[i] <== constraints[i]; + }} +}} +function FAST_PARTIAL_ROUND_W_HATS(i, j) {{ + var value[$N_PARTIAL_ROUNDS][11]; + $SET_FAST_PARTIAL_ROUND_W_HATS; + return value[i][j]; +}} +function FAST_PARTIAL_ROUND_VS(i, j) {{ + var value[$N_PARTIAL_ROUNDS][11]; + $SET_FAST_PARTIAL_ROUND_VS; + return value[i][j]; +}} +function FAST_PARTIAL_ROUND_INITIAL_MATRIX(i, j) {{ + var value[11][11]; + $SET_FAST_PARTIAL_ROUND_INITIAL_MATRIX; + return value[i][j]; +}} +function FAST_PARTIAL_ROUND_CONSTANTS(i) {{ + var value[$N_PARTIAL_ROUNDS]; + $SET_FAST_PARTIAL_ROUND_CONSTANTS; + return value[i]; +}} +function FAST_PARTIAL_FIRST_ROUND_CONSTANT(i) {{ + var value[12]; + $SET_FAST_PARTIAL_FIRST_ROUND_CONSTANT; + return value[i]; +}} +function MDS_MATRIX_CIRC(i) {{ + var mds[12]; + $SET_MDS_MATRIX_CIRC; + return mds[i]; +}} +function MDS_MATRIX_DIAG(i) {{ + var mds[12]; + $SET_MDS_MATRIX_DIAG; + return mds[i]; +}}" + ).to_string(); + template_str = template_str.replace("$WIRE_SWAP", &*Self::WIRE_SWAP.to_string()); + template_str = template_str.replace("$START_DELTA", &*Self::START_DELTA.to_string()); + template_str = template_str.replace("$START_FULL_1", &*Self::START_FULL_1.to_string()); + template_str = template_str.replace( + "$HALF_N_FULL_ROUNDS", + &*poseidon::HALF_N_FULL_ROUNDS.to_string(), + ); + template_str = template_str.replace( + "$N_PARTIAL_ROUNDS", + &*poseidon::N_PARTIAL_ROUNDS.to_string(), + ); + template_str = template_str.replace("$START_PARTIAL", &*Self::START_PARTIAL.to_string()); + + let mut partial_const_str = "".to_owned(); + for i in 0..poseidon::N_PARTIAL_ROUNDS { + partial_const_str += &*(" value[".to_owned() + + &*i.to_string() + + "] = " + + &*::FAST_PARTIAL_ROUND_CONSTANTS[i].to_string() + + ";\n"); + } + template_str = template_str.replace( + " $SET_FAST_PARTIAL_ROUND_CONSTANTS;\n", + &*partial_const_str, + ); + + let mut circ_str = "".to_owned(); + for i in 0..12 { + circ_str += &*(" mds[".to_owned() + + &*i.to_string() + + "] = " + + &*::MDS_MATRIX_CIRC[i].to_string() + + ";\n"); + } + template_str = template_str.replace(" $SET_MDS_MATRIX_CIRC;\n", &*circ_str); + + let mut diag_str = "".to_owned(); + for i in 0..12 { + diag_str += &*(" mds[".to_owned() + + &*i.to_string() + + "] = " + + &*::MDS_MATRIX_DIAG[i].to_string() + + ";\n"); + } + template_str = template_str.replace(" $SET_MDS_MATRIX_DIAG;\n", &*diag_str); + + let mut first_round_const_str = "".to_owned(); + for i in 0..12 { + first_round_const_str += &*(" value[".to_owned() + + &*i.to_string() + + "] = " + + &*::FAST_PARTIAL_FIRST_ROUND_CONSTANT[i].to_string() + + ";\n"); + } + template_str = template_str.replace( + " $SET_FAST_PARTIAL_FIRST_ROUND_CONSTANT;\n", + &*first_round_const_str, + ); + + let mut init_m_str = "".to_owned(); + for i in 0..11 { + for j in 0..11 { + init_m_str += &*(" value[".to_owned() + + &*i.to_string() + + "][" + + &*j.to_string() + + "] = " + + &*::FAST_PARTIAL_ROUND_INITIAL_MATRIX[i][j].to_string() + + ";\n"); + } + } + template_str = + template_str.replace(" $SET_FAST_PARTIAL_ROUND_INITIAL_MATRIX;\n", &*init_m_str); + + let mut partial_hats_str = "".to_owned(); + for i in 0..poseidon::N_PARTIAL_ROUNDS { + for j in 0..11 { + partial_hats_str += &*(" value[".to_owned() + + &*i.to_string() + + "][" + + &*j.to_string() + + "] = " + + &*::FAST_PARTIAL_ROUND_W_HATS[i][j].to_string() + + ";\n"); + } + } + template_str = + template_str.replace(" $SET_FAST_PARTIAL_ROUND_W_HATS;\n", &*partial_hats_str); + + let mut partial_vs_str = "".to_owned(); + for i in 0..poseidon::N_PARTIAL_ROUNDS { + for j in 0..11 { + partial_vs_str += &*(" value[".to_owned() + + &*i.to_string() + + "][" + + &*j.to_string() + + "] = " + + &*::FAST_PARTIAL_ROUND_VS[i][j].to_string() + + ";\n"); + } + } + template_str = template_str.replace(" $SET_FAST_PARTIAL_ROUND_VS;\n", &*partial_vs_str); + + template_str + } + fn export_solidity_verification_code(&self) -> String { + todo!() + } + fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { let mut constraints = Vec::with_capacity(self.num_constraints()); diff --git a/plonky2/src/gates/poseidon_mds.rs b/plonky2/src/gates/poseidon_mds.rs index 8e2f4a7664..b47cbd62f0 100644 --- a/plonky2/src/gates/poseidon_mds.rs +++ b/plonky2/src/gates/poseidon_mds.rs @@ -131,6 +131,14 @@ impl + Poseidon, const D: usize> Gate for Pos Ok(PoseidonMdsGate::new()) } + fn export_circom_verification_code(&self) -> String { + unimplemented!() + } + + fn export_solidity_verification_code(&self) -> String { + unimplemented!() + } + fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { let inputs: [_; SPONGE_WIDTH] = (0..SPONGE_WIDTH) .map(|i| vars.get_local_ext_algebra(Self::wires_input(i))) diff --git a/plonky2/src/gates/public_input.rs b/plonky2/src/gates/public_input.rs index f770e2e6e1..7bca0a779d 100644 --- a/plonky2/src/gates/public_input.rs +++ b/plonky2/src/gates/public_input.rs @@ -44,6 +44,46 @@ impl, const D: usize> Gate for PublicInputGat Ok(Self) } + fn export_circom_verification_code(&self) -> String { + format!( + "template PublicInputGateLib() {{ + signal input constants[NUM_OPENINGS_CONSTANTS()][2]; + signal input wires[NUM_OPENINGS_WIRES()][2]; + signal input public_input_hash[4]; + signal input constraints[NUM_GATE_CONSTRAINTS()][2]; + signal output out[NUM_GATE_CONSTRAINTS()][2]; + + signal filter[2]; + $SET_FILTER; + + signal hashes[4][2]; + for (var i = 0; i < 4; i++) {{ + hashes[i][0] <== public_input_hash[i]; + hashes[i][1] <== 0; + out[i] <== ConstraintPush()(constraints[i], filter, GlExtSub()(wires[i], hashes[i])); + }} + for (var i = 4; i < NUM_GATE_CONSTRAINTS(); i++) {{ + out[i] <== constraints[i]; + }} +}}" + ) + } + fn export_solidity_verification_code(&self) -> String { + format!( + "library PublicInputGateLib {{ + using GoldilocksExtLib for uint64[2]; + function set_filter(GatesUtilsLib.EvaluationVars memory ev) internal pure {{ + $SET_FILTER; + }} + function eval(GatesUtilsLib.EvaluationVars memory ev, uint64[2][$NUM_GATE_CONSTRAINTS] memory constraints) internal pure {{ + for (uint32 i = 0; i < 4; i++) {{ + GatesUtilsLib.push(constraints, ev.filter, i, ev.wires[i].sub(ev.public_input_hash[i])); + }} + }} +}}" + ) + } + fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { Self::wires_public_inputs_hash() .zip(vars.public_inputs_hash.elements) diff --git a/plonky2/src/gates/random_access.rs b/plonky2/src/gates/random_access.rs index 9110a59b67..50489fa21e 100644 --- a/plonky2/src/gates/random_access.rs +++ b/plonky2/src/gates/random_access.rs @@ -136,6 +136,131 @@ impl, const D: usize> Gate for RandomAccessGa Ok(Self::new(num_copies, bits, num_extra_constants)) } + fn export_circom_verification_code(&self) -> String { + let mut template_str = format!( + "template RandomAccessB$BITSC$NUM_COPIESE$NUM_EXTRA_CONSTANTS() {{ + signal input constants[NUM_OPENINGS_CONSTANTS()][2]; + signal input wires[NUM_OPENINGS_WIRES()][2]; + signal input public_input_hash[4]; + signal input constraints[NUM_GATE_CONSTRAINTS()][2]; + signal output out[NUM_GATE_CONSTRAINTS()][2]; + + signal filter[2]; + $SET_FILTER; + + var index = 0; + signal acc[$NUM_COPIES][$BITS][2]; + signal list_items[$NUM_COPIES][$BITS + 1][$VEC_SIZE][2]; + for (var copy = 0; copy < $NUM_COPIES; copy++) {{ + for (var i = 0; i < $BITS; i++) {{ + out[index] <== ConstraintPush()(constraints[index], filter, + GlExtMul()(wires[ra_wire_bit(i, copy)], GlExtSub()(wires[ra_wire_bit(i, copy)], GlExt(1, 0)()))); + index++; + }} + for (var i = $BITS; i > 0; i--) {{ + if(i == $BITS) {{ + acc[copy][i - 1] <== wires[ra_wire_bit(i - 1, copy)]; + }} else {{ + acc[copy][i - 1] <== GlExtAdd()(GlExtAdd()(acc[copy][i], acc[copy][i]), wires[ra_wire_bit(i - 1, copy)]); + }} + }} + out[index] <== ConstraintPush()(constraints[index], filter, GlExtSub()(acc[copy][0], wires[(2 + $VEC_SIZE) * copy])); + index++; + for (var i = 0; i < $VEC_SIZE; i++) {{ + list_items[copy][0][i] <== wires[(2 + $VEC_SIZE) * copy + 2 + i]; + }} + for (var i = 0; i < $BITS; i++) {{ + for (var j = 0; j < ($VEC_SIZE >> i); j = j + 2) {{ + list_items[copy][i + 1][j \\ 2] <== GlExtAdd()(list_items[copy][i][j], GlExtMul()(wires[ra_wire_bit(i, copy)], GlExtSub()(list_items[copy][i][j + 1], list_items[copy][i][j]))); + }} + }} + out[index] <== ConstraintPush()(constraints[index], filter, GlExtSub()(list_items[copy][$BITS][0], wires[(2 + $VEC_SIZE) * copy + 1])); + index++; + }} + for (var i = 0; i < $NUM_EXTRA_CONSTANTS; i++) {{ + out[index] <== ConstraintPush()(constraints[index], filter, GlExtSub()(constants[$NUM_SELECTORS + i], wires[(2 + $VEC_SIZE) * $NUM_COPIES + i])); + index++; + }} + + for (var i = index; i < NUM_GATE_CONSTRAINTS(); i++) {{ + out[i] <== constraints[i]; + }} +}} +function ra_wire_bit(i, copy) {{ + return $NUM_ROUTED_WIRES + copy * $BITS + i; +}}" + ).to_string(); + template_str = template_str.replace("$BITS", &*self.bits.to_string()); + template_str = template_str.replace("$VEC_SIZE", &*self.vec_size().to_string()); + template_str = + template_str.replace("$NUM_ROUTED_WIRES", &*self.num_routed_wires().to_string()); + template_str = template_str.replace("$NUM_COPIES", &*self.num_copies.to_string()); + template_str = template_str.replace( + "$NUM_EXTRA_CONSTANTS", + &*self.num_extra_constants.to_string(), + ); + template_str + } + fn export_solidity_verification_code(&self) -> String { + let mut template_str = format!( + "library RandomAccessB$BITSC$NUM_COPIESE$NUM_EXTRA_CONSTANTSLib {{ + using GoldilocksExtLib for uint64[2]; + function set_filter(GatesUtilsLib.EvaluationVars memory ev) internal pure {{ + $SET_FILTER; + }} + function wire_access_index(uint32 copy) internal pure returns (uint32) {{ + return (2 + $VEC_SIZE) * copy; + }} + function wire_list_item(uint32 i, uint32 copy) internal pure returns (uint32) {{ + return (2 + $VEC_SIZE) * copy + 2 + i; + }} + function wire_claimed_element(uint32 copy) internal pure returns (uint32) {{ + return (2 + $VEC_SIZE) * copy + 1; + }} + function wire_bit(uint32 i, uint32 copy) internal pure returns (uint32) {{ + return $NUM_ROUTED_WIRES + copy * $BITS + i; + }} + function eval(GatesUtilsLib.EvaluationVars memory ev, uint64[2][$NUM_GATE_CONSTRAINTS] memory constraints) internal pure {{ + uint32 index = 0; + for (uint32 copy = 0; copy < $NUM_COPIES; copy++) {{ + for (uint32 i = 0; i < $BITS; i++) {{ + GatesUtilsLib.push(constraints, ev.filter, index++, ev.wires[wire_bit(i, copy)].mul(ev.wires[wire_bit(i, copy)].sub(GoldilocksExtLib.one()))); + }} + uint64[2] memory acc; + for (uint32 i = $BITS; i > 0; i--) {{ + acc = acc.add(acc).add(ev.wires[wire_bit(i - 1, copy)]); + }} + GatesUtilsLib.push(constraints, ev.filter, index++, acc.sub(ev.wires[wire_access_index(copy)])); + uint64[2][$VEC_SIZE] memory list_items; + for (uint32 i = 0; i < $VEC_SIZE; i++) {{ + list_items[i] = ev.wires[wire_list_item(i, copy)]; + }} + for (uint32 i = 0; i < $BITS; i++) {{ + for (uint32 j = 0; j < ($VEC_SIZE >> i); j = j + 2) {{ + list_items[j / 2] = list_items[j].add(ev.wires[wire_bit(i, copy)].mul(list_items[j + 1].sub(list_items[j]))); + }} + }} + GatesUtilsLib.push(constraints, ev.filter, index++, list_items[0].sub(ev.wires[wire_claimed_element(copy)])); + }} + for (uint32 i = 0; i < $NUM_EXTRA_CONSTANTS; i++) {{ + GatesUtilsLib.push(constraints, ev.filter, index++, ev.constants[$NUM_SELECTORS + i].sub(ev.wires[(2 + $VEC_SIZE) * $NUM_COPIES + i])); + }} + }} +}}" + ) + .to_string(); + template_str = template_str.replace("$BITS", &*self.bits.to_string()); + template_str = template_str.replace("$VEC_SIZE", &*self.vec_size().to_string()); + template_str = + template_str.replace("$NUM_ROUTED_WIRES", &*self.num_routed_wires().to_string()); + template_str = template_str.replace("$NUM_COPIES", &*self.num_copies.to_string()); + template_str = template_str.replace( + "$NUM_EXTRA_CONSTANTS", + &*self.num_extra_constants.to_string(), + ); + template_str + } + fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { let mut constraints = Vec::with_capacity(self.num_constraints()); diff --git a/plonky2/src/gates/reducing.rs b/plonky2/src/gates/reducing.rs index b313efe695..6a33b57a06 100644 --- a/plonky2/src/gates/reducing.rs +++ b/plonky2/src/gates/reducing.rs @@ -98,6 +98,81 @@ impl, const D: usize> Gate for ReducingGate String { + let mut template_str = format!( + "template Reducing$NUM_COEFFS() {{ + signal input constants[NUM_OPENINGS_CONSTANTS()][2]; + signal input wires[NUM_OPENINGS_WIRES()][2]; + signal input public_input_hash[4]; + signal input constraints[NUM_GATE_CONSTRAINTS()][2]; + signal output out[NUM_GATE_CONSTRAINTS()][2]; + + signal filter[2]; + $SET_FILTER; + + var acc_start = 2 * $D; + signal m[$NUM_COEFFS][2][2]; + for (var i = 0; i < $NUM_COEFFS; i++) {{ + m[i] <== WiresAlgebraMul(acc_start, $D)(wires); + out[i * $D] <== ConstraintPush()(constraints[i * $D], filter, GlExtAdd()(m[i][0], GlExtSub()(wires[3 * $D + i], wires[r_wires_accs_start(i, $NUM_COEFFS)]))); + for (var j = 1; j < $D; j++) {{ + out[i * $D + j] <== ConstraintPush()(constraints[i * $D + j], filter, GlExtSub()(m[i][j], wires[r_wires_accs_start(i, $NUM_COEFFS) + j])); + }} + acc_start = r_wires_accs_start(i, $NUM_COEFFS); + }} + + for (var i = $NUM_COEFFS * $D; i < NUM_GATE_CONSTRAINTS(); i++) {{ + out[i] <== constraints[i]; + }} +}} +function r_wires_accs_start(i, num_coeffs) {{ + if (i == num_coeffs - 1) return 0; + else return (3 + i) * $D + num_coeffs; +}}" + ).to_string(); + + template_str = template_str.replace("$NUM_COEFFS", &*self.num_coeffs.to_string()); + template_str = template_str.replace("$D", &*D.to_string()); + + template_str + } + fn export_solidity_verification_code(&self) -> String { + let mut template_str = format!( + "library Reducing$NUM_COEFFSLib {{ + using GoldilocksFieldLib for uint64; + using GoldilocksExtLib for uint64[2]; + + function set_filter(GatesUtilsLib.EvaluationVars memory ev) internal pure {{ + $SET_FILTER; + }} + + function wires_accs_start(uint32 i) internal pure returns(uint32) {{ + if (i == $NUM_COEFFS - 1) return 0; + return (3 + i) * $D + $NUM_COEFFS; + }} + + function eval(GatesUtilsLib.EvaluationVars memory ev, uint64[2][$NUM_GATE_CONSTRAINTS] memory constraints) internal pure {{ + uint32 acc_start = 2 * $D; + for (uint32 i = 0; i < $NUM_COEFFS; i++) {{ + uint64[2][$D] memory m = GatesUtilsLib.wires_algebra_mul(ev.wires, acc_start, $D); + GatesUtilsLib.push(constraints, ev.filter, i * $D, m[0].add(ev.wires[3 * $D + i].sub(ev.wires[wires_accs_start(i)]))); + for (uint32 j = 1; j < $D; j++) {{ + GatesUtilsLib.push(constraints, ev.filter, i * $D + j, m[j].sub(ev.wires[wires_accs_start(i) + j])); + }} + acc_start = wires_accs_start(i); + }} + }} +}}" + ) + .to_string(); + + template_str = template_str.replace("$NUM_COEFFS", &*self.num_coeffs.to_string()); + + template_str + } + + fn eval_unfiltered_base_one( &self, vars: EvaluationVarsBase, diff --git a/plonky2/src/gates/reducing_extension.rs b/plonky2/src/gates/reducing_extension.rs index 5492c50611..1cb338db21 100644 --- a/plonky2/src/gates/reducing_extension.rs +++ b/plonky2/src/gates/reducing_extension.rs @@ -77,6 +77,78 @@ impl, const D: usize> Gate for ReducingExtens Ok(Self::new(num_coeffs)) } + + fn export_circom_verification_code(&self) -> String { + let mut template_str = format!( + "template ReducingExtension$NUM_COEFFS() {{ + signal input constants[NUM_OPENINGS_CONSTANTS()][2]; + signal input wires[NUM_OPENINGS_WIRES()][2]; + signal input public_input_hash[4]; + signal input constraints[NUM_GATE_CONSTRAINTS()][2]; + signal output out[NUM_GATE_CONSTRAINTS()][2]; + + signal filter[2]; + $SET_FILTER; + + var acc_start = 2 * $D; + signal m[$NUM_COEFFS][2][2]; + for (var i = 0; i < $NUM_COEFFS; i++) {{ + m[i] <== WiresAlgebraMul(acc_start, $D)(wires); + for (var j = 0; j < $D; j++) {{ + out[i * $D + j] <== ConstraintPush()(constraints[i * $D + j], filter, GlExtAdd()(m[i][j], GlExtSub()(wires[(3 + i) * $D + j], wires[re_wires_accs_start(i, $NUM_COEFFS) + j]))); + }} + acc_start = re_wires_accs_start(i, $NUM_COEFFS); + }} + + for (var i = $NUM_COEFFS * $D; i < NUM_GATE_CONSTRAINTS(); i++) {{ + out[i] <== constraints[i]; + }} +}} +function re_wires_accs_start(i, num_coeffs) {{ + if (i == num_coeffs - 1) return 0; + else return (3 + i + num_coeffs) * $D; +}}" + ).to_string(); + + template_str = template_str.replace("$NUM_COEFFS", &*self.num_coeffs.to_string()); + template_str = template_str.replace("$D", &*D.to_string()); + + template_str + } + fn export_solidity_verification_code(&self) -> String { + let mut template_str = format!( + "library ReducingExtension$NUM_COEFFSLib {{ + using GoldilocksFieldLib for uint64; + using GoldilocksExtLib for uint64[2]; + + function set_filter(GatesUtilsLib.EvaluationVars memory ev) internal pure {{ + $SET_FILTER; + }} + + function wires_accs_start(uint32 i) internal pure returns(uint32) {{ + if (i == $NUM_COEFFS - 1) return 0; + return (3 + i + $NUM_COEFFS) * $D; + }} + + function eval(GatesUtilsLib.EvaluationVars memory ev, uint64[2][$NUM_GATE_CONSTRAINTS] memory constraints) internal pure {{ + uint32 acc_start = 2 * $D; + for (uint32 i = 0; i < $NUM_COEFFS; i++) {{ + uint64[2][$D] memory m = GatesUtilsLib.wires_algebra_mul(ev.wires, acc_start, $D); + for (uint32 j = 0; j < $D; j++) {{ + GatesUtilsLib.push(constraints, ev.filter, i * $D + j, m[j].add(ev.wires[(3 + i) * $D + j].sub(ev.wires[wires_accs_start(i) + j]))); + }} + acc_start = wires_accs_start(i); + }} + }} +}}" + ) + .to_string(); + + template_str = template_str.replace("$NUM_COEFFS", &*self.num_coeffs.to_string()); + + template_str + } + fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { let alpha = vars.get_local_ext_algebra(Self::wires_alpha()); let old_acc = vars.get_local_ext_algebra(Self::wires_old_acc()); diff --git a/plonky2/src/gates/selectors.rs b/plonky2/src/gates/selectors.rs index 1018ba755b..17b3b0e5a9 100644 --- a/plonky2/src/gates/selectors.rs +++ b/plonky2/src/gates/selectors.rs @@ -15,8 +15,8 @@ pub(crate) const UNUSED_SELECTOR: usize = u32::MAX as usize; #[derive(Debug, Clone, Eq, PartialEq, Serialize)] pub struct SelectorsInfo { - pub(crate) selector_indices: Vec, - pub(crate) groups: Vec>, + pub selector_indices: Vec, + pub groups: Vec>, } impl SelectorsInfo { diff --git a/plonky2/src/hash/mod.rs b/plonky2/src/hash/mod.rs index b829392063..6399d3c153 100644 --- a/plonky2/src/hash/mod.rs +++ b/plonky2/src/hash/mod.rs @@ -7,3 +7,4 @@ pub mod merkle_tree; pub mod path_compression; pub mod poseidon; pub mod poseidon_goldilocks; +pub mod poseidon_bn128; diff --git a/plonky2/src/hash/poseidon_bn128.rs b/plonky2/src/hash/poseidon_bn128.rs new file mode 100644 index 0000000000..3f56db151d --- /dev/null +++ b/plonky2/src/hash/poseidon_bn128.rs @@ -0,0 +1,269 @@ + +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); + +use crate::field::extension::quadratic::QuadraticExtension; +use crate::field::extension::Extendable; +use crate::field::goldilocks_field::GoldilocksField; + +use crate::hash::hash_types::{HashOut, RichField}; +use crate::hash::hashing::{compress, hash_n_to_hash_no_pad, PlonkyPermutation}; +use crate::hash::poseidon::{PoseidonHash, SPONGE_RATE, SPONGE_WIDTH}; +use crate::iop::target::{BoolTarget, Target}; +use crate::plonk::circuit_builder::CircuitBuilder; +use crate::plonk::config::{AlgebraicHasher, GenericConfig, Hasher}; + +use super::poseidon::PoseidonPermutation; + +#[derive(Copy, Clone, Default, Debug, PartialEq)] +pub struct PoseidonBN128Permutation { + state: [F; SPONGE_WIDTH], +} + +impl Eq for PoseidonBN128Permutation {} + +impl AsRef<[F]> for PoseidonBN128Permutation { + fn as_ref(&self) -> &[F] { + &self.state + } +} + + +impl PlonkyPermutation for PoseidonBN128Permutation { + const RATE: usize = SPONGE_RATE; + const WIDTH: usize = SPONGE_WIDTH; + + fn new>(elts: I) -> Self { + let mut perm = Self { + state: [F::default(); SPONGE_WIDTH], + }; + perm.set_from_iter(elts, 0); + perm + } + + fn set_elt(&mut self, elt: F, idx: usize) { + self.state[idx] = elt; + } + + fn set_from_slice(&mut self, elts: &[F], start_idx: usize) { + let begin = start_idx; + let end = start_idx + elts.len(); + self.state[begin..end].copy_from_slice(elts); + } + + fn set_from_iter>(&mut self, elts: I, start_idx: usize) { + for (s, e) in self.state[start_idx..].iter_mut().zip(elts) { + *s = e; + } + } + + fn permute(&mut self) { + assert_eq!(SPONGE_WIDTH, 12); + unsafe { + let h = permute( + self.state[0].to_canonical_u64(), + self.state[1].to_canonical_u64(), + self.state[2].to_canonical_u64(), + self.state[3].to_canonical_u64(), + self.state[4].to_canonical_u64(), + self.state[5].to_canonical_u64(), + self.state[6].to_canonical_u64(), + self.state[7].to_canonical_u64(), + self.state[8].to_canonical_u64(), + self.state[9].to_canonical_u64(), + self.state[10].to_canonical_u64(), + self.state[11].to_canonical_u64(), + ); + + let permute_output = [ + F::from_canonical_u64(if h.r0 >= F::ORDER { + h.r0 - F::ORDER + } else { + h.r0 + }), + F::from_canonical_u64(if h.r1 >= F::ORDER { + h.r1 - F::ORDER + } else { + h.r1 + }), + F::from_canonical_u64(if h.r2 >= F::ORDER { + h.r2 - F::ORDER + } else { + h.r2 + }), + F::from_canonical_u64(if h.r3 >= F::ORDER { + h.r3 - F::ORDER + } else { + h.r3 + }), + F::from_canonical_u64(if h.r4 >= F::ORDER { + h.r4 - F::ORDER + } else { + h.r4 + }), + F::from_canonical_u64(if h.r5 >= F::ORDER { + h.r5 - F::ORDER + } else { + h.r5 + }), + F::from_canonical_u64(if h.r6 >= F::ORDER { + h.r6 - F::ORDER + } else { + h.r6 + }), + F::from_canonical_u64(if h.r7 >= F::ORDER { + h.r7 - F::ORDER + } else { + h.r7 + }), + F::from_canonical_u64(if h.r8 >= F::ORDER { + h.r8 - F::ORDER + } else { + h.r8 + }), + F::from_canonical_u64(if h.r9 >= F::ORDER { + h.r9 - F::ORDER + } else { + h.r9 + }), + F::from_canonical_u64(if h.r10 >= F::ORDER { + h.r10 - F::ORDER + } else { + h.r10 + }), + F::from_canonical_u64(if h.r11 >= F::ORDER { + h.r11 - F::ORDER + } else { + h.r11 + }), + ]; + self.set_from_slice(&permute_output, 0) + } + } + + + fn squeeze(&self) -> &[F] { + &self.state[..Self::RATE] + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct PoseidonBN128Hash; +impl Hasher for PoseidonBN128Hash { + const HASH_SIZE: usize = 4 * 8; + type Hash = HashOut; + type Permutation = PoseidonBN128Permutation; + + fn hash_no_pad(input: &[F]) -> Self::Hash { + hash_n_to_hash_no_pad::(input) + } + + // fn hash_public_inputs(input: &[F]) -> Self::Hash { + // PoseidonHash::hash_no_pad(input) + // } + + fn two_to_one(left: Self::Hash, right: Self::Hash) -> Self::Hash { + compress::(left, right) + } +} + +// impl AlgebraicHasher for PoseidonBN128Hash { +// type AlgebraicPermutation = PoseidonBN128Permutation; + +// fn permute_swapped( +// inputs: Self::AlgebraicPermutation, +// swap: BoolTarget, +// builder: &mut CircuitBuilder, +// ) -> Self::AlgebraicPermutation +// where +// F: RichField + Extendable, +// { +// let gate_type = PoseidonGate::::new(); +// let gate = builder.add_gate(gate_type, vec![]); + +// let swap_wire = PoseidonGate::::WIRE_SWAP; +// let swap_wire = Target::wire(gate, swap_wire); +// builder.connect(swap.target, swap_wire); + +// // Route input wires. +// let inputs = inputs.as_ref(); +// for i in 0..SPONGE_WIDTH { +// let in_wire = PoseidonGate::::wire_input(i); +// let in_wire = Target::wire(gate, in_wire); +// builder.connect(inputs[i], in_wire); +// } + +// // Collect output wires. +// Self::AlgebraicPermutation::new( +// (0..SPONGE_WIDTH).map(|i| Target::wire(gate, PoseidonGate::::wire_output(i))), +// ) +// } +// } + + +// TODO: this is a work around. Still use Goldilocks based Poseidon for algebraic PoseidonBN128Hash. +impl AlgebraicHasher for PoseidonBN128Hash { + type AlgebraicPermutation = PoseidonPermutation; + + fn permute_swapped( + inputs: Self::AlgebraicPermutation, + swap: BoolTarget, + builder: &mut CircuitBuilder, + ) -> Self::AlgebraicPermutation + where + F: RichField + Extendable, + { + PoseidonHash::permute_swapped(inputs, swap, builder) + } + // fn public_inputs_hash( + // inputs: Vec, + // builder: &mut CircuitBuilder, + // ) -> HashOutTarget + // where + // F: RichField + Extendable, + // { + // PoseidonHash::public_inputs_hash(inputs, builder) + // } +} + +/// Configuration using Poseidon over the Goldilocks field. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct PoseidonBN128GoldilocksConfig; + +impl GenericConfig<2> for PoseidonBN128GoldilocksConfig { + type F = GoldilocksField; + type FE = QuadraticExtension; + type Hasher = PoseidonBN128Hash; + type InnerHasher = PoseidonBN128Hash; +} + +#[cfg(test)] +mod tests { + use anyhow::Result; + use plonky2::field::types::Field; + use plonky2::plonk::config::{GenericConfig, Hasher, PoseidonGoldilocksConfig}; + + use crate::config::PoseidonBN128Hash; + + #[test] + fn test_poseidon_bn128() -> Result<()> { + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + + let mut v = Vec::new(); + v.push(F::from_canonical_u64(8917524657281059100u64)); + v.push(F::from_canonical_u64(13029010200779371910u64)); + v.push(F::from_canonical_u64(16138660518493481604u64)); + v.push(F::from_canonical_u64(17277322750214136960u64)); + v.push(F::from_canonical_u64(1441151880423231822u64)); + let h = PoseidonBN128Hash::hash_no_pad(&v); + assert_eq!(h.elements[0].0, 16736853722845225729u64); + assert_eq!(h.elements[1].0, 1446699130810517790u64); + assert_eq!(h.elements[2].0, 15445626857806971868u64); + assert_eq!(h.elements[3].0, 6331160477881736675u64); + + Ok(()) + } +} diff --git a/plonky2/src/util/timing.rs b/plonky2/src/util/timing.rs index b1a2832943..2e1d5bec01 100644 --- a/plonky2/src/util/timing.rs +++ b/plonky2/src/util/timing.rs @@ -1,7 +1,7 @@ #[cfg(feature = "timing")] use std::time::{Duration, Instant}; -use log::{log,debug, Level}; +use log::{ Level}; /// The hierarchy of scopes, and the time consumed by each one. Useful for profiling. #[cfg(feature = "timing")]