1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
use alloc::vec::Vec;
use core::marker::PhantomData;
use std::iter::repeat;

use warg_crypto::{
    hash::{Hash, SupportedDigest},
    VisitBytes,
};

use super::{
    map::{hash_branch, hash_leaf},
    path::{ReversePath, Side},
};

/// An inclusion proof of the specified value in a map
///
/// # Compression
///
/// Since the depth of a tree is always `n` and a proof needs to contain all
/// branch node peers from the leaf to the root, a proof should contain `n`
/// hashes. However, several strategies can be used to compress a proof;
/// saving both memory and bytes on the wire.
///
/// First, the hash of the item and the root are known by both sides and can
/// be omitted.
///
/// Second, sparse peers can be represented by `None`. Since we take references
/// to the hashes, Rust null-optimization is used.
///
/// Third, since sparse peers are more likely at the bottom of the tree, we
/// can omit all leading sparse peers. The verifier can dynamically reconstruct
pub struct Proof<D, K, V>
where
    D: SupportedDigest,
    K: VisitBytes,
    V: VisitBytes,
{
    key: PhantomData<K>,
    value: PhantomData<V>,
    /// Sibling node hashes needed to construct a proof
    pub peers: Vec<Option<Hash<D>>>,
}

impl<D, K, V> Proof<D, K, V>
where
    D: SupportedDigest,
    K: VisitBytes,
    V: VisitBytes,
{
    pub(crate) fn new(peers: Vec<Option<Hash<D>>>) -> Self {
        Self {
            key: PhantomData,
            value: PhantomData,
            peers,
        }
    }

    pub(crate) fn push(&mut self, peer: Option<Hash<D>>) {
        self.peers.push(peer);
    }

    /// Computes the root obtained by evaluating this inclusion proof with the given leaf
    pub fn evaluate(&self, key: &K, value: &V) -> Hash<D> {
        // Get the path from bottom to top.
        let path = ReversePath::<D>::new(Hash::of(key));

        let fill = repeat(None).take(256 - self.peers.len());
        // Calculate the leaf hash.
        let mut hash = hash_leaf(value);

        // Loop over each side and peer.
        let peers = fill.chain(self.peers.iter().cloned());
        for (i, (side, peer)) in path.zip(peers).enumerate() {
            match &peer {
                Some(_) => {
                    hash = match side {
                        Side::Left => hash_branch(&hash, &peer.unwrap()),
                        Side::Right => hash_branch(&peer.unwrap(), &hash),
                    };
                }
                None => match side {
                    Side::Left => hash = hash_branch(&hash, D::empty_tree_hash(i)),
                    Side::Right => {
                        hash = hash_branch(D::empty_tree_hash(i), &hash);
                    }
                },
            }
        }

        hash
    }
}

impl<D, K, V> From<Proof<D, K, V>> for Vec<Option<Hash<D>>>
where
    D: SupportedDigest,
    K: VisitBytes,
    V: VisitBytes,
{
    fn from(value: Proof<D, K, V>) -> Self {
        value.peers
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_proof_evaluate() {
        use warg_crypto::hash::Sha256;

        let a = crate::map::Map::<Sha256, &str, &[u8]>::default();
        let b = a.insert("foo", b"bar");
        let c = b.insert("baz", b"bat");

        let root = c.root().clone();

        let p = c.prove("baz").unwrap();

        assert_eq!(root, p.evaluate(&"baz", &b"bat".as_slice()));
        assert_ne!(root, p.evaluate(&"other", &b"bar".as_slice()));
    }
}