plonky2/src/hash/path_compression.rs

144 lines
5.2 KiB
Rust
Raw Normal View History

2021-09-20 14:37:28 +02:00
use std::collections::HashMap;
use num::Integer;
use crate::field::field_types::{Field, RichField};
use crate::hash::hashing::{compress, hash_or_noop};
use crate::hash::merkle_proofs::MerkleProof;
/// Compress multiple Merkle proofs on the same tree by removing redundancy in the Merkle paths.
pub(crate) fn compress_merkle_proofs<F: Field>(
cap_height: usize,
2021-09-20 17:34:52 +02:00
indices: &[usize],
proofs: &[MerkleProof<F>],
2021-09-20 14:37:28 +02:00
) -> Vec<MerkleProof<F>> {
assert!(!proofs.is_empty());
2021-09-20 17:34:52 +02:00
let height = cap_height + proofs[0].siblings.len();
2021-09-20 14:37:28 +02:00
let num_leaves = 1 << height;
let mut compressed_proofs = Vec::with_capacity(proofs.len());
// Holds the known nodes in the tree at a given time. The root is at index 1.
2021-09-21 08:37:23 +02:00
// Valid indices are 1 through n, and each element at index `i` has
// children at indices `2i` and `2i +1` its parent at index `floor(i 2)`.
2021-09-20 14:37:28 +02:00
let mut known = vec![false; 2 * num_leaves];
2021-09-20 17:34:52 +02:00
for &i in indices {
2021-09-20 14:37:28 +02:00
// The leaves are known.
2021-09-20 17:34:52 +02:00
known[i + num_leaves] = true;
2021-09-20 14:37:28 +02:00
}
// For each proof collect all the unknown proof elements.
2021-09-20 17:34:52 +02:00
for (&i, p) in indices.iter().zip(proofs) {
2021-09-20 14:37:28 +02:00
let mut compressed_proof = MerkleProof {
siblings: Vec::new(),
};
let mut index = i + num_leaves;
2021-09-20 17:34:52 +02:00
for &sibling in &p.siblings {
2021-09-20 14:37:28 +02:00
let sibling_index = index ^ 1;
if !known[sibling_index] {
// If the sibling is not yet known, add it to the proof and set it to known.
compressed_proof.siblings.push(sibling);
known[sibling_index] = true;
}
// Go up the tree and set the parent to known.
index >>= 1;
known[index] = true;
}
compressed_proofs.push(compressed_proof);
}
compressed_proofs
}
2021-09-20 17:58:25 +02:00
/// Decompress compressed Merkle proofs.
2021-09-20 14:37:28 +02:00
/// Note: The data and indices must be in the same order as in `compress_merkle_proofs`.
2021-09-20 17:34:52 +02:00
pub(crate) fn decompress_merkle_proofs<F: RichField>(
2021-09-20 14:37:28 +02:00
leaves_data: &[Vec<F>],
leaves_indices: &[usize],
compressed_proofs: &[MerkleProof<F>],
height: usize,
2021-09-20 17:34:52 +02:00
cap_height: usize,
2021-09-20 14:37:28 +02:00
) -> Vec<MerkleProof<F>> {
let num_leaves = 1 << height;
2021-09-21 13:12:46 -07:00
let compressed_proofs = compressed_proofs.to_vec();
2021-09-20 14:37:28 +02:00
let mut decompressed_proofs = Vec::with_capacity(compressed_proofs.len());
// Holds the already seen nodes in the tree along with their value.
let mut seen = HashMap::new();
for (&i, v) in leaves_indices.iter().zip(leaves_data) {
// Observe the leaves.
seen.insert(i + num_leaves, hash_or_noop(v.to_vec()));
}
// For every index, go up the tree by querying `seen` to get node values, or if they are unknown
2021-09-21 08:37:23 +02:00
// get them from the compressed proof.
for (&i, p) in leaves_indices.iter().zip(compressed_proofs) {
let mut compressed_siblings = p.siblings.into_iter();
2021-09-20 14:37:28 +02:00
let mut decompressed_proof = MerkleProof {
siblings: Vec::new(),
};
let mut index = i + num_leaves;
let mut current_digest = seen[&index];
for _ in 0..height - cap_height {
let sibling_index = index ^ 1;
2021-09-21 08:37:23 +02:00
// Get the value of the sibling node by querying it or getting it from the proof.
2021-09-20 14:37:28 +02:00
let h = *seen
.entry(sibling_index)
2021-09-21 08:37:23 +02:00
.or_insert_with(|| compressed_siblings.next().unwrap());
2021-09-20 14:37:28 +02:00
decompressed_proof.siblings.push(h);
// Update the current digest to the value of the parent.
current_digest = if index.is_even() {
compress(current_digest, h)
} else {
compress(h, current_digest)
};
// Observe the parent.
index >>= 1;
seen.insert(index, current_digest);
}
decompressed_proofs.push(decompressed_proof);
}
decompressed_proofs
}
#[cfg(test)]
mod tests {
use rand::{thread_rng, Rng};
use super::*;
use crate::field::crandall_field::CrandallField;
use crate::field::field_types::Field;
use crate::hash::merkle_tree::MerkleTree;
#[test]
fn test_path_compression() {
type F = CrandallField;
let h = 10;
let cap_height = 3;
let vs = (0..1 << h).map(|_| vec![F::rand()]).collect::<Vec<_>>();
let mt = MerkleTree::new(vs.clone(), cap_height);
let mut rng = thread_rng();
let k = rng.gen_range(1..=1 << h);
let indices = (0..k).map(|_| rng.gen_range(0..1 << h)).collect::<Vec<_>>();
2021-09-20 17:34:52 +02:00
let proofs = indices.iter().map(|&i| mt.prove(i)).collect::<Vec<_>>();
2021-09-20 14:37:28 +02:00
2021-09-20 17:34:52 +02:00
let compressed_proofs = compress_merkle_proofs(cap_height, &indices, &proofs);
let decompressed_proofs = decompress_merkle_proofs(
2021-09-20 14:37:28 +02:00
&indices.iter().map(|&i| vs[i].clone()).collect::<Vec<_>>(),
&indices,
&compressed_proofs,
h,
2021-09-20 17:34:52 +02:00
cap_height,
2021-09-20 14:37:28 +02:00
);
assert_eq!(proofs, decompressed_proofs);
let compressed_proof_bytes = serde_cbor::to_vec(&compressed_proofs).unwrap();
println!(
"Compressed proof length: {} bytes",
compressed_proof_bytes.len()
);
let proof_bytes = serde_cbor::to_vec(&proofs).unwrap();
println!("Proof length: {} bytes", proof_bytes.len());
}
}