mirror of
https://github.com/logos-storage/zk-benchmarks.git
synced 2026-01-04 06:43:09 +00:00
Merge ac7eeb75cdac0cfa5838d7729dda0e1ec9b2f9f6 into 7ba808630f2e1fa3231503b82429b6f88290b07b
This commit is contained in:
commit
4a94f1d169
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,3 +2,4 @@
|
||||
build
|
||||
target
|
||||
a.out
|
||||
Cargo.lock
|
||||
6
.gitmodules
vendored
6
.gitmodules
vendored
@ -13,3 +13,9 @@
|
||||
[submodule "hash/snark/src/hash-circuits"]
|
||||
path = hash/snark/external/hash-circuits
|
||||
url = https://github.com/faulhornlabs/hash-circuits
|
||||
[submodule "hash/risc0/external/risc0"]
|
||||
path = hash/risc0/external/risc0
|
||||
url = https://github.com/risc0/risc0.git
|
||||
[submodule "hash/plonky2/external/zk_evm"]
|
||||
path = hash/plonky2/external/zk_evm
|
||||
url = https://github.com/hashcloak/zk_evm.git
|
||||
|
||||
2
hash/cpu/external/Blake3
vendored
2
hash/cpu/external/Blake3
vendored
@ -1 +1 @@
|
||||
Subproject commit dd30dcb00221591db3a983e0215b81d86cff941d
|
||||
Subproject commit 4d32708f511fd85c6b0fb131295cc73224246738
|
||||
2
hash/cpu/external/constantine
vendored
2
hash/cpu/external/constantine
vendored
@ -1 +1 @@
|
||||
Subproject commit 4dd0a02f1afd338f5207e40a47cac6705196b490
|
||||
Subproject commit dbd2630daa6d599a9e78ad247e6858baf41664da
|
||||
2
hash/cpu/external/zikkurat-algebra
vendored
2
hash/cpu/external/zikkurat-algebra
vendored
@ -1 +1 @@
|
||||
Subproject commit 96e349786bd004e64a9cf50e0122f89863b24e92
|
||||
Subproject commit 0fb198a9087531f32bb00bd13d4feaf813bc473a
|
||||
21
hash/plonky2/bench/Cargo.toml
Normal file
21
hash/plonky2/bench/Cargo.toml
Normal file
@ -0,0 +1,21 @@
|
||||
[package]
|
||||
name = "plonky2_hash_benchmarks"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
|
||||
# TODO: This can be later changed to original github
|
||||
plonky2_u32 ={ git = "https://github.com/hashcloak/plonky2-u32.git"}
|
||||
plonky2 = "0.2.2"
|
||||
rand = "0.8.5"
|
||||
anyhow = "1.0.86"
|
||||
sha2 = "0.10"
|
||||
tiny-keccak={version="2.0.2", features=["keccak"]}
|
||||
hex="0.4.3"
|
||||
evm_arithmetization ={ path = "../external/zk_evm/evm_arithmetization" }
|
||||
starky = "0.4.0"
|
||||
env_logger = "0.11.3"
|
||||
log = "0.4.21"
|
||||
11
hash/plonky2/bench/README.md
Normal file
11
hash/plonky2/bench/README.md
Normal file
@ -0,0 +1,11 @@
|
||||
Plonky2 Hash Circuit Benchmarking
|
||||
--------------------------------
|
||||
- The `plonky2/bench` contains the following hash circuit benchamrking code: sha256, keccak256 and poseidon.
|
||||
- The code for sha256 is taken from [plonky2-sha256](https://github.com/polymerdao/plonky2-sha256).
|
||||
- The keccak hash has two different implementation taken from [plonky2-keccak256](https://github.com/qope/plonky2-keccak256) and Polygon-zero [zk_evm](https://github.com/0xPolygonZero/zk_evm/tree/develop).
|
||||
- The `build.sh` script builds the whole code. You need rust nightly version to build the code. The script overrides the default rust version to nightly.
|
||||
- `run.sh` and `run_tree.sh` runs the benchmark. (`run.sh` for sha256, keccak, and keccak-polygon and `run_tree.sh` for poseidon)
|
||||
- Benchmarks can be parameterized using environment variables. By convention, we start the names of these environment variables with the `ZKBENCH_` prefix.
|
||||
- By default the `run.sh` will run the sha256 benchmark over 256 Bytes of data. other hashes can be run by settig the environment variables accordingly.
|
||||
- By default the `run_tree.sh` will run the poseidon benchmark over tree depth 4.
|
||||
- Additional files `bench.cfg` and `bench_tree.cfg` specifies the configurations and parameters.
|
||||
11
hash/plonky2/bench/bench.cfg
Normal file
11
hash/plonky2/bench/bench.cfg
Normal file
@ -0,0 +1,11 @@
|
||||
name: "Plonky2 hashes circuit benchmarking"
|
||||
author:
|
||||
timeout: 200
|
||||
params:
|
||||
[ HASH_TYPE: [ "sha256", "keccak", "keccak-polygon"]
|
||||
, INPUT_SIZE_BYTES: [ 256, 512, 1024, 2048 ]
|
||||
]
|
||||
tags: plonky2, $HASH_TYPE
|
||||
|
||||
comments:
|
||||
The benchmarks includes for sha256, keccak and poseidon hash.
|
||||
10
hash/plonky2/bench/bench_tree.cfg
Normal file
10
hash/plonky2/bench/bench_tree.cfg
Normal file
@ -0,0 +1,10 @@
|
||||
name: "Plonky2 hashes circuit benchmarking"
|
||||
author:
|
||||
timeout: 100
|
||||
params:
|
||||
[ HASH_TYPE_TREE: [ "poseidon"]
|
||||
, TREE_DEPTH: [ 2, 4, 8, 16 ]
|
||||
]
|
||||
tags: plonky2, $HASH_TYPE_TREE
|
||||
comments:
|
||||
The benchmarks includes for poseidon hash.
|
||||
9
hash/plonky2/bench/build.sh
Executable file
9
hash/plonky2/bench/build.sh
Executable file
@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Set nightly as the default toolchain
|
||||
rustup override set nightly
|
||||
|
||||
# Build
|
||||
RUSTFLAGS=-Ctarget-cpu=native cargo build --release --bin plonky2_hash_benchmarks
|
||||
|
||||
|
||||
15
hash/plonky2/bench/run.sh
Executable file
15
hash/plonky2/bench/run.sh
Executable file
@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
if [ -z ${ZKBENCH_HASH_TYPE} ]; then
|
||||
ZKBENCH_HASH_TYPE="sha256"
|
||||
fi
|
||||
|
||||
if [ -z ${ZKBENCH_INPUT_SIZE_BYTES} ]; then
|
||||
ZKBENCH_INPUT_SIZE_BYTES=256
|
||||
fi
|
||||
|
||||
echo "Running benchmarks with the following configurations:"
|
||||
echo "HASH = $ZKBENCH_HASH_TYPE"
|
||||
echo "Input Size (Bytes) = $ZKBENCH_INPUT_SIZE_BYTES"
|
||||
|
||||
# Run the benchmarks
|
||||
./target/release/plonky2_hash_benchmarks $ZKBENCH_HASH_TYPE $ZKBENCH_INPUT_SIZE_BYTES
|
||||
15
hash/plonky2/bench/run_tree.sh
Executable file
15
hash/plonky2/bench/run_tree.sh
Executable file
@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
if [ -z ${ZKBENCH_HASH_TYPE_TREE} ]; then
|
||||
ZKBENCH_HASH_TYPE_TREE="poseidon"
|
||||
fi
|
||||
|
||||
if [ -z ${ZKBENCH_TREE_DEPTH} ]; then
|
||||
ZKBENCH_TREE_DEPTH=4
|
||||
fi
|
||||
|
||||
echo "Running benchmarks with the following configurations:"
|
||||
echo "HASH = $ZKBENCH_HASH_TYPE_TREE"
|
||||
echo "Tree Depth = $ZKBENCH_TREE_DEPTH"
|
||||
|
||||
# Run the benchmarks
|
||||
./target/release/plonky2_hash_benchmarks $ZKBENCH_HASH_TYPE_TREE $ZKBENCH_TREE_DEPTH
|
||||
78
hash/plonky2/bench/src/arithmetic/binary_arithmetic.rs
Normal file
78
hash/plonky2/bench/src/arithmetic/binary_arithmetic.rs
Normal file
@ -0,0 +1,78 @@
|
||||
use plonky2::hash::hash_types::RichField;
|
||||
use plonky2::field::extension::Extendable;
|
||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||
use plonky2::iop::target::BoolTarget;
|
||||
|
||||
pub fn xor_circuit<F, const D: usize>(
|
||||
a: BoolTarget,
|
||||
b: BoolTarget,
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
) -> BoolTarget
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
{
|
||||
|
||||
// xor(a, b) = a*(1-b) + (1-a)*b = a + b - 2*ab
|
||||
let b_minus_2ab = builder.arithmetic(-F::TWO, F::ONE, a.target, b.target, b.target);
|
||||
let a_plus_b_minus_2ab = builder.add(a.target, b_minus_2ab);
|
||||
|
||||
BoolTarget::new_unsafe(a_plus_b_minus_2ab)
|
||||
}
|
||||
|
||||
pub fn xor_const_circuit<F, const D: usize>(
|
||||
a: BoolTarget,
|
||||
b: bool,
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
) -> BoolTarget
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
{
|
||||
// b = 0 => xor(a, b) = a
|
||||
// b = 1 => xor(a, b) = 1 - a = not(a)
|
||||
if b {
|
||||
builder.not(a)
|
||||
} else {
|
||||
a
|
||||
}
|
||||
}
|
||||
|
||||
// reffered to https://github.com/polymerdao/plonky2-sha256
|
||||
/// 0 -> [0, 1, 2, ..., 63]
|
||||
/// 1 -> [63, 0, 1, ..., 62]
|
||||
/// 63 -> [1, 2, ..., 63, 0]
|
||||
pub fn rotate_u64(y: usize) -> Vec<usize> {
|
||||
let mut res = Vec::new();
|
||||
for i in 64 - y..64 {
|
||||
res.push(i);
|
||||
}
|
||||
for i in 0..64 - y {
|
||||
res.push(i);
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn from_bits_to_u64(bools: &[bool]) -> u64 {
|
||||
let mut result: u64 = 0;
|
||||
let mut shift = 0;
|
||||
for &bit in bools {
|
||||
if bit {
|
||||
result |= 1 << shift;
|
||||
}
|
||||
shift += 1;
|
||||
if shift == 64 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub fn u64_to_bits(num: u64) -> Vec<bool> {
|
||||
let mut result = Vec::with_capacity(64);
|
||||
let mut n = num;
|
||||
for _ in 0..64 {
|
||||
result.push(n & 1 == 1);
|
||||
n >>= 1;
|
||||
}
|
||||
result
|
||||
}
|
||||
61
hash/plonky2/bench/src/arithmetic/u32_arithmetic.rs
Normal file
61
hash/plonky2/bench/src/arithmetic/u32_arithmetic.rs
Normal file
@ -0,0 +1,61 @@
|
||||
use plonky2::iop::target::BoolTarget;
|
||||
use plonky2::hash::hash_types::RichField;
|
||||
use plonky2::field::extension::Extendable;
|
||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||
use plonky2_u32::gadgets::arithmetic_u32::U32Target;
|
||||
use plonky2_u32::gadgets::arithmetic_u32::CircuitBuilderU32;
|
||||
|
||||
pub fn add_u32<F: RichField + Extendable<D>, const D: usize>(
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
a: &U32Target,
|
||||
b: &U32Target,
|
||||
) -> U32Target {
|
||||
let (res, _carry) = builder.add_u32(*a, *b);
|
||||
res
|
||||
}
|
||||
|
||||
pub fn u32_to_bits_target<F: RichField + Extendable<D>, const D: usize, const B: usize>(
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
a: &U32Target,
|
||||
) -> Vec<BoolTarget> {
|
||||
let mut res = Vec::new();
|
||||
let bit_targets = builder.split_le_base::<B>(a.0, 32);
|
||||
for i in (0..32).rev() {
|
||||
res.push(BoolTarget::new_unsafe(bit_targets[i]));
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
pub fn bits_to_u32_target<F: RichField + Extendable<D>, const D: usize>(
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
bits_target: Vec<BoolTarget>,
|
||||
) -> U32Target {
|
||||
let bit_len = bits_target.len();
|
||||
assert_eq!(bit_len, 32);
|
||||
U32Target(builder.le_sum(bits_target[0..32].iter().rev()))
|
||||
}
|
||||
|
||||
// x>>y
|
||||
// Assume: 0 at index 32
|
||||
pub fn shift32(y: usize) -> Vec<usize> {
|
||||
let mut res = Vec::new();
|
||||
for _ in 32 - y..32 {
|
||||
res.push(32);
|
||||
}
|
||||
for i in 0..32 - y {
|
||||
res.push(i);
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
// define ROTATE(x, y) (((x)>>(y)) | ((x)<<(32-(y))))
|
||||
pub fn rotate32(y: usize) -> Vec<usize> {
|
||||
let mut res = Vec::new();
|
||||
for i in 32 - y..32 {
|
||||
res.push(i);
|
||||
}
|
||||
for i in 0..32 - y {
|
||||
res.push(i);
|
||||
}
|
||||
res
|
||||
}
|
||||
152
hash/plonky2/bench/src/arithmetic/u64_arithmetic.rs
Normal file
152
hash/plonky2/bench/src/arithmetic/u64_arithmetic.rs
Normal file
@ -0,0 +1,152 @@
|
||||
use plonky2::hash::hash_types::RichField;
|
||||
use plonky2::field::extension::Extendable;
|
||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||
use std::marker::PhantomData;
|
||||
use plonky2::iop::target::BoolTarget;
|
||||
use plonky2::iop::witness::PartialWitness;
|
||||
use plonky2::iop::witness::WitnessWrite;
|
||||
|
||||
use crate::arithmetic::binary_arithmetic::{rotate_u64,xor_const_circuit,u64_to_bits,xor_circuit};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct U64Target<F, const D: usize> {
|
||||
pub bits: Vec<BoolTarget>,
|
||||
_phantom: PhantomData<F>,
|
||||
}
|
||||
|
||||
impl<F, const D: usize> U64Target<F, D>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
{
|
||||
pub fn new(builder: &mut CircuitBuilder<F, D>) -> Self {
|
||||
let mut result = vec![];
|
||||
for _ in 0..64 {
|
||||
result.push(builder.add_virtual_bool_target_safe());
|
||||
}
|
||||
Self {
|
||||
bits: result,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from(bits: Vec<BoolTarget>) -> Self {
|
||||
assert_eq!(bits.len(), 64);
|
||||
Self {
|
||||
bits,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_witness(&self, bits: Vec<bool>, pw: &mut PartialWitness<F>) {
|
||||
for i in 0..64 {
|
||||
pw.set_bool_target(self.bits[i], bits[i]);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn constant(x: u64, builder: &mut CircuitBuilder<F, D>) -> Self {
|
||||
let mut result = vec![];
|
||||
let x_bits = u64_to_bits(x);
|
||||
for i in 0..64 {
|
||||
result.push(builder.constant_bool(x_bits[i]));
|
||||
}
|
||||
Self {
|
||||
bits: result,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn connect(&self, other: &Self, builder: &mut CircuitBuilder<F, D>) {
|
||||
for i in 0..64 {
|
||||
builder.connect(self.bits[i].target, other.bits[i].target);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_bits(&self, builder: &mut CircuitBuilder<F, D>) -> Vec<BoolTarget> {
|
||||
let output = Self::new(builder);
|
||||
self.connect(&output, builder);
|
||||
output.bits
|
||||
}
|
||||
|
||||
pub fn xor(&self, other: &Self, builder: &mut CircuitBuilder<F, D>) -> Self {
|
||||
let mut result = vec![];
|
||||
for i in 0..64 {
|
||||
let xor_target = xor_circuit(self.bits[i], other.bits[i], builder);
|
||||
result.push(xor_target);
|
||||
}
|
||||
Self {
|
||||
bits: result,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn xor_const(&self, other: u64, builder: &mut CircuitBuilder<F, D>) -> Self {
|
||||
let other_bits = u64_to_bits(other);
|
||||
let mut result = vec![];
|
||||
for i in 0..64 {
|
||||
let xor_target = xor_const_circuit(self.bits[i], other_bits[i], builder);
|
||||
result.push(xor_target);
|
||||
}
|
||||
Self {
|
||||
bits: result,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/* Rotate left by n
|
||||
* Note that the input parameter n is constant. It is not necessary to make n a constant target or public input,
|
||||
* because different n generates a different circuit.
|
||||
*/
|
||||
pub fn rotl(&self, n: usize) -> Self {
|
||||
let rotate = rotate_u64(n);
|
||||
let mut output = vec![];
|
||||
for i in 0..64 {
|
||||
output.push(self.bits[rotate[i]]);
|
||||
}
|
||||
|
||||
Self {
|
||||
bits: output,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn and(&self, other: &Self, builder: &mut CircuitBuilder<F, D>) -> Self {
|
||||
let mut result = vec![];
|
||||
for i in 0..64 {
|
||||
result.push(builder.and(self.bits[i], other.bits[i]));
|
||||
}
|
||||
Self {
|
||||
bits: result,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn not(&self, builder: &mut CircuitBuilder<F, D>) -> Self {
|
||||
let mut result = vec![];
|
||||
for i in 0..64 {
|
||||
result.push(builder.not(self.bits[i]));
|
||||
}
|
||||
Self {
|
||||
bits: result,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate `self & !other`.
|
||||
pub fn and_not(&self, other: &Self, builder: &mut CircuitBuilder<F, D>) -> Self {
|
||||
let mut result = vec![];
|
||||
for i in 0..64 {
|
||||
// x(1 - y) = x - xy
|
||||
result.push(BoolTarget::new_unsafe(builder.arithmetic(
|
||||
F::NEG_ONE,
|
||||
F::ONE,
|
||||
self.bits[i].target,
|
||||
other.bits[i].target,
|
||||
self.bits[i].target,
|
||||
)));
|
||||
}
|
||||
Self {
|
||||
bits: result,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
312
hash/plonky2/bench/src/bench/keccak256/keccak.rs
Normal file
312
hash/plonky2/bench/src/bench/keccak256/keccak.rs
Normal file
@ -0,0 +1,312 @@
|
||||
// code taken from https://github.com/qope/plonky2-keccak256/tree/main
|
||||
|
||||
use std::marker::PhantomData;
|
||||
use plonky2::{
|
||||
field::extension::Extendable,
|
||||
hash::hash_types::RichField,
|
||||
iop::{
|
||||
target::BoolTarget,
|
||||
witness::{PartialWitness, WitnessWrite},
|
||||
},
|
||||
plonk::{
|
||||
circuit_builder::CircuitBuilder,
|
||||
circuit_data::CircuitConfig,
|
||||
config::PoseidonGoldilocksConfig,
|
||||
},
|
||||
field::goldilocks_field::GoldilocksField,
|
||||
};
|
||||
use std::time::Instant;
|
||||
use rand::Rng;
|
||||
use tiny_keccak::{Keccak,Hasher};
|
||||
use anyhow::Result;
|
||||
use crate::arithmetic::{
|
||||
binary_arithmetic::xor_circuit,
|
||||
u64_arithmetic::U64Target
|
||||
};
|
||||
|
||||
pub const ROUND_CONSTANTS: [u64; 24] = [
|
||||
1u64,
|
||||
0x8082u64,
|
||||
0x800000000000808au64,
|
||||
0x8000000080008000u64,
|
||||
0x808bu64,
|
||||
0x80000001u64,
|
||||
0x8000000080008081u64,
|
||||
0x8000000000008009u64,
|
||||
0x8au64,
|
||||
0x88u64,
|
||||
0x80008009u64,
|
||||
0x8000000au64,
|
||||
0x8000808bu64,
|
||||
0x800000000000008bu64,
|
||||
0x8000000000008089u64,
|
||||
0x8000000000008003u64,
|
||||
0x8000000000008002u64,
|
||||
0x8000000000000080u64,
|
||||
0x800au64,
|
||||
0x800000008000000au64,
|
||||
0x8000000080008081u64,
|
||||
0x8000000000008080u64,
|
||||
0x80000001u64,
|
||||
0x8000000080008008u64,
|
||||
];
|
||||
pub const ROTR: [usize; 25] = [
|
||||
0, 1, 62, 28, 27, 36, 44, 6, 55, 20, 3, 10, 43, 25, 39, 41, 45, 15, 21, 8, 18, 2, 61, 56, 14,
|
||||
];
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct KeccakTarget<F, const D: usize> {
|
||||
words: Vec<U64Target<F, D>>,
|
||||
_phantom: PhantomData<F>,
|
||||
}
|
||||
|
||||
impl<F, const D: usize> KeccakTarget<F, D>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
{
|
||||
pub fn new(builder: &mut CircuitBuilder<F, D>) -> Self {
|
||||
let mut result = vec![];
|
||||
for _ in 0..25 {
|
||||
result.push(U64Target::new(builder));
|
||||
}
|
||||
Self {
|
||||
words: result,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_witness(&self, bits: Vec<bool>, pw: &mut PartialWitness<F>) {
|
||||
assert_eq!(bits.len(), 1600);
|
||||
for i in 0..25 {
|
||||
self.words[i].set_witness(bits[i * 64..(i + 1) * 64].to_vec(), pw);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn connect(&self, other: &Self, builder: &mut CircuitBuilder<F, D>) {
|
||||
for i in 0..25 {
|
||||
self.words[i].connect(&other.words[i], builder);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from(bits: Vec<BoolTarget>) -> Self {
|
||||
let mut result = vec![];
|
||||
for i in 0..25 {
|
||||
result.push(U64Target::from(bits[i * 64..(i + 1) * 64].to_vec()));
|
||||
}
|
||||
Self {
|
||||
words: result,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn keccak_round(&mut self, rc: u64, builder: &mut CircuitBuilder<F, D>) {
|
||||
// θ step
|
||||
let mut c = vec![];
|
||||
for x in 0..5 {
|
||||
let xor01 = self.words[x].xor(&self.words[x + 5], builder);
|
||||
let xor012 = xor01.xor(&self.words[x + 2 * 5], builder);
|
||||
let xor0123 = xor012.xor(&self.words[x + 3 * 5], builder);
|
||||
let xor01234 = xor0123.xor(&self.words[x + 4 * 5], builder);
|
||||
c.push(xor01234);
|
||||
}
|
||||
let mut d = vec![];
|
||||
for x in 0..5 {
|
||||
let rot_c = c[(x + 1) % 5].rotl(1);
|
||||
d.push(c[(x + 4) % 5].xor(&rot_c, builder));
|
||||
}
|
||||
for x in 0..5 {
|
||||
for y in 0..5 {
|
||||
self.words[x + y * 5] = self.words[x + y * 5].xor(&d[x], builder);
|
||||
}
|
||||
}
|
||||
// ρ and π steps
|
||||
let mut b_words: [Option<U64Target<F, D>>; 25] = [(); 25].map(|_| None);
|
||||
for x in 0..5 {
|
||||
for y in 0..5 {
|
||||
let rot_self = self.words[x + y * 5].rotl(ROTR[x + y * 5]);
|
||||
|
||||
b_words[y + ((2 * x + 3 * y) % 5) * 5] = Some(rot_self);
|
||||
}
|
||||
}
|
||||
let b = KeccakTarget {
|
||||
words: b_words.into_iter().map(|x| x.unwrap()).collect(),
|
||||
_phantom: PhantomData,
|
||||
};
|
||||
|
||||
// χ step
|
||||
for x in 0..5 {
|
||||
for y in 0..5 {
|
||||
// b.words[(x + 2) % 5 + y * 5] & !b.words[(x + 1) % 5 + y * 5]
|
||||
let and_not_b =
|
||||
b.words[(x + 2) % 5 + y * 5].and_not(&b.words[(x + 1) % 5 + y * 5], builder);
|
||||
self.words[x + y * 5] = b.words[x + y * 5].xor(&and_not_b, builder);
|
||||
}
|
||||
}
|
||||
|
||||
self.words[0] = self.words[0].xor_const(rc, builder);
|
||||
}
|
||||
|
||||
pub fn keccakf(&self, builder: &mut CircuitBuilder<F, D>) -> Self {
|
||||
let mut result = self.clone();
|
||||
for round_constant in ROUND_CONSTANTS.into_iter().take(24) {
|
||||
result.keccak_round(round_constant, builder);
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
pub fn keccak256_circuit<F, const D: usize>(
|
||||
input: Vec<BoolTarget>,
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
) -> Vec<BoolTarget>
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
{
|
||||
assert_eq!(input.len() % 8, 0); // input should be bytes.
|
||||
let block_size_in_bytes = 136; // in bytes
|
||||
let input_len_in_bytes = input.len() / 8;
|
||||
let num_blocks = input_len_in_bytes / block_size_in_bytes + 1;
|
||||
|
||||
let mut padded = vec![];
|
||||
for _ in 0..block_size_in_bytes * 8 * num_blocks {
|
||||
padded.push(builder.add_virtual_bool_target_safe());
|
||||
}
|
||||
|
||||
// register input
|
||||
for i in 0..input_len_in_bytes * 8 {
|
||||
builder.connect(padded[i].target, input[i].target);
|
||||
}
|
||||
|
||||
// append 0x01 = 1000 0000 after the last input
|
||||
let true_target = builder.constant_bool(true);
|
||||
builder.connect(padded[input_len_in_bytes * 8].target, true_target.target);
|
||||
|
||||
// pad 0s
|
||||
let false_target = builder.constant_bool(false);
|
||||
let last_index = padded.len() - 1;
|
||||
for i in input_len_in_bytes * 8 + 1..last_index {
|
||||
builder.connect(padded[i].target, false_target.target);
|
||||
}
|
||||
|
||||
// xor 0x80 = 0000 0001 with the last byte.
|
||||
// however the last bit is ensured to be 0, so just fill 1.
|
||||
builder.connect(padded[last_index].target, true_target.target);
|
||||
|
||||
let mut m = KeccakTarget::new(builder);
|
||||
for i in 0..1600 {
|
||||
let word = i / 64;
|
||||
let bit = i % 64;
|
||||
builder.connect(m.words[word].bits[bit].target, false_target.target);
|
||||
}
|
||||
|
||||
for i in 0..num_blocks {
|
||||
for j in 0..block_size_in_bytes * 8 {
|
||||
let word = j / 64;
|
||||
let bit = j % 64;
|
||||
let xor_t = xor_circuit(
|
||||
m.words[word].bits[bit],
|
||||
padded[i * block_size_in_bytes * 8 + j],
|
||||
builder,
|
||||
);
|
||||
m.words[word].bits[bit] = xor_t;
|
||||
}
|
||||
m = m.keccakf(builder);
|
||||
}
|
||||
|
||||
let mut z = Vec::new();
|
||||
for i in 0..256 {
|
||||
let new_target = builder.add_virtual_bool_target_safe();
|
||||
let word = i / 64;
|
||||
let bit = i % 64;
|
||||
builder.connect(new_target.target, m.words[word].bits[bit].target);
|
||||
z.push(new_target);
|
||||
}
|
||||
z
|
||||
}
|
||||
|
||||
pub fn keccak_bench(size: usize) -> Result<()>{
|
||||
|
||||
type F = GoldilocksField;
|
||||
type C = PoseidonGoldilocksConfig;
|
||||
const D: usize = 2;
|
||||
|
||||
let input_bytes = generate_data(size);
|
||||
let input = hex::encode(input_bytes);
|
||||
let expected_output = expected_keccak(&hex::decode(input.clone()).unwrap());
|
||||
|
||||
let input_bits = hex_str_to_bits(input.as_str())?;
|
||||
let exptected_output_bits = hex_str_to_bits(&expected_output)?;
|
||||
|
||||
let config = CircuitConfig::standard_recursion_config();
|
||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||
|
||||
let mut input_t = vec![];
|
||||
for i in 0..input_bits.len() {
|
||||
input_t.push(builder.constant_bool(input_bits[i]));
|
||||
}
|
||||
let output_t = keccak256_circuit(input_t, &mut builder);
|
||||
|
||||
let mut pw = PartialWitness::new();
|
||||
for i in 0..256 {
|
||||
pw.set_bool_target(output_t[i], exptected_output_bits[i]);
|
||||
}
|
||||
let circuit_size = builder.num_gates();
|
||||
|
||||
let data = builder.build::<C>();
|
||||
|
||||
let (proof_generation_time, proof) = {
|
||||
let now = Instant::now();
|
||||
let proof = data.prove(pw)?;
|
||||
(now.elapsed(), proof)
|
||||
};
|
||||
let proof_size = proof.to_bytes().len();
|
||||
let (verification_time, verification_result) = {
|
||||
let now = Instant::now();
|
||||
let res = data.verify(proof);
|
||||
(now.elapsed(), res)
|
||||
};
|
||||
|
||||
eprintln!("circuit size: {}", circuit_size);
|
||||
eprintln!("proof generation time: {:?}", proof_generation_time);
|
||||
eprintln!("verification time: {:?}", verification_time);
|
||||
eprintln!("proof size: {:?}", proof_size);
|
||||
|
||||
verification_result
|
||||
}
|
||||
|
||||
fn generate_data(size: usize) -> Vec<u8> {
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
let mut bytes = vec![0u8; size];
|
||||
rng.fill(&mut bytes[..]);
|
||||
bytes
|
||||
}
|
||||
|
||||
fn u8_to_bits(num: u8) -> Vec<bool> {
|
||||
let mut result = Vec::with_capacity(8);
|
||||
let mut n = num;
|
||||
for _ in 0..8 {
|
||||
result.push(n & 1 == 1);
|
||||
n >>= 1;
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn hex_str_to_bits(input: &str) -> Result<Vec<bool>> {
|
||||
let input_u8 = hex::decode(input)?;
|
||||
let input_bits = input_u8
|
||||
.iter()
|
||||
.flat_map(|x| u8_to_bits(*x))
|
||||
.collect::<Vec<_>>();
|
||||
Ok(input_bits)
|
||||
}
|
||||
|
||||
fn expected_keccak(input: &[u8]) -> String {
|
||||
let mut hasher = Keccak::v256();
|
||||
hasher.update(input);
|
||||
let mut hash = [0u8; 32];
|
||||
hasher.finalize(&mut hash);
|
||||
|
||||
hex::encode(hash)
|
||||
}
|
||||
161
hash/plonky2/bench/src/bench/keccak256/keccak_polygon.rs
Normal file
161
hash/plonky2/bench/src/bench/keccak256/keccak_polygon.rs
Normal file
@ -0,0 +1,161 @@
|
||||
// original source: https://github.com/0xPolygonZero/zk_evm/tree/develop
|
||||
|
||||
use evm_arithmetization::{
|
||||
keccak::keccak_stark::KeccakStark,
|
||||
prover::prove_single_table,
|
||||
StarkConfig
|
||||
};
|
||||
use anyhow::Result;
|
||||
use plonky2::{
|
||||
fri::oracle::PolynomialBatch,
|
||||
iop::challenger::Challenger,
|
||||
plonk::config::{GenericConfig, PoseidonGoldilocksConfig},
|
||||
field::polynomial::PolynomialValues,
|
||||
field::types::Field,
|
||||
timed,
|
||||
util::timing::TimingTree,
|
||||
};
|
||||
use starky::{
|
||||
cross_table_lookup::{CtlData, CtlZData},
|
||||
lookup::{GrandProductChallenge, GrandProductChallengeSet},
|
||||
lookup::{Filter,Column},
|
||||
};
|
||||
|
||||
use env_logger::{
|
||||
DEFAULT_FILTER_ENV,
|
||||
Env,
|
||||
try_init_from_env
|
||||
};
|
||||
|
||||
#[allow(dead_code)]
|
||||
/// Number of rounds in a Keccak permutation.
|
||||
pub(crate) const NUM_ROUNDS: usize = 24;
|
||||
|
||||
/// Number of 64-bit elements in the Keccak permutation input.
|
||||
pub(crate) const NUM_INPUTS: usize = 25;
|
||||
|
||||
fn ceil_div(a: usize, b: usize) -> usize {
|
||||
(a + b - 1) / b
|
||||
}
|
||||
|
||||
pub fn keccak_polygon_bench(size: usize) -> Result<()> {
|
||||
// here input in in terms of block
|
||||
let mut num_perms = 1;
|
||||
if size > 25 * 64/8 {
|
||||
num_perms = ceil_div(size, 25 * 64/8) as usize;
|
||||
}
|
||||
|
||||
const D: usize = 2;
|
||||
type C = PoseidonGoldilocksConfig;
|
||||
type F = <C as GenericConfig<D>>::F;
|
||||
type S = KeccakStark<F, D>;
|
||||
let stark = S::default();
|
||||
let config = StarkConfig::standard_fast_config();
|
||||
|
||||
init_logger();
|
||||
|
||||
let input: Vec<([u64; NUM_INPUTS], usize)> =
|
||||
(0..num_perms).map(|_| (rand::random(), 0)).collect();
|
||||
|
||||
let mut timing = TimingTree::new("prove", log::Level::Debug);
|
||||
let trace_poly_values = timed!(
|
||||
timing,
|
||||
"generate trace",
|
||||
stark.generate_trace(input, 8, &mut timing)
|
||||
);
|
||||
|
||||
let cloned_trace_poly_values = timed!(timing, "clone", trace_poly_values.clone());
|
||||
|
||||
let trace_commitments = timed!(
|
||||
timing,
|
||||
"compute trace commitment",
|
||||
PolynomialBatch::<F, C, D>::from_values(
|
||||
cloned_trace_poly_values,
|
||||
config.fri_config.rate_bits,
|
||||
false,
|
||||
config.fri_config.cap_height,
|
||||
&mut timing,
|
||||
None,
|
||||
)
|
||||
);
|
||||
let degree = 1 << trace_commitments.degree_log;
|
||||
|
||||
// Fake CTL data.
|
||||
let ctl_z_data = CtlZData::new(
|
||||
vec![PolynomialValues::zero(degree)],
|
||||
PolynomialValues::zero(degree),
|
||||
GrandProductChallenge {
|
||||
beta: F::ZERO,
|
||||
gamma: F::ZERO,
|
||||
},
|
||||
vec![],
|
||||
vec![Filter::new_simple(Column::constant(F::ZERO))],
|
||||
);
|
||||
let ctl_data = CtlData {
|
||||
zs_columns: vec![ctl_z_data.clone(); config.num_challenges],
|
||||
};
|
||||
|
||||
prove_single_table(
|
||||
&stark,
|
||||
&config,
|
||||
&trace_poly_values,
|
||||
&trace_commitments,
|
||||
&ctl_data,
|
||||
&GrandProductChallengeSet {
|
||||
challenges: vec![ctl_z_data.challenge; config.num_challenges],
|
||||
},
|
||||
&mut Challenger::new(),
|
||||
&mut timing,
|
||||
None,
|
||||
)?;
|
||||
|
||||
timing.print();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn init_logger() {
|
||||
let _ = try_init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "debug"));
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use anyhow::Result;
|
||||
use plonky2::field::types::PrimeField64;
|
||||
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
||||
use tiny_keccak::keccakf;
|
||||
use evm_arithmetization::keccak::columns::reg_output_limb;
|
||||
use super::*;
|
||||
const NUM_ROUNDS: usize = 24;
|
||||
|
||||
#[test]
|
||||
fn keccak_correctness_test() -> Result<()> {
|
||||
let input: [u64; NUM_INPUTS] = rand::random();
|
||||
|
||||
const D: usize = 2;
|
||||
type C = PoseidonGoldilocksConfig;
|
||||
type F = <C as GenericConfig<D>>::F;
|
||||
type S = KeccakStark<F, D>;
|
||||
|
||||
let stark: KeccakStark<<PoseidonGoldilocksConfig as GenericConfig<2>>::F, 2> = S::default();
|
||||
|
||||
let rows = stark.generate_trace_rows(vec![(input, 0)], 8);
|
||||
let last_row = rows[NUM_ROUNDS - 1];
|
||||
let output = (0..NUM_INPUTS)
|
||||
.map(|i| {
|
||||
let hi = last_row[reg_output_limb(2 * i + 1)].to_canonical_u64();
|
||||
let lo = last_row[reg_output_limb(2 * i)].to_canonical_u64();
|
||||
(hi << 32) | lo
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let expected = {
|
||||
let mut state = input;
|
||||
keccakf(&mut state);
|
||||
state
|
||||
};
|
||||
|
||||
assert_eq!(output, expected);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
75
hash/plonky2/bench/src/bench/poseidon.rs
Normal file
75
hash/plonky2/bench/src/bench/poseidon.rs
Normal file
@ -0,0 +1,75 @@
|
||||
use anyhow::Result;
|
||||
use plonky2::{
|
||||
field::types::Field,
|
||||
field::goldilocks_field::GoldilocksField,
|
||||
hash::poseidon::PoseidonHash,
|
||||
iop::witness::{PartialWitness, WitnessWrite},
|
||||
plonk::circuit_builder::CircuitBuilder,
|
||||
plonk::circuit_data::CircuitConfig,
|
||||
plonk::config::{GenericConfig, PoseidonGoldilocksConfig},
|
||||
};
|
||||
use rand::Rng;
|
||||
use std::time;
|
||||
|
||||
fn generate_data(size: usize) -> Vec<GoldilocksField> {
|
||||
|
||||
let mut data: Vec<GoldilocksField> = Vec::new();
|
||||
for _ in 0..(1<<size) {
|
||||
let mut rng = rand::thread_rng();
|
||||
let random_u64: u64 = rng.gen();
|
||||
data.push(GoldilocksField::from_canonical_u64(random_u64));
|
||||
}
|
||||
data
|
||||
|
||||
}
|
||||
|
||||
|
||||
pub fn poseidon_bench(depth: usize) -> Result<()> {
|
||||
|
||||
let data = generate_data(depth);
|
||||
|
||||
const D: usize = 2;
|
||||
type C = PoseidonGoldilocksConfig;
|
||||
type F = <C as GenericConfig<D>>::F;
|
||||
|
||||
let config = CircuitConfig::standard_recursion_config();
|
||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||
|
||||
// The arithmetic circuit.
|
||||
let initial = builder.add_virtual_targets(data.len());
|
||||
|
||||
let hash = builder.hash_or_noop::<PoseidonHash>(initial.clone());
|
||||
|
||||
// Public inputs are the initial value (provided below) and the result (which is generated).
|
||||
builder.register_public_inputs(initial.clone().as_slice());
|
||||
builder.register_public_input(hash.elements[0]);
|
||||
builder.register_public_input(hash.elements[1]);
|
||||
builder.register_public_input(hash.elements[2]);
|
||||
builder.register_public_input(hash.elements[3]);
|
||||
|
||||
// Provide initial values.
|
||||
let mut pw = PartialWitness::new();
|
||||
pw.set_target_arr(initial.as_slice(), data.as_slice());
|
||||
|
||||
|
||||
let data = builder.build::<C>();
|
||||
|
||||
let (proof_generation_time, proof) = {
|
||||
|
||||
let start = time::Instant::now();
|
||||
let proof = data.prove(pw)?;
|
||||
let end_time = start.elapsed();
|
||||
(end_time, proof)
|
||||
};
|
||||
|
||||
let (verification_time, result) = {
|
||||
let start = time::Instant::now();
|
||||
let result = data.verify(proof);
|
||||
let end_time = start.elapsed();
|
||||
(end_time, result)
|
||||
};
|
||||
|
||||
eprintln!("proof generation time: {:?}", proof_generation_time);
|
||||
eprintln!("verification time: {:?}", verification_time);
|
||||
result
|
||||
}
|
||||
26
hash/plonky2/bench/src/bench/sha256/ch.rs
Normal file
26
hash/plonky2/bench/src/bench/sha256/ch.rs
Normal file
@ -0,0 +1,26 @@
|
||||
use plonky2::iop::target::BoolTarget;
|
||||
use plonky2::hash::hash_types::RichField;
|
||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||
use plonky2::field::extension::Extendable;
|
||||
use plonky2_u32::gadgets::arithmetic_u32::U32Target;
|
||||
use crate::arithmetic::u32_arithmetic::{bits_to_u32_target, u32_to_bits_target};
|
||||
|
||||
|
||||
pub fn ch<F: RichField + Extendable<D>, const D: usize>(
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
a: &U32Target,
|
||||
b: &U32Target,
|
||||
c: &U32Target,
|
||||
) -> U32Target {
|
||||
let a_bits = u32_to_bits_target::<F, D, 2>(builder, a);
|
||||
let b_bits = u32_to_bits_target::<F, D, 2>(builder, b);
|
||||
let c_bits = u32_to_bits_target::<F, D, 2>(builder, c);
|
||||
let mut res_bits = Vec::new();
|
||||
for i in 0..32 {
|
||||
let b_sub_c = builder.sub(b_bits[i].target, c_bits[i].target);
|
||||
let a_mul_b_sub_c = builder.mul(a_bits[i].target, b_sub_c);
|
||||
let a_mul_b_sub_c_add_c = builder.add(a_mul_b_sub_c, c_bits[i].target);
|
||||
res_bits.push(BoolTarget::new_unsafe(a_mul_b_sub_c_add_c));
|
||||
}
|
||||
bits_to_u32_target(builder, res_bits)
|
||||
}
|
||||
24
hash/plonky2/bench/src/bench/sha256/constants.rs
Normal file
24
hash/plonky2/bench/src/bench/sha256/constants.rs
Normal file
@ -0,0 +1,24 @@
|
||||
|
||||
pub const H: [u32; 8] = [
|
||||
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
|
||||
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
|
||||
];
|
||||
|
||||
pub const K: [u32; 64] = [
|
||||
0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5,
|
||||
0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5,
|
||||
0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3,
|
||||
0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174,
|
||||
0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC,
|
||||
0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA,
|
||||
0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7,
|
||||
0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967,
|
||||
0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13,
|
||||
0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85,
|
||||
0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3,
|
||||
0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070,
|
||||
0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5,
|
||||
0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3,
|
||||
0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208,
|
||||
0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2
|
||||
];
|
||||
31
hash/plonky2/bench/src/bench/sha256/maj.rs
Normal file
31
hash/plonky2/bench/src/bench/sha256/maj.rs
Normal file
@ -0,0 +1,31 @@
|
||||
use plonky2::iop::target::BoolTarget;
|
||||
use plonky2::hash::hash_types::RichField;
|
||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||
use plonky2::field::extension::Extendable;
|
||||
use plonky2_u32::gadgets::arithmetic_u32::U32Target;
|
||||
|
||||
use crate::arithmetic::u32_arithmetic::{bits_to_u32_target, u32_to_bits_target};
|
||||
|
||||
pub fn maj<F: RichField + Extendable<D>, const D: usize>(
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
a: &U32Target,
|
||||
b: &U32Target,
|
||||
c: &U32Target,
|
||||
) -> U32Target {
|
||||
let a_bits = u32_to_bits_target::<F, D, 2>(builder, a);
|
||||
let b_bits = u32_to_bits_target::<F, D, 2>(builder, b);
|
||||
let c_bits = u32_to_bits_target::<F, D, 2>(builder, c);
|
||||
let mut res_bits = Vec::new();
|
||||
for i in 0..32 {
|
||||
let m = builder.mul(b_bits[i].target, c_bits[i].target);
|
||||
let two = builder.two();
|
||||
let two_m = builder.mul(two, m);
|
||||
let b_add_c = builder.add(b_bits[i].target, c_bits[i].target);
|
||||
let b_add_c_sub_two_m = builder.sub(b_add_c, two_m);
|
||||
let a_mul_b_add_c_sub_two_m = builder.mul(a_bits[i].target, b_add_c_sub_two_m);
|
||||
let res = builder.add(a_mul_b_add_c_sub_two_m, m);
|
||||
|
||||
res_bits.push(BoolTarget::new_unsafe(res));
|
||||
}
|
||||
bits_to_u32_target(builder, res_bits)
|
||||
}
|
||||
245
hash/plonky2/bench/src/bench/sha256/sha.rs
Normal file
245
hash/plonky2/bench/src/bench/sha256/sha.rs
Normal file
@ -0,0 +1,245 @@
|
||||
// code is taken from https://github.com/polymerdao/plonky2-sha256
|
||||
|
||||
use plonky2::iop::target::BoolTarget;
|
||||
use plonky2::hash::hash_types::RichField;
|
||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||
use plonky2::field::extension::Extendable;
|
||||
use plonky2_u32::gadgets::arithmetic_u32::{CircuitBuilderU32, U32Target};
|
||||
use anyhow::Result;
|
||||
use plonky2::plonk::config::PoseidonGoldilocksConfig;
|
||||
use plonky2::plonk::config::GenericConfig;
|
||||
use plonky2::plonk::circuit_data::CircuitConfig;
|
||||
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
|
||||
use sha2::{Digest, Sha256};
|
||||
use super::sigma::big_sigma0;
|
||||
use super::sigma::big_sigma1;
|
||||
use super::sigma::sigma0;
|
||||
use super::sigma::sigma1;
|
||||
use super::maj::maj;
|
||||
use super::constants::*;
|
||||
use super::ch::ch;
|
||||
use crate::arithmetic::u32_arithmetic::add_u32;
|
||||
use rand::Rng;
|
||||
|
||||
pub struct Sha256Targets {
|
||||
pub message: Vec<BoolTarget>,
|
||||
pub digest: Vec<BoolTarget>,
|
||||
}
|
||||
|
||||
pub fn array_to_bits(bytes: &[u8]) -> Vec<bool> {
|
||||
let len = bytes.len();
|
||||
let mut ret = Vec::new();
|
||||
for i in 0..len {
|
||||
for j in 0..8 {
|
||||
let b = (bytes[i] >> (7 - j)) & 1;
|
||||
ret.push(b == 1);
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
|
||||
pub fn make_circuits<F: RichField + Extendable<D>, const D: usize>(
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
msg_len_in_bits: u64,
|
||||
|
||||
) -> Sha256Targets {
|
||||
|
||||
|
||||
let mut message = Vec::new();
|
||||
let mut digest = Vec::new();
|
||||
|
||||
let block_count = (msg_len_in_bits + 65 + 511) / 512;
|
||||
let padded_msg_len = 512 * block_count;
|
||||
let p = padded_msg_len - 64 - msg_len_in_bits;
|
||||
assert!(p > 1);
|
||||
|
||||
//msg
|
||||
for _ in 0..msg_len_in_bits {
|
||||
message.push(builder.add_virtual_bool_target_unsafe());
|
||||
}
|
||||
|
||||
//append a single bit '1'
|
||||
message.push(builder.constant_bool(true));
|
||||
|
||||
//append '0' bit so that total length become multiple of 512
|
||||
for _ in 0..p - 1 {
|
||||
message.push(builder.constant_bool(false));
|
||||
}
|
||||
|
||||
//append the msg length as 64bit big-endian integer
|
||||
for i in 0..64 {
|
||||
let b = ((msg_len_in_bits as u64) >> (63 - i)) & 1;
|
||||
message.push(builder.constant_bool(b == 1));
|
||||
}
|
||||
|
||||
// init states
|
||||
let mut state = Vec::new();
|
||||
for i in 0..8 {
|
||||
state.push(builder.constant_u32(H[i]));
|
||||
}
|
||||
|
||||
let mut k256 = Vec::new();
|
||||
for i in 0..64 {
|
||||
k256.push(builder.constant_u32(K[i]));
|
||||
}
|
||||
|
||||
for blk in 0..block_count {
|
||||
let mut x = Vec::new();
|
||||
let mut a = state[0].clone();
|
||||
let mut b = state[1].clone();
|
||||
let mut c = state[2].clone();
|
||||
let mut d = state[3].clone();
|
||||
let mut e = state[4].clone();
|
||||
let mut f = state[5].clone();
|
||||
let mut g = state[6].clone();
|
||||
let mut h = state[7].clone();
|
||||
|
||||
for i in 0..16 {
|
||||
let index = blk as usize * 512 + i * 32;
|
||||
let u32_target = builder.le_sum(message[index..index + 32].iter().rev());
|
||||
|
||||
x.push(U32Target(u32_target));
|
||||
let mut t1 = h.clone();
|
||||
let big_sigma1_e = big_sigma1(builder, &e);
|
||||
t1 = add_u32(builder, &t1, &big_sigma1_e);
|
||||
let ch_e_f_g = ch(builder, &e, &f, &g);
|
||||
t1 = add_u32(builder, &t1, &ch_e_f_g);
|
||||
t1 = add_u32(builder, &t1, &k256[i]);
|
||||
t1 = add_u32(builder, &t1, &x[i]);
|
||||
|
||||
let mut t2 = big_sigma0(builder, &a);
|
||||
let maj_a_b_c = maj(builder, &a, &b, &c);
|
||||
t2 = add_u32(builder, &t2, &maj_a_b_c);
|
||||
|
||||
h = g;
|
||||
g = f;
|
||||
f = e;
|
||||
e = add_u32(builder, &d, &t1);
|
||||
d = c;
|
||||
c = b;
|
||||
b = a;
|
||||
a = add_u32(builder, &t1, &t2);
|
||||
}
|
||||
|
||||
for i in 16..64 {
|
||||
let s0 = sigma0(builder, &x[(i + 1) & 0x0f]);
|
||||
let s1 = sigma1(builder, &x[(i + 14) & 0x0f]);
|
||||
|
||||
let s0_add_s1 = add_u32(builder, &s0, &s1);
|
||||
let s0_add_s1_add_x = add_u32(builder, &s0_add_s1, &x[(i + 9) & 0xf]);
|
||||
x[i & 0xf] = add_u32(builder, &x[i & 0xf], &s0_add_s1_add_x);
|
||||
|
||||
let big_sigma0_a = big_sigma0(builder, &a);
|
||||
let big_sigma1_e = big_sigma1(builder, &e);
|
||||
let ch_e_f_g = ch(builder, &e, &f, &g);
|
||||
let maj_a_b_c = maj(builder, &a, &b, &c);
|
||||
|
||||
let h_add_sigma1 = add_u32(builder, &h, &big_sigma1_e);
|
||||
let h_add_sigma1_add_ch_e_f_g = add_u32(builder, &h_add_sigma1, &ch_e_f_g);
|
||||
let h_add_sigma1_add_ch_e_f_g_add_k256 =
|
||||
add_u32(builder, &h_add_sigma1_add_ch_e_f_g, &k256[i]);
|
||||
|
||||
let t1 = add_u32(builder, &x[i & 0xf], &h_add_sigma1_add_ch_e_f_g_add_k256);
|
||||
let t2 = add_u32(builder, &big_sigma0_a, &maj_a_b_c);
|
||||
|
||||
h = g;
|
||||
g = f;
|
||||
f = e;
|
||||
e = add_u32(builder, &d, &t1);
|
||||
d = c;
|
||||
c = b;
|
||||
b = a;
|
||||
a = add_u32(builder, &t1, &t2);
|
||||
}
|
||||
|
||||
state[0] = add_u32(builder, &state[0], &a);
|
||||
state[1] = add_u32(builder, &state[1], &b);
|
||||
state[2] = add_u32(builder, &state[2], &c);
|
||||
state[3] = add_u32(builder, &state[3], &d);
|
||||
state[4] = add_u32(builder, &state[4], &e);
|
||||
state[5] = add_u32(builder, &state[5], &f);
|
||||
state[6] = add_u32(builder, &state[6], &g);
|
||||
state[7] = add_u32(builder, &state[7], &h);
|
||||
}
|
||||
|
||||
for i in 0..8 {
|
||||
let bit_targets = builder.split_le_base::<2>(state[i].0, 32);
|
||||
for j in (0..32).rev() {
|
||||
digest.push(BoolTarget::new_unsafe(bit_targets[j]));
|
||||
}
|
||||
}
|
||||
|
||||
Sha256Targets { message, digest }
|
||||
}
|
||||
|
||||
|
||||
fn generate_random_bytes(size: usize) -> Vec<u8> {
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
let mut bytes = vec![0u8; size];
|
||||
rng.fill(&mut bytes[..]);
|
||||
|
||||
bytes
|
||||
|
||||
}
|
||||
|
||||
pub fn sha256_bench(size: usize) -> Result<()> {
|
||||
let msg = generate_random_bytes(size);
|
||||
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(msg.clone());
|
||||
let hash = hasher.finalize();
|
||||
|
||||
let msg_bits = array_to_bits(&msg.clone());
|
||||
let len = msg.len() * 8;
|
||||
println!("block count: {}", (len + 65 + 511) / 512);
|
||||
const D: usize = 2;
|
||||
type C = PoseidonGoldilocksConfig;
|
||||
type F = <C as GenericConfig<D>>::F;
|
||||
let mut builder = CircuitBuilder::<F, D>::new(CircuitConfig::standard_recursion_config());
|
||||
let targets = make_circuits(&mut builder, len as u64);
|
||||
let mut pw = PartialWitness::new();
|
||||
|
||||
for i in 0..len {
|
||||
pw.set_bool_target(targets.message[i], msg_bits[i]);
|
||||
}
|
||||
|
||||
let expected_res = array_to_bits(hash.as_slice());
|
||||
for i in 0..expected_res.len() {
|
||||
if expected_res[i] {
|
||||
builder.assert_one(targets.digest[i].target);
|
||||
} else {
|
||||
builder.assert_zero(targets.digest[i].target);
|
||||
}
|
||||
}
|
||||
|
||||
println!(
|
||||
"number of gates: {}",
|
||||
builder.num_gates()
|
||||
);
|
||||
let data = builder.build::<C>();
|
||||
|
||||
let (proof_time, proof ) = {
|
||||
|
||||
let start = std::time::Instant::now();
|
||||
let proof = data.prove(pw).unwrap();
|
||||
let end = start.elapsed();
|
||||
(end, proof)
|
||||
};
|
||||
let proof_size = proof.to_bytes().len();
|
||||
|
||||
let (verification_time, res) = {
|
||||
let start = std::time::Instant::now();
|
||||
let res = data.verify(proof);
|
||||
let end = start.elapsed();
|
||||
(end, res)
|
||||
};
|
||||
|
||||
eprintln!("Proof Generation Time: {:?}", proof_time);
|
||||
eprintln!("Verification Time: {:?}", verification_time);
|
||||
eprintln!("Proof size: {:?}", proof_size);
|
||||
|
||||
res
|
||||
|
||||
}
|
||||
91
hash/plonky2/bench/src/bench/sha256/sigma.rs
Normal file
91
hash/plonky2/bench/src/bench/sha256/sigma.rs
Normal file
@ -0,0 +1,91 @@
|
||||
use plonky2::hash::hash_types::RichField;
|
||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||
use plonky2::field::extension::Extendable;
|
||||
use plonky2_u32::gadgets::arithmetic_u32::U32Target;
|
||||
use crate::arithmetic::u32_arithmetic::{rotate32, shift32};
|
||||
use crate::arithmetic::u32_arithmetic::u32_to_bits_target;
|
||||
use crate::arithmetic::u32_arithmetic::bits_to_u32_target;
|
||||
use super::xor3::xor3;
|
||||
|
||||
pub fn sigma0<F: RichField + Extendable<D>, const D: usize>(
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
a: &U32Target,
|
||||
) -> U32Target {
|
||||
let mut a_bits = u32_to_bits_target::<F, D, 2>(builder, a);
|
||||
a_bits.push(builder.constant_bool(false));
|
||||
let rotate7 = rotate32(7);
|
||||
let rotate18 = rotate32(18);
|
||||
let shift3 = shift32(3);
|
||||
let mut res_bits = Vec::new();
|
||||
for i in 0..32 {
|
||||
res_bits.push(xor3(
|
||||
builder,
|
||||
a_bits[rotate7[i]],
|
||||
a_bits[rotate18[i]],
|
||||
a_bits[shift3[i]],
|
||||
));
|
||||
}
|
||||
bits_to_u32_target(builder, res_bits)
|
||||
}
|
||||
|
||||
pub fn sigma1<F: RichField + Extendable<D>, const D: usize>(
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
a: &U32Target,
|
||||
) -> U32Target {
|
||||
let mut a_bits = u32_to_bits_target::<F, D, 2>(builder, a);
|
||||
a_bits.push(builder.constant_bool(false));
|
||||
let rotate17 = rotate32(17);
|
||||
let rotate19 = rotate32(19);
|
||||
let shift10 = shift32(10);
|
||||
let mut res_bits = Vec::new();
|
||||
for i in 0..32 {
|
||||
res_bits.push(xor3(
|
||||
builder,
|
||||
a_bits[rotate17[i]],
|
||||
a_bits[rotate19[i]],
|
||||
a_bits[shift10[i]],
|
||||
));
|
||||
}
|
||||
bits_to_u32_target(builder, res_bits)
|
||||
}
|
||||
|
||||
//#define Sigma0(x) (ROTATE((x), 2) ^ ROTATE((x),13) ^ ROTATE((x),22))
|
||||
pub fn big_sigma0<F: RichField + Extendable<D>, const D: usize>(
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
a: &U32Target,
|
||||
) -> U32Target {
|
||||
let a_bits = u32_to_bits_target::<F, D, 2>(builder, a);
|
||||
let rotate2 = rotate32(2);
|
||||
let rotate13 = rotate32(13);
|
||||
let rotate22 = rotate32(22);
|
||||
let mut res_bits = Vec::new();
|
||||
for i in 0..32 {
|
||||
res_bits.push(xor3(
|
||||
builder,
|
||||
a_bits[rotate2[i]],
|
||||
a_bits[rotate13[i]],
|
||||
a_bits[rotate22[i]],
|
||||
));
|
||||
}
|
||||
bits_to_u32_target(builder, res_bits)
|
||||
}
|
||||
|
||||
pub fn big_sigma1<F: RichField + Extendable<D>, const D: usize>(
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
a: &U32Target,
|
||||
) -> U32Target {
|
||||
let a_bits = u32_to_bits_target::<F, D, 2>(builder, a);
|
||||
let rotate6 = rotate32(6);
|
||||
let rotate11 = rotate32(11);
|
||||
let rotate25 = rotate32(25);
|
||||
let mut res_bits = Vec::new();
|
||||
for i in 0..32 {
|
||||
res_bits.push(xor3(
|
||||
builder,
|
||||
a_bits[rotate6[i]],
|
||||
a_bits[rotate11[i]],
|
||||
a_bits[rotate25[i]],
|
||||
));
|
||||
}
|
||||
bits_to_u32_target(builder, res_bits)
|
||||
}
|
||||
26
hash/plonky2/bench/src/bench/sha256/xor3.rs
Normal file
26
hash/plonky2/bench/src/bench/sha256/xor3.rs
Normal file
@ -0,0 +1,26 @@
|
||||
use plonky2::iop::target::BoolTarget;
|
||||
use plonky2::hash::hash_types::RichField;
|
||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||
use plonky2::field::extension::Extendable;
|
||||
|
||||
pub fn xor3<F: RichField + Extendable<D>, const D: usize>(
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
a: BoolTarget,
|
||||
b: BoolTarget,
|
||||
c: BoolTarget,
|
||||
) -> BoolTarget {
|
||||
let m = builder.mul(b.target, c.target);
|
||||
let two_b = builder.add(b.target, b.target);
|
||||
let two_c = builder.add(c.target, c.target);
|
||||
let two_m = builder.add(m, m);
|
||||
let four_m = builder.add(two_m, two_m);
|
||||
let one = builder.one();
|
||||
let one_sub_two_b = builder.sub(one, two_b);
|
||||
let one_sub_two_b_sub_two_c = builder.sub(one_sub_two_b, two_c);
|
||||
let one_sub_two_b_sub_two_c_add_four_m = builder.add(one_sub_two_b_sub_two_c, four_m);
|
||||
let mut res = builder.mul(a.target, one_sub_two_b_sub_two_c_add_four_m);
|
||||
res = builder.add(res, b.target);
|
||||
res = builder.add(res, c.target);
|
||||
|
||||
BoolTarget::new_unsafe(builder.sub(res, two_m))
|
||||
}
|
||||
77
hash/plonky2/bench/src/main.rs
Normal file
77
hash/plonky2/bench/src/main.rs
Normal file
@ -0,0 +1,77 @@
|
||||
|
||||
use std::process;
|
||||
mod bench{
|
||||
pub mod poseidon;
|
||||
pub mod sha256{
|
||||
pub mod constants;
|
||||
pub mod sigma;
|
||||
pub mod sha;
|
||||
pub mod xor3;
|
||||
pub mod maj;
|
||||
pub mod ch;
|
||||
|
||||
}
|
||||
|
||||
pub mod keccak256{
|
||||
pub mod keccak;
|
||||
pub mod keccak_polygon;
|
||||
}
|
||||
}
|
||||
|
||||
mod arithmetic {
|
||||
pub mod binary_arithmetic;
|
||||
pub mod u32_arithmetic;
|
||||
pub mod u64_arithmetic;
|
||||
}
|
||||
|
||||
use bench::poseidon::poseidon_bench;
|
||||
use bench::keccak256::keccak::keccak_bench;
|
||||
use bench::sha256::sha::sha256_bench;
|
||||
use bench::keccak256::keccak_polygon::keccak_polygon_bench;
|
||||
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
|
||||
if args.len() != 3 {
|
||||
println!("Wrong number of arguments! The program expects two arguments: <hash_type> and <size>");
|
||||
// Exit the program with a non-zero exit code
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
let hash_type = &args[1];
|
||||
let size = args[2].parse::<usize>().unwrap();
|
||||
|
||||
match hash_type.as_str() {
|
||||
|
||||
"poseidon" => {
|
||||
println!("Running Poseidon: ");
|
||||
eprintln!("Tree Depth: {:?}", size);
|
||||
let _ = poseidon_bench(size);
|
||||
}
|
||||
|
||||
"keccak" => {
|
||||
println!("Running keccak: ");
|
||||
eprintln!("input size: {:?}", size);
|
||||
let _ = keccak_bench(size);
|
||||
}
|
||||
|
||||
"keccak-polygon" => {
|
||||
println!("Running keccak of plolygon zk_evm: ");
|
||||
let _ = keccak_polygon_bench(size);
|
||||
}
|
||||
|
||||
|
||||
"sha256" => {
|
||||
println!("Running sha256: ");
|
||||
let _ = sha256_bench(size);
|
||||
}
|
||||
|
||||
_ => {
|
||||
println!("Wrong Benchmark Name!");
|
||||
}
|
||||
}
|
||||
|
||||
println!("All Done!");
|
||||
|
||||
}
|
||||
1
hash/plonky2/external/zk_evm
vendored
Submodule
1
hash/plonky2/external/zk_evm
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 5511569d166c29cc85211df31bb9485870ca53ab
|
||||
2
hash/snark/external/hash-circuits
vendored
2
hash/snark/external/hash-circuits
vendored
@ -1 +1 @@
|
||||
Subproject commit e6b99b20f038f27390f590313ce7de227d6dd42a
|
||||
Subproject commit 3ae1517526f1061a8d37a159270cc15727e6b503
|
||||
Loading…
x
Reference in New Issue
Block a user