Introduce SmtProof (#270)

* add conversion for `SmtLeaf`

* introduce `SmtProof` scaffolding

* implement `verify_membership()`

* SmtLeaf: knows its index

* `SmtLeaf::index`

* `SmtLeaf::get_value()` returns an Option

* fix `verify_membership()`

* impl `SmtProof::get`

* impl `into_parts()`

* `SmtProof::compute_root`

* use `SmtProof` in `Smt::open`

* `SmtLeaf` constructors

* Vec

* impl `Error` for `SmtLeafError`

* fix std Error

* move Word/Digest conversions to LeafIndex

* `SmtProof::new()` returns an error

* `SparseMerkleTree::path_and_leaf_to_opening`

* `SmtLeaf`: serializable/deserializable

* `SmtProof`: serializable/deserializable

* add tests for SmtLeaf serialization

* move `SmtLeaf` to submodule

* use constructors internally

* fix docs

* Add `Vec`

* add `Vec` to tests

* no_std use statements

* fmt

* `Errors`: make heading

* use `SMT_DEPTH`

* SmtLeaf single case: check leaf index

* Multiple case: check consistency with leaf index

* use `pub(super)` instead of `pub(crate)`

* use `pub(super)`

* `SmtLeaf`: add `num_entries()` accessor

* Fix `SmtLeaf` serialization

* improve leaf serialization tests
This commit is contained in:
Philippe Laferrière
2024-02-06 13:36:31 -05:00
committed by Bobbin Threadbare
parent 61a0764a61
commit e55b3ed2ce
8 changed files with 680 additions and 218 deletions

View File

@@ -0,0 +1,104 @@
use crate::utils::string::ToString;
use winter_utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable};
use super::{MerklePath, RpoDigest, SmtLeaf, SmtProofError, Word, SMT_DEPTH};
/// A proof which can be used to assert membership (or non-membership) of key-value pairs in a
/// [`super::Smt`].
///
/// The proof consists of a Merkle path and leaf which describes the node located at the base of the
/// path.
#[derive(PartialEq, Eq, Debug)]
pub struct SmtProof {
path: MerklePath,
leaf: SmtLeaf,
}
impl SmtProof {
// CONSTRUCTOR
// --------------------------------------------------------------------------------------------
/// Returns a new instance of [`SmtProof`] instantiated from the specified path and leaf.
///
/// # Errors
/// Returns an error if the path length is not [`SMT_DEPTH`].
pub fn new(path: MerklePath, leaf: SmtLeaf) -> Result<Self, SmtProofError> {
if path.len() != SMT_DEPTH.into() {
return Err(SmtProofError::InvalidPathLength(path.len()));
}
Ok(Self { path, leaf })
}
/// Returns a new instance of [`SmtProof`] instantiated from the specified path and leaf.
///
/// The length of the path is not checked. Reserved for internal use.
pub(super) fn new_unchecked(path: MerklePath, leaf: SmtLeaf) -> Self {
Self { path, leaf }
}
// PROOF VERIFIER
// --------------------------------------------------------------------------------------------
/// Returns true if a [`super::Smt`] with the specified root contains the provided
/// key-value pair.
///
/// Note: this method cannot be used to assert non-membership. That is, if false is returned,
/// it does not mean that the provided key-value pair is not in the tree.
pub fn verify_membership(&self, key: &RpoDigest, value: &Word, root: &RpoDigest) -> bool {
let maybe_value_in_leaf = self.leaf.get_value(key);
match maybe_value_in_leaf {
Some(value_in_leaf) => {
// The value must match for the proof to be valid
if value_in_leaf != *value {
return false;
}
// make sure the Merkle path resolves to the correct root
self.compute_root() == *root
}
// If the key maps to a different leaf, the proof cannot verify membership of `value`
None => false,
}
}
// PUBLIC ACCESSORS
// --------------------------------------------------------------------------------------------
/// Returns the value associated with the specific key according to this proof, or None if
/// this proof does not contain a value for the specified key.
///
/// A key-value pair generated by using this method should pass the `verify_membership()` check.
pub fn get(&self, key: &RpoDigest) -> Option<Word> {
self.leaf.get_value(key)
}
/// Computes the root of a [`super::Smt`] to which this proof resolves.
pub fn compute_root(&self) -> RpoDigest {
self.path
.compute_root(self.leaf.index().value(), self.leaf.hash())
.expect("failed to compute Merkle path root")
}
/// Consume the proof and returns its parts.
pub fn into_parts(self) -> (MerklePath, SmtLeaf) {
(self.path, self.leaf)
}
}
impl Serializable for SmtProof {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
self.path.write_into(target);
self.leaf.write_into(target);
}
}
impl Deserializable for SmtProof {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let path = MerklePath::read_from(source)?;
let leaf = SmtLeaf::read_from(source)?;
Self::new(path, leaf).map_err(|err| DeserializationError::InvalidValue(err.to_string()))
}
}