diff --git a/Cargo.lock b/Cargo.lock index 2c8d2aa..8833dff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2387,7 +2387,7 @@ dependencies = [ [[package]] name = "pmtree" version = "1.0.0" -source = "git+https://github.com/Rate-Limiting-Nullifier/pmtree?rev=bf27d4273b34a7f4ec3c77cdf67fb17a5602504a#bf27d4273b34a7f4ec3c77cdf67fb17a5602504a" +source = "git+https://github.com/vacp2p/pmtree?rev=8b005bb44a4c51b56638ad6057b8bc90629fca59#8b005bb44a4c51b56638ad6057b8bc90629fca59" dependencies = [ "rayon", ] diff --git a/rln/src/ffi.rs b/rln/src/ffi.rs index ab2b9ff..eb0df6a 100644 --- a/rln/src/ffi.rs +++ b/rln/src/ffi.rs @@ -385,6 +385,22 @@ pub extern "C" fn recover_id_secret( ) } +//////////////////////////////////////////////////////// +// Persistent metadata APIs +//////////////////////////////////////////////////////// + +#[allow(clippy::not_unsafe_ptr_arg_deref)] +#[no_mangle] +pub extern "C" fn set_metadata(ctx: *mut RLN, input_buffer: *const Buffer) -> bool { + call!(ctx, set_metadata, input_buffer) +} + +#[allow(clippy::not_unsafe_ptr_arg_deref)] +#[no_mangle] +pub extern "C" fn get_metadata(ctx: *const RLN, output_buffer: *mut Buffer) -> bool { + call_with_output_arg!(ctx, get_metadata, output_buffer) +} + #[allow(clippy::not_unsafe_ptr_arg_deref)] #[no_mangle] pub extern "C" fn hash(input_buffer: *const Buffer, output_buffer: *mut Buffer) -> bool { diff --git a/rln/src/pm_tree_adapter.rs b/rln/src/pm_tree_adapter.rs index 40f13f8..2ec58b2 100644 --- a/rln/src/pm_tree_adapter.rs +++ b/rln/src/pm_tree_adapter.rs @@ -7,11 +7,15 @@ use std::collections::HashSet; use std::fmt::Debug; use std::path::PathBuf; use std::str::FromStr; -use utils::pmtree::Hasher; +use utils::pmtree::{Database, Hasher}; use utils::*; +const METADATA_KEY: [u8; 8] = *b"metadata"; + pub struct PmTree { tree: pmtree::MerkleTree, + // metadata that an application may use to store additional information + metadata: Vec, } pub struct PmTreeProof { @@ -135,7 +139,10 @@ impl ZerokitMerkleTree for PmTree { Err(_) => pmtree::MerkleTree::new(depth, config.0)?, }; - Ok(PmTree { tree }) + Ok(PmTree { + tree, + metadata: Vec::new(), + }) } fn depth(&self) -> usize { @@ -238,6 +245,25 @@ impl ZerokitMerkleTree for PmTree { fn compute_root(&mut self) -> Result> { Ok(self.tree.root()) } + + fn set_metadata(&mut self, metadata: &[u8]) -> Result<()> { + self.tree.db.put(METADATA_KEY, metadata.to_vec())?; + self.metadata = metadata.to_vec(); + Ok(()) + } + + fn metadata(&self) -> Result> { + if !self.metadata.is_empty() { + return Ok(self.metadata.clone()); + } + // if empty, try searching the db + let data = self.tree.db.get(METADATA_KEY)?; + + if data.is_none() { + return Err(Report::msg("metadata does not exist")); + } + Ok(data.unwrap()) + } } impl ZerokitMerkleProof for PmTreeProof { diff --git a/rln/src/public.rs b/rln/src/public.rs index 90d679d..a0ba94a 100644 --- a/rln/src/public.rs +++ b/rln/src/public.rs @@ -437,6 +437,43 @@ impl RLN<'_> { Ok(()) } + /// Sets some metadata that a consuming application may want to store in the RLN object. + /// This metadata is not used by the RLN module. + /// + /// Input values are: + /// - `metadata`: a byte vector containing the metadata + /// + /// Example + /// + /// ``` + /// let metadata = b"some metadata"; + /// rln.set_metadata(metadata).unwrap(); + /// ``` + pub fn set_metadata(&mut self, metadata: &[u8]) -> Result<()> { + self.tree.set_metadata(metadata)?; + Ok(()) + } + + /// Returns the metadata stored in the RLN object. + /// + /// Output values are: + /// - `output_data`: a writer receiving the serialization of the metadata + /// + /// Example + /// + /// ``` + /// use std::io::Cursor; + /// + /// let mut buffer = Cursor::new(Vec::::new()); + /// rln.get_metadata(&mut buffer).unwrap(); + /// let metadata = buffer.into_inner(); + /// ``` + pub fn get_metadata(&self, mut output_data: W) -> Result<()> { + let metadata = self.tree.metadata()?; + output_data.write_all(&metadata)?; + Ok(()) + } + /// Returns the Merkle tree root /// /// Output values are: @@ -1882,4 +1919,22 @@ mod test { let (received_leaf, _) = bytes_le_to_fr(output_buffer.into_inner().as_ref()); assert_eq!(received_leaf, leaf); } + + #[test] + fn test_metadata() { + let tree_height = TEST_TREE_HEIGHT; + + let input_buffer = + Cursor::new(json!({ "resources_folder": TEST_RESOURCES_FOLDER }).to_string()); + let mut rln = RLN::new(tree_height, input_buffer).unwrap(); + + let arbitrary_metadata: &[u8] = b"block_number:200000"; + rln.set_metadata(arbitrary_metadata).unwrap(); + + let mut buffer = Cursor::new(Vec::::new()); + rln.get_metadata(&mut buffer).unwrap(); + let received_metadata = buffer.into_inner(); + + assert_eq!(arbitrary_metadata, received_metadata); + } } diff --git a/rln/tests/ffi.rs b/rln/tests/ffi.rs index 5a192b6..ec5e0b6 100644 --- a/rln/tests/ffi.rs +++ b/rln/tests/ffi.rs @@ -1201,4 +1201,32 @@ mod test { // We check that the received id_commitment is the same as the one we inserted assert_eq!(received_id_commitment, id_commitment); } + + #[test] + fn test_metadata() { + // We create a RLN instance + let tree_height = TEST_TREE_HEIGHT; + + let mut rln_pointer = MaybeUninit::<*mut RLN>::uninit(); + let input_config = json!({ "resources_folder": TEST_RESOURCES_FOLDER }).to_string(); + let input_buffer = &Buffer::from(input_config.as_bytes()); + let success = new(tree_height, input_buffer, rln_pointer.as_mut_ptr()); + assert!(success, "RLN object creation failed"); + let rln_pointer = unsafe { &mut *rln_pointer.assume_init() }; + + let seed_bytes: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + let input_buffer = &Buffer::from(seed_bytes); + + let success = set_metadata(rln_pointer, input_buffer); + assert!(success, "set_metadata call failed"); + + let mut output_buffer = MaybeUninit::::uninit(); + let success = get_metadata(rln_pointer, output_buffer.as_mut_ptr()); + assert!(success, "get_metadata call failed"); + + let output_buffer = unsafe { output_buffer.assume_init() }; + let result_data = <&[u8]>::from(&output_buffer).to_vec(); + + assert_eq!(result_data, seed_bytes.to_vec()); + } } diff --git a/utils/Cargo.toml b/utils/Cargo.toml index ff83e24..cea96c0 100644 --- a/utils/Cargo.toml +++ b/utils/Cargo.toml @@ -11,7 +11,7 @@ bench = false ark-ff = { version = "=0.4.1", default-features = false, features = ["asm"] } num-bigint = { version = "=0.4.3", default-features = false, features = ["rand"] } color-eyre = "=0.6.2" -pmtree = { git = "https://github.com/Rate-Limiting-Nullifier/pmtree", rev = "bf27d4273b34a7f4ec3c77cdf67fb17a5602504a", optional = true} +pmtree = { git = "https://github.com/vacp2p/pmtree", rev = "8b005bb44a4c51b56638ad6057b8bc90629fca59", optional = true} sled = "=0.34.7" serde = "1.0.44" diff --git a/utils/src/merkle_tree/full_merkle_tree.rs b/utils/src/merkle_tree/full_merkle_tree.rs index cad0a34..187423a 100644 --- a/utils/src/merkle_tree/full_merkle_tree.rs +++ b/utils/src/merkle_tree/full_merkle_tree.rs @@ -29,6 +29,9 @@ pub struct FullMerkleTree { // The next available (i.e., never used) tree index. Equivalently, the number of leaves added to the tree // (deletions leave next_index unchanged) next_index: usize, + + // metadata that an application may use to store additional information + metadata: Vec, } /// Element of a Merkle proof @@ -94,6 +97,7 @@ where cached_nodes, nodes, next_index, + metadata: Vec::new(), }) } @@ -234,6 +238,15 @@ where fn compute_root(&mut self) -> Result> { Ok(self.root()) } + + fn set_metadata(&mut self, metadata: &[u8]) -> Result<()> { + self.metadata = metadata.to_vec(); + Ok(()) + } + + fn metadata(&self) -> Result> { + Ok(self.metadata.to_vec()) + } } impl FullMerkleTree diff --git a/utils/src/merkle_tree/merkle_tree.rs b/utils/src/merkle_tree/merkle_tree.rs index a6e6fed..e8ad633 100644 --- a/utils/src/merkle_tree/merkle_tree.rs +++ b/utils/src/merkle_tree/merkle_tree.rs @@ -63,6 +63,8 @@ pub trait ZerokitMerkleTree { fn delete(&mut self, index: usize) -> Result<()>; fn proof(&self, index: usize) -> Result; fn verify(&self, leaf: &FrOf, witness: &Self::Proof) -> Result; + fn set_metadata(&mut self, metadata: &[u8]) -> Result<()>; + fn metadata(&self) -> Result>; } pub trait ZerokitMerkleProof { diff --git a/utils/src/merkle_tree/optimal_merkle_tree.rs b/utils/src/merkle_tree/optimal_merkle_tree.rs index 3d39412..fd662f0 100644 --- a/utils/src/merkle_tree/optimal_merkle_tree.rs +++ b/utils/src/merkle_tree/optimal_merkle_tree.rs @@ -30,6 +30,9 @@ where // The next available (i.e., never used) tree index. Equivalently, the number of leaves added to the tree // (deletions leave next_index unchanged) next_index: usize, + + // metadata that an application may use to store additional information + metadata: Vec, } /// The Merkle proof @@ -76,6 +79,7 @@ where depth, nodes: HashMap::new(), next_index: 0, + metadata: Vec::new(), }) } @@ -216,6 +220,15 @@ where self.recalculate_from(0)?; Ok(self.root()) } + + fn set_metadata(&mut self, metadata: &[u8]) -> Result<()> { + self.metadata = metadata.to_vec(); + Ok(()) + } + + fn metadata(&self) -> Result> { + Ok(self.metadata.to_vec()) + } } impl OptimalMerkleTree