From b7b394f3e59b400485795699af6131c3e36a2873 Mon Sep 17 00:00:00 2001 From: Anton Suprunchuk Date: Sat, 23 Oct 2021 18:17:02 +0300 Subject: [PATCH] feat: custom proof serializers (#9) --- src/lib.rs | 2 + src/merkle_proof.rs | 142 +++++++++++++----- src/merkle_tree.rs | 4 +- src/partial_tree.rs | 3 +- src/proof_serializers/direct_hashes_order.rs | 45 ++++++ .../merkle_proof_serializer.rs | 16 ++ src/proof_serializers/mod.rs | 14 ++ src/proof_serializers/reverse_hashes_order.rs | 49 ++++++ 8 files changed, 230 insertions(+), 45 deletions(-) create mode 100644 src/proof_serializers/direct_hashes_order.rs create mode 100644 src/proof_serializers/merkle_proof_serializer.rs create mode 100644 src/proof_serializers/mod.rs create mode 100644 src/proof_serializers/reverse_hashes_order.rs diff --git a/src/lib.rs b/src/lib.rs index 53d4781..703c63d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -133,6 +133,7 @@ pub use hasher::Hasher; pub use merkle_proof::MerkleProof; pub use merkle_tree::MerkleTree; pub use partial_tree::PartialTree; +pub use proof_serializers::MerkleProofSerializer; mod error; mod hasher; @@ -143,3 +144,4 @@ mod partial_tree; pub mod utils; pub mod algorithms; +pub mod proof_serializers; diff --git a/src/merkle_proof.rs b/src/merkle_proof.rs index 649ca06..7aae5a9 100644 --- a/src/merkle_proof.rs +++ b/src/merkle_proof.rs @@ -1,9 +1,11 @@ +use crate::{ + error::Error, + partial_tree::PartialTree, + proof_serializers::{DirectHashesOrder, MerkleProofSerializer}, + utils, Hasher, +}; use std::convert::TryFrom; -use crate::error::Error; -use crate::partial_tree::PartialTree; -use crate::{utils, Hasher}; - /// [`MerkleProof`] is used to parse, verify, calculate a root for Merkle proofs. /// /// ## Usage @@ -52,7 +54,8 @@ impl MerkleProof { MerkleProof { proof_hashes } } - /// Creates a proof from a slice of bytes + /// Creates a proof from a slice of bytes, direct hashes order. If you're looking for + /// other options of bytes to proof deserialization, take a look at [`MerkleProof::deserialize`] /// /// ## Examples /// @@ -78,7 +81,51 @@ impl MerkleProof { /// /// ['Error`]: crate::Error pub fn from_bytes(bytes: &[u8]) -> Result { - Self::try_from(bytes) + Self::deserialize::(bytes) + } + + /// Creates a proof from a slice of bytes. Bytes can be serialized in different ways, so this + /// method requires specifying a serializer. You can take a look at built-in serializers at + /// [`crate::proof_serializers`]. If the serializer you're looking for is not there, it is + /// easy to make your own - take a look at the [`MerkleProofSerializer`] trait. + /// + /// ## Examples + /// + /// ``` + /// # use std::convert::TryFrom; + /// # use rs_merkle::{MerkleProof, algorithms::Sha256, proof_serializers}; + /// # fn main() -> Result<(), Box> { + /// let proof_bytes: Vec = vec![ + /// 229, 160, 31, 238, 20, 224, 237, 92, 72, 113, 79, 34, 24, 15, 37, 173, 131, 101, 181, + /// 63, 151, 121, 247, 157, 196, 163, 215, 233, 57, 99, 249, 74, + /// 37, 47, 16, 200, 54, 16, 235, 202, 26, 5, 156, 11, 174, 130, 85, 235, 162, 249, 91, 228, + /// 209, 215, 188, 250, 137, 215, 36, 138, 130, 217, 241, 17, + /// 46, 125, 44, 3, 169, 80, 122, 226, 101, 236, 245, 181, 53, 104, 133, 165, 51, 147, 162, + /// 2, 157, 36, 19, 148, 153, 114, 101, 161, 162, 90, 239, 198, + /// ]; + /// + /// let proof: MerkleProof = MerkleProof + /// ::deserialize::(proof_bytes.as_slice())?; + /// + /// assert_eq!(proof.serialize::(), &[ + /// 46, 125, 44, 3, 169, 80, 122, 226, 101, 236, 245, 181, 53, 104, 133, 165, 51, 147, 162, + /// 2, 157, 36, 19, 148, 153, 114, 101, 161, 162, 90, 239, 198, + /// 37, 47, 16, 200, 54, 16, 235, 202, 26, 5, 156, 11, 174, 130, 85, 235, 162, 249, 91, 228, + /// 209, 215, 188, 250, 137, 215, 36, 138, 130, 217, 241, 17, + /// 229, 160, 31, 238, 20, 224, 237, 92, 72, 113, 79, 34, 24, 15, 37, 173, 131, 101, 181, + /// 63, 151, 121, 247, 157, 196, 163, 215, 233, 57, 99, 249, 74, + /// ]); + /// # Ok(()) + /// # } + /// ``` + /// + /// ## Errors + /// + /// In case of a parsing error result will contain [`Error`] + /// + /// ['Error`]: crate::Error + pub fn deserialize(bytes: &[u8]) -> Result { + S::deserialize(bytes) } /// Uses proof to verify that a given set of elements is contained in the original data @@ -318,9 +365,9 @@ impl MerkleProof { /// ## Important /// /// Please note that some applications may serialize proof differently, for example in reverse - /// order - from top to bottom, right to left. In that case, you'll need to do some - /// manipulations on the proof data manually. Raw proof hashes are available through - /// [`MerkleProof::proof_hashes`] + /// order - from top to bottom, right to left. In that case, you'll need to use another method - + /// [`MerkleProof::serialize`] with a custom serializer. Please consult + /// [`MerkleProof::serialize`] for more details. /// /// ## Examples /// @@ -356,13 +403,51 @@ impl MerkleProof { /// # } /// ``` pub fn to_bytes(&self) -> Vec { - let mut vectors: Vec> = self - .proof_hashes() - .iter() - .cloned() - .map(|hash| hash.into()) - .collect(); - vectors.drain(..).flatten().collect() + self.serialize::() + } + + /// Serializes proof hashes to a flat vector of bytes using a custom proof serializer. + /// The library includes some built-in proof serializers, check [`crate::proof_serializers`] + /// module to see what's available out of the box. If none fit your needs, you can easily + /// implement your own - check the [`MerkleProofSerializer`] trait for more details. + /// + /// ## Examples + /// + /// ``` + /// # use rs_merkle::{ + /// # MerkleTree, MerkleProof, algorithms::Sha256, Hasher, Error, utils, proof_serializers + /// # }; + /// # use std::convert::TryFrom; + /// # + /// # fn main() -> Result<(), Box> { + /// let leaf_values = ["a", "b", "c", "d", "e", "f"]; + /// let leaves: Vec<[u8; 32]> = leaf_values + /// .iter() + /// .map(|x| Sha256::hash(x.as_bytes())) + /// .collect(); + /// + /// let merkle_tree = MerkleTree::::from_leaves(&leaves); + /// let indices_to_prove = vec![3, 4]; + /// let leaves_to_prove = leaves.get(3..5).ok_or("can't get leaves to prove")?; + /// let merkle_proof = merkle_tree.proof(&indices_to_prove); + /// let merkle_root = merkle_tree.root().ok_or("couldn't get the merkle root")?; + /// + /// // Serialize proof to pass it to the client over the network + /// let proof_bytes = merkle_proof.serialize::(); + /// + /// assert_eq!(proof_bytes, vec![ + /// 229, 160, 31, 238, 20, 224, 237, 92, 72, 113, 79, 34, 24, 15, 37, 173, 131, 101, 181, + /// 63, 151, 121, 247, 157, 196, 163, 215, 233, 57, 99, 249, 74, + /// 37, 47, 16, 200, 54, 16, 235, 202, 26, 5, 156, 11, 174, 130, 85, 235, 162, 249, 91, 228, + /// 209, 215, 188, 250, 137, 215, 36, 138, 130, 217, 241, 17, + /// 46, 125, 44, 3, 169, 80, 122, 226, 101, 236, 245, 181, 53, 104, 133, 165, 51, 147, 162, + /// 2, 157, 36, 19, 148, 153, 114, 101, 161, 162, 90, 239, 198, + /// ]); + /// # Ok(()) + /// # } + /// ``` + pub fn serialize(&self) -> Vec { + S::serialize(self) } } @@ -416,29 +501,6 @@ impl TryFrom<&[u8]> for MerkleProof { /// let proof_result = MerkleProof::::try_from(proof_bytes.as_slice()); /// ``` fn try_from(bytes: &[u8]) -> Result { - let hash_size = T::hash_size(); - - if bytes.len() % hash_size != 0 { - return Err(Error::wrong_proof_size(bytes.len(), hash_size)); - } - - let hashes_count = bytes.len() / hash_size; - let mut proof_hashes_slices = Vec::::with_capacity(hashes_count); - - for i in 0..hashes_count { - let slice_start = i * hash_size; - let slice_end = (i + 1) * hash_size; - let slice = bytes - .get(slice_start..slice_end) - .ok_or_else(Error::vec_to_hash_conversion_error)?; - let vec = - Vec::::try_from(slice).map_err(|_| Error::vec_to_hash_conversion_error())?; - match T::Hash::try_from(vec) { - Ok(val) => proof_hashes_slices.push(val), - Err(_) => return Err(Error::vec_to_hash_conversion_error()), - } - } - - Ok(Self::new(proof_hashes_slices)) + DirectHashesOrder::deserialize(bytes) } } diff --git a/src/merkle_tree.rs b/src/merkle_tree.rs index 56d1ae3..3e5f294 100644 --- a/src/merkle_tree.rs +++ b/src/merkle_tree.rs @@ -1,6 +1,4 @@ -use crate::partial_tree::PartialTree; -use crate::utils::indices; -use crate::{utils, Hasher, MerkleProof}; +use crate::{partial_tree::PartialTree, utils, utils::indices, Hasher, MerkleProof}; /// [`MerkleTree`] is a Merkle Tree that is well suited for both basic and advanced usage. /// diff --git a/src/partial_tree.rs b/src/partial_tree.rs index 2bb1234..b8a87ec 100644 --- a/src/partial_tree.rs +++ b/src/partial_tree.rs @@ -1,5 +1,4 @@ -use crate::error::Error; -use crate::{utils, Hasher}; +use crate::{error::Error, utils, Hasher}; type PartialTreeLayer = Vec<(usize, H)>; diff --git a/src/proof_serializers/direct_hashes_order.rs b/src/proof_serializers/direct_hashes_order.rs new file mode 100644 index 0000000..f6f62ed --- /dev/null +++ b/src/proof_serializers/direct_hashes_order.rs @@ -0,0 +1,45 @@ +use crate::{Error, Hasher, MerkleProof, MerkleProofSerializer}; +use std::convert::TryFrom; + +/// Serializes proof data to bytes with a direct hash order - hashes are concatenated from +/// left to right, bottom to top. +pub struct DirectHashesOrder {} + +impl MerkleProofSerializer for DirectHashesOrder { + fn serialize(proof: &MerkleProof) -> Vec { + let mut vectors: Vec> = proof + .proof_hashes() + .iter() + .cloned() + .map(|hash| hash.into()) + .collect(); + vectors.drain(..).flatten().collect() + } + + fn deserialize(bytes: &[u8]) -> Result, Error> { + let hash_size = T::hash_size(); + + if bytes.len() % hash_size != 0 { + return Err(Error::wrong_proof_size(bytes.len(), hash_size)); + } + + let hashes_count = bytes.len() / hash_size; + let mut proof_hashes_slices = Vec::::with_capacity(hashes_count); + + for i in 0..hashes_count { + let slice_start = i * hash_size; + let slice_end = (i + 1) * hash_size; + let slice = bytes + .get(slice_start..slice_end) + .ok_or_else(Error::vec_to_hash_conversion_error)?; + let vec = + Vec::::try_from(slice).map_err(|_| Error::vec_to_hash_conversion_error())?; + match T::Hash::try_from(vec) { + Ok(val) => proof_hashes_slices.push(val), + Err(_) => return Err(Error::vec_to_hash_conversion_error()), + } + } + + Ok(MerkleProof::new(proof_hashes_slices)) + } +} diff --git a/src/proof_serializers/merkle_proof_serializer.rs b/src/proof_serializers/merkle_proof_serializer.rs new file mode 100644 index 0000000..52143fc --- /dev/null +++ b/src/proof_serializers/merkle_proof_serializer.rs @@ -0,0 +1,16 @@ +use crate::{Error, Hasher, MerkleProof}; + +/// Trait representing a Merkle proof serializer. Used in [`MerkleProof::serialize`] and +/// [`MerkleProof::deserialize`]. +/// +/// The library provides some built-in implementations of this trait - check +/// [`proof_serializers`] module. +/// +/// [`proof_serializers`]: crate::proof_serializers +pub trait MerkleProofSerializer { + /// Serialize data from [`MerkleProof`] into a binary + fn serialize(proof: &MerkleProof) -> Vec; + + /// Deserialize data produced by [`MerkleProofSerializer::serialize`] back into [`MerkleProof`] + fn deserialize(bytes: &[u8]) -> Result, Error>; +} diff --git a/src/proof_serializers/mod.rs b/src/proof_serializers/mod.rs new file mode 100644 index 0000000..b690160 --- /dev/null +++ b/src/proof_serializers/mod.rs @@ -0,0 +1,14 @@ +//! This module contains built-in implementations of the [`MerkleProofSerializer`] trait. +//! Serializers are used in [`MerkleProof::serialize`] and [`MerkleProof::deserialize`] +//! +//! [`MerkleProofSerializer`]: crate::MerkleProofSerializer +//! [`MerkleProof::serialize`]: crate::MerkleProof::serialize +//! [`MerkleProof::deserialize`]: crate::MerkleProof::deserialize + +mod direct_hashes_order; +mod merkle_proof_serializer; +mod reverse_hashes_order; + +pub use direct_hashes_order::DirectHashesOrder; +pub use merkle_proof_serializer::MerkleProofSerializer; +pub use reverse_hashes_order::ReverseHashesOrder; diff --git a/src/proof_serializers/reverse_hashes_order.rs b/src/proof_serializers/reverse_hashes_order.rs new file mode 100644 index 0000000..f331a31 --- /dev/null +++ b/src/proof_serializers/reverse_hashes_order.rs @@ -0,0 +1,49 @@ +use crate::{Error, Hasher, MerkleProof, MerkleProofSerializer}; +use std::convert::TryFrom; + +/// Serializes proof data to bytes with a reverse hash order - hashes are concatenated from +/// top to bottom, right to left. +pub struct ReverseHashesOrder {} + +impl MerkleProofSerializer for ReverseHashesOrder { + fn serialize(proof: &MerkleProof) -> Vec { + let mut hashes: Vec> = proof + .proof_hashes() + .iter() + .cloned() + .map(|hash| hash.into()) + .collect(); + + hashes.reverse(); + hashes.drain(..).flatten().collect() + } + + fn deserialize(bytes: &[u8]) -> Result, Error> { + let hash_size = T::hash_size(); + + if bytes.len() % hash_size != 0 { + return Err(Error::wrong_proof_size(bytes.len(), hash_size)); + } + + let hashes_count = bytes.len() / hash_size; + let mut proof_hashes_slices = Vec::::with_capacity(hashes_count); + + for i in 0..hashes_count { + let slice_start = i * hash_size; + let slice_end = (i + 1) * hash_size; + let slice = bytes + .get(slice_start..slice_end) + .ok_or_else(Error::vec_to_hash_conversion_error)?; + let vec = + Vec::::try_from(slice).map_err(|_| Error::vec_to_hash_conversion_error())?; + match T::Hash::try_from(vec) { + Ok(val) => proof_hashes_slices.push(val), + Err(_) => return Err(Error::vec_to_hash_conversion_error()), + } + } + + proof_hashes_slices.reverse(); + + Ok(MerkleProof::new(proof_hashes_slices)) + } +}