Skip to content
Open
4 changes: 4 additions & 0 deletions dev/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ bin = [
{ name = "dlp1_sol", path = "../solutions/zk-protocols-basics/01_discrete_log_problem/dlp1.rs" },
{ name = "dlp2", path = "../exercises/zk-protocols-basics/01_discrete_log_problem/dlp2.rs" },
{ name = "dlp2_sol", path = "../solutions/zk-protocols-basics/01_discrete_log_problem/dlp2.rs" },
{ name = "addition", path = "../exercises/plonky3/src/addition.rs" },
{ name = "addition_sol", path = "../solutions/plonky3/src/addition.rs" },
{ name = "fibonacci", path = "../exercises/plonky3/src/fibonacci.rs" },
{ name = "fibonacci_sol", path = "../solutions/plonky3/src/fibonacci.rs" },
]

[package]
Expand Down
41 changes: 41 additions & 0 deletions exercises/plonky3/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
[package]
name = "fibonacci"
version = "0.1.0"
edition = "2021"

[workspace]
members = []
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[[bin]]
name = "fibonacci"
path = "src/fibonacci.rs"

[[bin]]
name = "addition"
path = "src/addition.rs"


[dependencies]
p3-air = { git = "https://github.com/Plonky3/Plonky3.git" }
p3-field = { git = "https://github.com/Plonky3/Plonky3.git" }
p3-matrix = { git = "https://github.com/Plonky3/Plonky3.git" }
p3-mersenne-31 = { git = "https://github.com/Plonky3/Plonky3.git" }
p3-util = { git = "https://github.com/Plonky3/Plonky3.git" }
p3-baby-bear = { git = "https://github.com/Plonky3/Plonky3.git" }
p3-challenger = { git = "https://github.com/Plonky3/Plonky3.git" }
p3-circle = { git = "https://github.com/Plonky3/Plonky3.git" }
p3-commit = { git = "https://github.com/Plonky3/Plonky3.git" }
p3-dft = { git = "https://github.com/Plonky3/Plonky3.git" }
p3-fri = { git = "https://github.com/Plonky3/Plonky3.git" }
p3-goldilocks = { git = "https://github.com/Plonky3/Plonky3.git" }
p3-blake3 = { git = "https://github.com/Plonky3/Plonky3.git" }
p3-keccak = { git = "https://github.com/Plonky3/Plonky3.git" }
p3-mds = { git = "https://github.com/Plonky3/Plonky3.git" }
p3-merkle-tree = { git = "https://github.com/Plonky3/Plonky3.git" }
p3-poseidon = { git = "https://github.com/Plonky3/Plonky3.git" }
p3-poseidon2 = { git = "https://github.com/Plonky3/Plonky3.git" }
p3-symmetric = { git = "https://github.com/Plonky3/Plonky3.git" }
p3-uni-stark = { git = "https://github.com/Plonky3/Plonky3.git" }
rand = "0.8.5"
tracing-subscriber = { version = "0.3.17", features = ["std", "env-filter"] }
tracing-forest = { version = "0.1.6", features = ["ansi", "smallvec"] }
43 changes: 43 additions & 0 deletions exercises/plonky3/src/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@



## What is Plonky3 and How does it work?

Plonky3 is a toolkit for implementing polynomial IOPs (PIOPs), such as STARKs, allowing developers to configure a variety of to-spec implementations from a single ZK proving system.

Polygon Plonky3 supports several finite fields and hash functions. Currently, the only supported polynomial commitment scheme is FRI, but future releases will support several, including Brakedown.

### Understand Plonky3

Plonky3 is a toolkit for designing a custom ZK proving implementation that can then be used to power application-optimized zkVMs and zkEVMs.

In the same way an AI dev uses Pytorch and Tensorflow for building AI models, Polygon Plonky3 provides the same flexibility for building ZK proving systems. Polygon Plonky3 enables simple builds, such as the Fibonacci sequence prover in this repo that requires only one AIR Scripts, to complex systems, such as SP1 from Succinct labs with multiple AIR scripts for different components of a single zkVM.

### How does Plonky3 work?

Here's a TLDR version:

1. Define the computation using Algebraic Intermediate Representation (AIR).
2. Generate a trace of the computation based on the AIR.
3. Utilize efficient finite field implementations for arithmetic operations.
4. Apply a vector commitment scheme (like MMCS) to create a succinct commitment to the trace.
5. Construct polynomials from the committed trace and commit to these polynomials using a polynomial commitment scheme (like Circle PCS).
6. Perform fast polynomial operations using FFTs and related algorithms.
7. Implement the FRI (Fast Reed-Solomon IOP) protocol to prove properties about committed polynomials.
8. Employ a challenger mechanism with the Fiat-Shamir heuristic for non-interactive proofs.
9. The unified STARK prover combines all components to generate the proof.
10. The verifier uses the same components to efficiently check the proof's validity.

Plonky3's modular design allows for easy customization and optimization of different components, making it adaptable to various use cases and performance requirements.

#### "Seems very complicated, I don't understand half of the steps."
*Don't worry, These are the underlying workflow of Plonky3 based ZK system, if you are builders who are using Plonky3, your main job is at Step 1 and Step 2, the rest is just configuration!*

## Understand Steps to create a proof

So now that we briefly walked through what Plonky3 is and how does it work, let's see this simple hands-on example to learn more!

We will walk through our fibonacci example code via blow steps

![Plonky3 Steps](https://github.com/BrianSeong99/plonky3_fibonacci/blob/master/pics/p3_fib_steps.png?raw=true)

126 changes: 126 additions & 0 deletions exercises/plonky3/src/addition.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
use std::fmt::Debug;
use std::marker::PhantomData;
use p3_air::{Air, AirBuilder, BaseAir};
use p3_field::{AbstractField, Field};
use p3_matrix::dense::RowMajorMatrix;
use p3_matrix::Matrix;

use p3_challenger::{HashChallenger, SerializingChallenger32};
use p3_circle::CirclePcs;
use p3_commit::ExtensionMmcs;
use p3_field::extension::BinomialExtensionField;
use p3_fri::FriConfig;
use p3_keccak::Keccak256Hash;
use p3_merkle_tree::FieldMerkleTreeMmcs;
use p3_mersenne_31::Mersenne31;
use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher32};
use p3_uni_stark::{prove, verify, StarkConfig};
use tracing_forest::util::LevelFilter;
use tracing_forest::ForestLayer;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
use tracing_subscriber::{EnvFilter, Registry};

pub struct AdditionAir {
pub a: u32,
pub b: u32,
pub c: u32,
}

impl<F: Field> BaseAir<F> for AdditionAir {
fn width(&self) -> usize {
3
}
}

impl<AB: AirBuilder> Air<AB> for AdditionAir {
fn eval(&self, builder: &mut AB) {
let main = builder.main();
let local = main.row_slice(0);

builder
.when_first_row()
.assert_eq(local[0] + local[1], local[2]);

let final_value = AB::Expr::from_canonical_u32(self.c);
builder.when_first_row().assert_eq(local[2], final_value);
}
}

pub fn generate_addition_trace<F: Field>(a: u32, b: u32, c: u32) -> RowMajorMatrix<F> {
let mut values = Vec::with_capacity(3 * 4);
let a_f = F::from_canonical_u32(a);
let b_f = F::from_canonical_u32(b);
let c_f = F::from_canonical_u32(c);

values.push(a_f);
values.push(b_f);
values.push(c_f);

for _ in 0..3 {
values.push(F::zero());
values.push(F::zero());
values.push(F::zero());
}

RowMajorMatrix::new(values, 3)
}

fn main() -> Result<(), impl Debug> {
let env_filter = EnvFilter::builder()
.with_default_directive(LevelFilter::INFO.into())
.from_env_lossy();

Registry::default()
.with(env_filter)
.with(ForestLayer::default())
.init();

type Val = Mersenne31;
type Challenge = BinomialExtensionField<Val, 3>;

type ByteHash = Keccak256Hash;
type FieldHash = SerializingHasher32<ByteHash>;
let byte_hash = ByteHash {};
let field_hash = FieldHash::new(Keccak256Hash {});

type MyCompress = CompressionFunctionFromHasher<u8, ByteHash, 2, 32>;
let compress = MyCompress::new(byte_hash);

type ValMmcs = FieldMerkleTreeMmcs<Val, u8, FieldHash, MyCompress, 32>;
let val_mmcs = ValMmcs::new(field_hash, compress);

type ChallengeMmcs = ExtensionMmcs<Val, Challenge, ValMmcs>;
let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone());

type Challenger = SerializingChallenger32<Val, HashChallenger<u8, ByteHash, 32>>;

let fri_config = FriConfig {
log_blowup: 1,
num_queries: 100,
proof_of_work_bits: 16,
mmcs: challenge_mmcs,
};

type Pcs = CirclePcs<Val, ValMmcs, ChallengeMmcs>;
let pcs = Pcs {
mmcs: val_mmcs,
fri_config,
_phantom: PhantomData,
};

type MyConfig = StarkConfig<Pcs, Challenge, Challenger>;
let config = MyConfig::new(pcs);

let a = 4;
let b = 5;
let c = 9;
let air = AdditionAir { a, b, c };
let trace = generate_addition_trace::<Val>(a, b, c);

let mut challenger = Challenger::from_hasher(vec![], byte_hash);
let proof = prove(&config, &air, &mut challenger, trace, &vec![]);

let mut challenger = Challenger::from_hasher(vec![], byte_hash);
verify(&config, &air, &mut challenger, &proof, &vec![])
}
131 changes: 131 additions & 0 deletions exercises/plonky3/src/fibonacci.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
use std::fmt::Debug;
use std::marker::PhantomData;

use p3_air::{Air, AirBuilder, BaseAir};
use p3_field::{AbstractField, Field};
use p3_matrix::dense::RowMajorMatrix;
use p3_matrix::Matrix;

use p3_challenger::{HashChallenger, SerializingChallenger32};
use p3_circle::CirclePcs;
use p3_commit::ExtensionMmcs;
use p3_field::extension::BinomialExtensionField;
use p3_fri::FriConfig;
use p3_keccak::Keccak256Hash;
use p3_merkle_tree::FieldMerkleTreeMmcs;
use p3_mersenne_31::Mersenne31;
use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher32};
use p3_uni_stark::{prove, verify, StarkConfig};
use tracing_forest::util::LevelFilter;
use tracing_forest::ForestLayer;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
use tracing_subscriber::{EnvFilter, Registry};

pub struct FibonacciAir {
pub num_steps: usize,
pub final_value: u32,
}

impl<F: Field> BaseAir<F> for FibonacciAir {
fn width(&self) -> usize {
2
}
}

impl<AB: AirBuilder> Air<AB> for FibonacciAir {
fn eval(&self, builder: &mut AB) {
let main = builder.main();
let local = main.row_slice(0);
let next = main.row_slice(1);

builder
.when_first_row()
.assert_eq(local[0], AB::Expr::zero());
builder
.when_first_row()
.assert_eq(local[1], AB::Expr::one());
builder.when_transition().assert_eq(next[0], local[1]);
builder
.when_transition()
.assert_eq(next[1], local[0] + local[1]);

let final_value = AB::Expr::from_canonical_u32(self.final_value);
builder.when_last_row().assert_eq(local[1], final_value);
}
}

pub fn generate_fibonacci_trace<F: Field>(num_steps: usize) -> RowMajorMatrix<F> {
let mut values = Vec::with_capacity(num_steps * 2);
let mut a = F::zero();
let mut b = F::one();
for _ in 0..num_steps {
values.push(a);
values.push(b);
let c = a + b;
a = b;
b = c;
}
RowMajorMatrix::new(values, 2)
}

fn main() -> Result<(), impl Debug> {
let env_filter = EnvFilter::builder()
.with_default_directive(LevelFilter::INFO.into())
.from_env_lossy();

Registry::default()
.with(env_filter)
.with(ForestLayer::default())
.init();

type Val = Mersenne31;
type Challenge = BinomialExtensionField<Val, 3>;

type ByteHash = Keccak256Hash;
type FieldHash = SerializingHasher32<ByteHash>;
let byte_hash = ByteHash {};
let field_hash = FieldHash::new(Keccak256Hash {});

type MyCompress = CompressionFunctionFromHasher<u8, ByteHash, 2, 32>;
let compress = MyCompress::new(byte_hash);

type ValMmcs = FieldMerkleTreeMmcs<Val, u8, FieldHash, MyCompress, 32>;
let val_mmcs = ValMmcs::new(field_hash, compress);

type ChallengeMmcs = ExtensionMmcs<Val, Challenge, ValMmcs>;
let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone());

type Challenger = SerializingChallenger32<Val, HashChallenger<u8, ByteHash, 32>>;

let fri_config = FriConfig {
log_blowup: 1,
num_queries: 100,
proof_of_work_bits: 16,
mmcs: challenge_mmcs,
};

type Pcs = CirclePcs<Val, ValMmcs, ChallengeMmcs>;
let pcs = Pcs {
mmcs: val_mmcs,
fri_config,
_phantom: PhantomData,
};

type MyConfig = StarkConfig<Pcs, Challenge, Challenger>;
let config = MyConfig::new(pcs);

let num_steps = 16;
let final_value = 987;
let air = FibonacciAir {
num_steps,
final_value,
};
let trace = generate_fibonacci_trace::<Val>(num_steps);

let mut challenger = Challenger::from_hasher(vec![], byte_hash);
let proof = prove(&config, &air, &mut challenger, trace, &vec![]);

let mut challenger = Challenger::from_hasher(vec![], byte_hash);
verify(&config, &air, &mut challenger, &proof, &vec![])
}
41 changes: 41 additions & 0 deletions solutions/plonky3/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
[package]
name = "fibonacci"
version = "0.1.0"
edition = "2021"

[workspace]
members = []
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[[bin]]
name = "fibonacci"
path = "src/fibonacci.rs"

[[bin]]
name = "addition"
path = "src/addition.rs"


[dependencies]
p3-air = { git = "https://github.com/Plonky3/Plonky3.git" }
p3-field = { git = "https://github.com/Plonky3/Plonky3.git" }
p3-matrix = { git = "https://github.com/Plonky3/Plonky3.git" }
p3-mersenne-31 = { git = "https://github.com/Plonky3/Plonky3.git" }
p3-util = { git = "https://github.com/Plonky3/Plonky3.git" }
p3-baby-bear = { git = "https://github.com/Plonky3/Plonky3.git" }
p3-challenger = { git = "https://github.com/Plonky3/Plonky3.git" }
p3-circle = { git = "https://github.com/Plonky3/Plonky3.git" }
p3-commit = { git = "https://github.com/Plonky3/Plonky3.git" }
p3-dft = { git = "https://github.com/Plonky3/Plonky3.git" }
p3-fri = { git = "https://github.com/Plonky3/Plonky3.git" }
p3-goldilocks = { git = "https://github.com/Plonky3/Plonky3.git" }
p3-blake3 = { git = "https://github.com/Plonky3/Plonky3.git" }
p3-keccak = { git = "https://github.com/Plonky3/Plonky3.git" }
p3-mds = { git = "https://github.com/Plonky3/Plonky3.git" }
p3-merkle-tree = { git = "https://github.com/Plonky3/Plonky3.git" }
p3-poseidon = { git = "https://github.com/Plonky3/Plonky3.git" }
p3-poseidon2 = { git = "https://github.com/Plonky3/Plonky3.git" }
p3-symmetric = { git = "https://github.com/Plonky3/Plonky3.git" }
p3-uni-stark = { git = "https://github.com/Plonky3/Plonky3.git" }
rand = "0.8.5"
tracing-subscriber = { version = "0.3.17", features = ["std", "env-filter"] }
tracing-forest = { version = "0.1.6", features = ["ansi", "smallvec"] }
Loading