mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-07 00:03:10 +00:00
Started implementing FRI
This commit is contained in:
parent
1ab12c3dfd
commit
aa50387d36
72
src/fri.rs
72
src/fri.rs
@ -1,3 +1,11 @@
|
||||
use crate::field::field::Field;
|
||||
use crate::hash::{compress, hash_n_to_hash};
|
||||
use crate::plonk_challenger::Challenger;
|
||||
use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues};
|
||||
use crate::proof::{Hash, FriProof};
|
||||
use crate::field::fft::fft;
|
||||
use crate::gadgets::merkle_proofs::MerkleTree;
|
||||
|
||||
/// Somewhat arbitrary. Smaller values will increase delta, but with diminishing returns,
|
||||
/// while increasing L, potentially requiring more challenge points.
|
||||
const EPSILON: f64 = 0.01;
|
||||
@ -10,6 +18,12 @@ struct FriConfig {
|
||||
/// a 4-to-1 reduction, then a 2-to-1 reduction. After these reductions, the reduced polynomial
|
||||
/// is sent directly.
|
||||
reduction_arity_bits: Vec<usize>,
|
||||
|
||||
/// Number of reductions in the FRI protocol. So if the original domain has size `2^n`,
|
||||
/// then the final domain will have size `2^(n-reduction_count)`.
|
||||
reduction_count: usize,
|
||||
|
||||
rate_bits: usize,
|
||||
}
|
||||
|
||||
fn fri_delta(rate_log: usize, conjecture: bool) -> f64 {
|
||||
@ -34,3 +48,61 @@ fn fri_l(codeword_len: usize, rate_log: usize, conjecture: bool) -> f64 {
|
||||
1.0 / (2.0 * EPSILON * rate.sqrt())
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Different arity + PoW.
|
||||
/// Performs a FRI round.
|
||||
fn fri_round<F: Field>(
|
||||
polynomial_coeffs: &PolynomialCoeffs<F>,
|
||||
polynomial_values: &PolynomialValues<F>,
|
||||
challenger: &mut Challenger<F>,
|
||||
config: &FriConfig,
|
||||
) -> FriProof<F> {
|
||||
let n = polynomial_values.values.len();
|
||||
assert_eq!(
|
||||
polynomial_coeffs.coeffs.len(),
|
||||
n
|
||||
);
|
||||
let mut trees = vec![MerkleTree::new(polynomial_values.values.iter().map(|&v| vec![v]).collect())];
|
||||
let mut root = trees.last().unwrap().root;
|
||||
let mut coeffs = polynomial_coeffs.clone();
|
||||
let mut values;
|
||||
|
||||
challenger.observe_hash(&root);
|
||||
|
||||
// Commit phase
|
||||
for _ in 0..config.reduction_count {
|
||||
let beta = challenger.get_challenge();
|
||||
coeffs = PolynomialCoeffs::new(
|
||||
coeffs
|
||||
.coeffs
|
||||
.chunks_exact(2)
|
||||
.map(|chunk| chunk[0] + beta * chunk[1])
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
values = fft(coeffs.clone().lde(config.rate_bits));
|
||||
|
||||
let tree = MerkleTree::new(values.values.iter().map(|&v| vec![v]).collect());
|
||||
challenger.observe_hash(&tree.root);
|
||||
trees.push(tree);
|
||||
}
|
||||
|
||||
// Query phase
|
||||
let mut merkle_proofs = Vec::new();
|
||||
let mut evals = Vec::new();
|
||||
for i in 0..config.reduction_count {
|
||||
let x = challenger.get_challenge();
|
||||
let x_index = (x.to_canonical_u64() as usize) % n;
|
||||
let n2 = n>>1;
|
||||
evals.extend(std::array::IntoIter::new([polynomial_values.values[x_index], polynomial_values.values[n2 + x_index]]));
|
||||
merkle_proofs.extend(std::array::IntoIter::new([trees[i].prove(x_index), trees[i].prove(n2 + x_index)]));
|
||||
}
|
||||
|
||||
FriProof {
|
||||
commit_phase_merkle_roots: trees.iter().map(|t| t.root).collect(),
|
||||
initial_merkle_proofs: vec![],
|
||||
intermediate_merkle_proofs: merkle_proofs,
|
||||
final_poly: coeffs
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
use crate::circuit_builder::CircuitBuilder;
|
||||
use crate::field::field::Field;
|
||||
use crate::hash::{compress, hash_n_to_hash};
|
||||
use crate::proof::{Hash, HashTarget};
|
||||
use crate::target::Target;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MerkleProof<F: Field> {
|
||||
/// The Merkle digest of each sibling subtree, staying from the bottommost layer.
|
||||
pub siblings: Vec<Hash<F>>,
|
||||
@ -13,6 +15,55 @@ pub struct MerkleProofTarget {
|
||||
pub siblings: Vec<HashTarget>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MerkleTree<F: Field> {
|
||||
/// The data in the leaves of the Merkle tree.
|
||||
pub leaves: Vec<Vec<F>>,
|
||||
|
||||
/// The layers of hashes in the tree. The first layer is the one at the bottom.
|
||||
pub layers: Vec<Vec<Hash<F>>>,
|
||||
|
||||
/// The Merkle root.
|
||||
pub root: Hash<F>,
|
||||
}
|
||||
|
||||
impl<F: Field> MerkleTree<F> {
|
||||
pub fn new(leaves: Vec<Vec<F>>) -> Self {
|
||||
let mut layers = vec![leaves.iter().map(|l| hash_n_to_hash(l.clone(), false)).collect::<Vec<_>>()];
|
||||
loop {
|
||||
match layers.last() {
|
||||
Some(l) if l.len() > 1 => {
|
||||
layers.push(l.chunks(2).map(|chunk| compress(chunk[0], chunk[1])).collect::<Vec<_>>());
|
||||
},
|
||||
_ => break
|
||||
}
|
||||
}
|
||||
let root = layers.pop().unwrap()[0];
|
||||
Self {
|
||||
leaves,
|
||||
layers,
|
||||
root
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Create a Merkle proof from a leaf index.
|
||||
pub fn prove(&self, leaf_index: usize) -> MerkleProof<F> {
|
||||
MerkleProof {
|
||||
siblings: self.layers
|
||||
.iter()
|
||||
.scan(leaf_index, |acc, layer| {
|
||||
let index = *acc ^ 1;
|
||||
*acc >>= 1;
|
||||
Some(layer[index])
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Verifies that the given leaf data is present at the given index in the Merkle tree with the
|
||||
/// given root.
|
||||
pub(crate) fn verify_merkle_proof<F: Field>(
|
||||
@ -20,8 +71,18 @@ pub(crate) fn verify_merkle_proof<F: Field>(
|
||||
leaf_index: usize,
|
||||
merkle_root: Hash<F>,
|
||||
proof: MerkleProof<F>,
|
||||
) {
|
||||
todo!()
|
||||
) -> bool {
|
||||
let mut index = leaf_index;
|
||||
let mut h = hash_n_to_hash(leaf_data, false);
|
||||
for s in &proof.siblings {
|
||||
h = if index & 1 == 0 {
|
||||
compress(h, *s)
|
||||
} else {
|
||||
compress(*s, h)
|
||||
};
|
||||
index >>= 1;
|
||||
}
|
||||
h == merkle_root
|
||||
}
|
||||
|
||||
impl<F: Field> CircuitBuilder<F> {
|
||||
@ -37,3 +98,21 @@ impl<F: Field> CircuitBuilder<F> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::field::crandall_field::CrandallField;
|
||||
|
||||
#[test]
|
||||
fn test_merkle_proofs() {
|
||||
type F = CrandallField;
|
||||
let num_leaves = 128;
|
||||
let leaves = (0..num_leaves).map(|_| vec![F::rand()]).collect::<Vec<_>>();
|
||||
let tree = MerkleTree::new(leaves);
|
||||
for i in 0..num_leaves {
|
||||
let proof = tree.prove(i);
|
||||
assert!(verify_merkle_proof(tree.leaves[i].clone(),i, tree.root, proof));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
use crate::field::field::Field;
|
||||
use crate::target::Target;
|
||||
use crate::gadgets::merkle_proofs::{MerkleProofTarget, MerkleProof};
|
||||
use crate::polynomial::polynomial::PolynomialCoeffs;
|
||||
|
||||
/// Represents a ~256 bit hash output.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct Hash<F: Field> {
|
||||
pub(crate) elements: [F; 4],
|
||||
}
|
||||
@ -60,7 +61,7 @@ pub struct FriProof<F: Field> {
|
||||
/// Merkle proofs for the reduced polynomials that were sent in the commit phase.
|
||||
pub intermediate_merkle_proofs: Vec<MerkleProof<F>>,
|
||||
/// The final polynomial in coefficient form.
|
||||
pub final_poly: Vec<F>,
|
||||
pub final_poly: PolynomialCoeffs<F>,
|
||||
}
|
||||
|
||||
/// Represents a single FRI query, i.e. a path through the reduction tree.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user