Skip to content

Commit

Permalink
add function for empty leaves
Browse files Browse the repository at this point in the history
  • Loading branch information
seemenkina committed May 16, 2024
1 parent 4931b25 commit 0e40b72
Show file tree
Hide file tree
Showing 11 changed files with 362 additions and 56 deletions.
6 changes: 6 additions & 0 deletions rln/benches/pmtree_benchmark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ pub fn pmtree_benchmark(c: &mut Criterion) {
tree.get(0).unwrap();
})
});

c.bench_function("Pmtree::get_empty_leaves_indices", |b| {
b.iter(|| {
tree.get_empty_leaves_indices();
})
});
}

criterion_group!(benches, pmtree_benchmark);
Expand Down
59 changes: 45 additions & 14 deletions rln/src/pm_tree_adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ const METADATA_KEY: [u8; 8] = *b"metadata";

pub struct PmTree {
tree: pmtree::MerkleTree<SledDB, PoseidonHash>,
/// The indices of leaves which are set into zero upto next_index.
/// Set to 0 if the leaf is empty and set to 1 in otherwise.
cached_leaves_indices: Vec<u8>,
// metadata that an application may use to store additional information
metadata: Vec<u8>,
}
Expand Down Expand Up @@ -143,6 +146,7 @@ impl ZerokitMerkleTree for PmTree {

Ok(PmTree {
tree,
cached_leaves_indices: vec![0; 1 << depth],
metadata: Vec::new(),
})
}
Expand All @@ -155,7 +159,7 @@ impl ZerokitMerkleTree for PmTree {
self.tree.capacity()
}

fn leaves_set(&mut self) -> usize {
fn leaves_set(&self) -> usize {
self.tree.leaves_set()
}

Expand All @@ -170,23 +174,41 @@ impl ZerokitMerkleTree for PmTree {
fn set(&mut self, index: usize, leaf: FrOf<Self::Hasher>) -> Result<()> {
self.tree
.set(index, leaf)
.map_err(|e| Report::msg(e.to_string()))
.map_err(|e| Report::msg(e.to_string()))?;
self.cached_leaves_indices[index] = 1;
Ok(())
}

fn set_range<I: IntoIterator<Item = FrOf<Self::Hasher>>>(
&mut self,
start: usize,
values: I,
) -> Result<()> {
let v = values.into_iter().collect::<Vec<_>>();
self.tree
.set_range(start, values)
.map_err(|e| Report::msg(e.to_string()))
.set_range(start, v.clone().into_iter())
.map_err(|e| Report::msg(e.to_string()))?;
for i in start..v.len() {
self.cached_leaves_indices[i] = 1
}
Ok(())
}

fn get(&self, index: usize) -> Result<FrOf<Self::Hasher>> {
self.tree.get(index).map_err(|e| Report::msg(e.to_string()))
}

fn get_empty_leaves_indices(&self) -> Vec<usize> {
let next_idx = self.leaves_set();
self.cached_leaves_indices
.iter()
.take(next_idx)
.enumerate()
.filter(|&(_, &v)| v == 0u8)
.map(|(idx, _)| idx)
.collect()
}

fn override_range<I: IntoIterator<Item = FrOf<Self::Hasher>>, J: IntoIterator<Item = usize>>(
&mut self,
start: usize,
Expand All @@ -201,7 +223,7 @@ impl ZerokitMerkleTree for PmTree {
(0, 0) => Err(Report::msg("no leaves or indices to be removed")),
(1, 0) => self.set(start, leaves[0]),
(0, 1) => self.delete(indices[0]),
(_, 0) => self.set_range_with_leaves(start, leaves),
(_, 0) => self.set_range(start, leaves),
(0, _) => self.remove_indices(&indices),
(_, _) => self.remove_indices_and_set_leaves(start, leaves, &indices),
}
Expand All @@ -216,7 +238,9 @@ impl ZerokitMerkleTree for PmTree {
fn delete(&mut self, index: usize) -> Result<()> {
self.tree
.delete(index)
.map_err(|e| Report::msg(e.to_string()))
.map_err(|e| Report::msg(e.to_string()))?;
self.cached_leaves_indices[index] = 0;
Ok(())
}

fn proof(&self, index: usize) -> Result<Self::Proof> {
Expand Down Expand Up @@ -261,12 +285,6 @@ type PmTreeHasher = <PmTree as ZerokitMerkleTree>::Hasher;
type FrOfPmTreeHasher = FrOf<PmTreeHasher>;

impl PmTree {
fn set_range_with_leaves(&mut self, start: usize, leaves: Vec<FrOfPmTreeHasher>) -> Result<()> {
self.tree
.set_range(start, leaves)
.map_err(|e| Report::msg(e.to_string()))
}

fn remove_indices(&mut self, indices: &[usize]) -> Result<()> {
let start = indices[0];
let end = indices.last().unwrap() + 1;
Expand All @@ -275,7 +293,12 @@ impl PmTree {

self.tree
.set_range(start, new_leaves)
.map_err(|e| Report::msg(e.to_string()))
.map_err(|e| Report::msg(e.to_string()))?;

for i in start..end {
self.cached_leaves_indices[i] = 0
}
Ok(())
}

fn remove_indices_and_set_leaves(
Expand All @@ -302,7 +325,15 @@ impl PmTree {

self.tree
.set_range(min_index, set_values)
.map_err(|e| Report::msg(e.to_string()))
.map_err(|e| Report::msg(e.to_string()))?;

for i in indices {
self.cached_leaves_indices[*i] = 0;
}
for i in min_index..(max_index - min_index) {
self.cached_leaves_indices[i] = 1
}
Ok(())
}
}

Expand Down
38 changes: 38 additions & 0 deletions rln/src/public.rs
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,44 @@ impl RLN<'_> {
Ok(())
}

/// Returns indices of leaves in the tree are set to zero (upto the final leaf that was set).
///
/// Output values are:
/// - `output_data`: a writer receiving the serialization of the indices of leaves.
///
/// Example
/// ```
/// use rln::circuit::Fr;
/// use rln::utils::*;
///
/// let start_index = 5;
/// let no_of_leaves = 256;
///
/// // We generate a vector of random leaves
/// let mut leaves: Vec<Fr> = Vec::new();
/// let mut rng = thread_rng();
/// for _ in 0..no_of_leaves {
/// let (_, id_commitment) = keygen();
/// let rate_commitment = poseidon_hash(&[id_commitment, 1.into()]);
/// leaves.push(rate_commitment);
/// }
///
/// // We add leaves in a batch into the tree
/// let mut buffer = Cursor::new(vec_fr_to_bytes_le(&leaves));
/// rln.set_leaves_from(index, &mut buffer).unwrap();
///
/// // Get indices of first empty leaves upto start_index
/// let mut buffer = Cursor::new(Vec::<u8>::new());
/// rln.get_empty_leaves_indices(&mut buffer).unwrap();
/// let idxs = bytes_le_to_vec_usize(&buffer.into_inner()).unwrap();
/// assert_eq!(idxs, [0, 1, 2, 3, 4]);
/// ```
pub fn get_empty_leaves_indices<W: Write>(&self, mut output_data: W) -> Result<()> {
let idxs = self.tree.get_empty_leaves_indices();
idxs.serialize_compressed(&mut output_data)?;
Ok(())
}

////////////////////////////////////////////////////////
// zkSNARK APIs
////////////////////////////////////////////////////////
Expand Down
13 changes: 13 additions & 0 deletions rln/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,19 @@ pub fn normalize_usize(input: usize) -> Vec<u8> {
normalized_usize
}

pub fn bytes_le_to_vec_usize(input: &[u8]) -> Result<Vec<usize>> {
let nof_elem = usize::try_from(u64::from_le_bytes(input[0..8].try_into()?))?;
if nof_elem == 0 {
Ok(vec![])
} else {
let elements: Vec<usize> = (&input[8..])
.chunks(8)
.map(|ch| usize::from_le_bytes(ch[0..8].try_into().unwrap()))
.collect();
Ok(elements)
}
}

// using for test
pub fn generate_input_buffer() -> Cursor<String> {
Cursor::new(json!({}).to_string())
Expand Down
62 changes: 60 additions & 2 deletions rln/tests/poseidon_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@

#[cfg(test)]
mod test {
use rln::circuit::*;
use rln::hashers::PoseidonHash;
use rln::{circuit::*, poseidon_tree::PoseidonTree};
use utils::{FullMerkleTree, OptimalMerkleTree, ZerokitMerkleProof, ZerokitMerkleTree};

#[test]
/// The test is checked correctness for `FullMerkleTree` and `OptimalMerkleTree` with Poseidon hash
// The test is checked correctness for `FullMerkleTree` and `OptimalMerkleTree` with Poseidon hash
fn test_zerokit_merkle_implementations() {
let sample_size = 100;
let leaves: Vec<Fr> = (0..sample_size).map(|s| Fr::from(s)).collect();
Expand All @@ -33,4 +33,62 @@ mod test {

assert_eq!(tree_full_root, tree_opt_root);
}

#[test]
fn test_subtree_root() {
let depth = 4;
let nof_leaves: usize = 1 << (depth - 1);

let mut tree = PoseidonTree::default(depth).unwrap();
let leaves: Vec<Fr> = (0..nof_leaves).map(|s| Fr::from(s as i32)).collect();

// check set_range
let _ = tree.set_range(0, leaves.clone());
assert!(tree.get_empty_leaves_indices().is_empty());

let mut vec_idxs = Vec::new();
// check delete function
for i in 0..nof_leaves {
vec_idxs.push(i);
let _ = tree.delete(i);
assert_eq!(tree.get_empty_leaves_indices(), vec_idxs);
}
// check set function
for i in (0..nof_leaves).rev() {
vec_idxs.pop();
let _ = tree.set(i, leaves[i]);
assert_eq!(tree.get_empty_leaves_indices(), vec_idxs);
}

// check remove_indices_and_set_leaves inside override_range function
assert!(tree.get_empty_leaves_indices().is_empty());
let leaves_2: Vec<Fr> = (0..2).map(|s| Fr::from(s as i32)).collect();
tree.override_range(0, leaves_2.clone(), [0, 1, 2, 3])
.unwrap();
assert_eq!(tree.get_empty_leaves_indices(), vec![2, 3]);

// check remove_indices inside override_range function
tree.override_range(0, [], [0, 1]).unwrap();
assert_eq!(tree.get_empty_leaves_indices(), vec![0, 1, 2, 3]);

// check set_range inside override_range function
tree.override_range(0, leaves_2.clone(), []).unwrap();
assert_eq!(tree.get_empty_leaves_indices(), vec![2, 3]);

let leaves_4: Vec<Fr> = (0..4).map(|s| Fr::from(s as i32)).collect();
// check if the indexes for write and delete are the same
tree.override_range(0, leaves_4.clone(), [0, 1, 2, 3])
.unwrap();
assert!(tree.get_empty_leaves_indices().is_empty());

// check if indexes for deletion are before indexes for overwriting
// tree.override_range(4, leaves_4.clone(), [0, 1, 2, 3])
// .unwrap();
// assert_eq!(tree.get_empty_leaves_indices(), vec![0, 1, 2, 3]);

// check if the indices for write and delete do not overlap completely
// tree.override_range(2, leaves_4.clone(), [0, 1, 2, 3])
// .unwrap();
// assert_eq!(tree.get_empty_leaves_indices(), vec![0, 1]);
}
}
12 changes: 12 additions & 0 deletions rln/tests/public.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,22 @@ mod test {
let id_commitment = utils_poseidon_hash(&vec![identity_secret_hash]);
let rate_commitment = utils_poseidon_hash(&[id_commitment, user_message_limit.into()]);

// check that leaves indices is empty
let mut buffer = Cursor::new(Vec::<u8>::new());
rln.get_empty_leaves_indices(&mut buffer).unwrap();
let idxs = bytes_le_to_vec_usize(&buffer.into_inner()).unwrap();
assert!(idxs.is_empty());

// We pass rate_commitment as Read buffer to RLN's set_leaf
let mut buffer = Cursor::new(fr_to_bytes_le(&rate_commitment));
rln.set_leaf(leaf_index, &mut buffer).unwrap();

// check that leaves before leaf_index is set to zero
let mut buffer = Cursor::new(Vec::<u8>::new());
rln.get_empty_leaves_indices(&mut buffer).unwrap();
let idxs = bytes_le_to_vec_usize(&buffer.into_inner()).unwrap();
assert_eq!(idxs, [0, 1, 2]);

// We check correct computation of the root
let mut buffer = Cursor::new(Vec::<u8>::new());
rln.get_root(&mut buffer).unwrap();
Expand Down
12 changes: 12 additions & 0 deletions utils/benches/merkle_tree_benchmark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@ pub fn optimal_merkle_tree_benchmark(c: &mut Criterion) {
tree.get(0).unwrap();
})
});

c.bench_function("OptimalMerkleTree::get_empty_leaves_indices", |b| {
b.iter(|| {
tree.get_empty_leaves_indices();
})
});
}

pub fn full_merkle_tree_benchmark(c: &mut Criterion) {
Expand Down Expand Up @@ -125,6 +131,12 @@ pub fn full_merkle_tree_benchmark(c: &mut Criterion) {
tree.get(0).unwrap();
})
});

c.bench_function("FullMerkleTree::get_empty_leaves_indices", |b| {
b.iter(|| {
tree.get_empty_leaves_indices();
})
});
}

criterion_group!(
Expand Down
Loading

0 comments on commit 0e40b72

Please sign in to comment.