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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
use crate::{Error, Result};
use rs_merkle::{algorithms::Sha256, Hasher, MerkleTree};

use super::{CommitHash, CommitProof, CommitState, Comparison, TreeHash};

/// Encapsulates a Merkle tree and provides functions
/// for generating and comparing proofs.
#[derive(Default)]
pub struct CommitTree {
    tree: MerkleTree<Sha256>,
    maybe_last_commit: Option<TreeHash>,
    last_commit: Option<TreeHash>,
}

impl CommitTree {
    /// Create a new empty commit tree.
    pub fn new() -> Self {
        Self {
            tree: MerkleTree::<Sha256>::new(),
            maybe_last_commit: None,
            last_commit: None,
        }
    }

    /// Compute the Sha256 hash of some data.
    pub fn hash(data: &[u8]) -> TreeHash {
        Sha256::hash(data)
    }

    /// Number of leaves in the tree.
    pub fn len(&self) -> usize {
        self.tree.leaves_len()
    }

    /// Determine if this commit tree is empty.
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Insert a commit hash into the tree,
    pub fn insert(&mut self, hash: TreeHash) -> &mut Self {
        self.maybe_last_commit = Some(hash);
        self.tree.insert(hash);
        self
    }

    /// Append a collections of commit hashes to the tree.
    pub fn append(&mut self, hashes: &mut Vec<TreeHash>) -> &mut Self {
        self.maybe_last_commit = hashes.last().cloned();
        self.tree.append(hashes);
        self
    }

    /// Commit changes to the tree to compute the root.
    pub fn commit(&mut self) {
        self.tree.commit();
        if self.maybe_last_commit.is_some() {
            self.last_commit = self.maybe_last_commit.take();
        }
    }

    /// Revert changes to the tree.
    pub fn rollback(&mut self) {
        self.tree.rollback();
        self.maybe_last_commit = None;
        if let Some(leaves) = self.leaves() {
            self.last_commit = leaves.last().cloned();
        } else {
            self.last_commit = None;
        }
    }

    /// Leaves of the tree.
    pub fn leaves(&self) -> Option<Vec<TreeHash>> {
        self.tree.leaves()
    }

    /// Root hash and a proof of the last leaf node.
    pub fn head(&self) -> Result<CommitProof> {
        if self.is_empty() {
            return Err(Error::NoRootCommit);
        }
        self.proof(&[self.tree.leaves_len() - 1])
    }

    /// Proof for the given indices.
    pub fn proof(&self, leaf_indices: &[usize]) -> Result<CommitProof> {
        let root = self.root().ok_or(Error::NoRootCommit)?;
        let proof = self.tree.proof(leaf_indices);
        Ok(CommitProof {
            root,
            proof,
            length: self.len(),
            indices: leaf_indices.to_vec(),
        })
    }

    /// Compare this tree against another root hash and merkle proof.
    pub fn compare(&self, proof: &CommitProof) -> Result<Comparison> {
        let CommitProof {
            root: other_root,
            proof,
            length,
            indices: indices_to_prove,
        } = proof;
        let root = self.root().ok_or(Error::NoRootCommit)?;
        if &root == other_root {
            Ok(Comparison::Equal)
        } else {
            let leaves = self.tree.leaves().unwrap_or_default();
            let leaves_to_prove = indices_to_prove
                .into_iter()
                .filter_map(|i| leaves.get(*i).cloned())
                .collect::<Vec<_>>();
            if leaves_to_prove.len() == indices_to_prove.len() {
                if proof.verify(
                    other_root.into(),
                    indices_to_prove.as_slice(),
                    leaves_to_prove.as_slice(),
                    *length,
                ) {
                    Ok(Comparison::Contains(indices_to_prove.to_vec()))
                } else {
                    Ok(Comparison::Unknown)
                }
            } else {
                Ok(Comparison::Unknown)
            }
        }
    }

    /// Compute the first commit state.
    ///
    /// Must have at least one commit.
    pub fn first_commit(&self) -> Result<CommitState> {
        let leaves = self.tree.leaves().ok_or(Error::NoRootCommit)?;

        // Compute a proof for the first commit
        let leaf = *leaves.first().ok_or(Error::NoRootCommit)?;
        let leaves = vec![leaf];
        let tree = MerkleTree::<Sha256>::from_leaves(&leaves);
        let proof = tree.proof(&[0]);
        let root = tree.root().map(CommitHash).ok_or(Error::NoRootCommit)?;
        let first_proof = CommitProof {
            root,
            proof,
            length: leaves.len(),
            indices: vec![0],
        };

        let first_commit = CommitHash(leaf);
        Ok(CommitState(first_commit, first_proof))
    }

    /// Last commit hash in the underlying merkle tree.
    pub fn last_commit(&self) -> Option<CommitHash> {
        self.last_commit.map(CommitHash)
    }

    /// Commit state of this tree.
    ///
    /// The tree must already have some commits.
    pub fn commit_state(&self) -> Result<CommitState> {
        let last_commit = self.last_commit().ok_or(Error::NoLastCommit)?;
        Ok(CommitState(last_commit, self.head()?))
    }

    /// Root hash of the underlying merkle tree.
    pub fn root(&self) -> Option<CommitHash> {
        self.tree.root().map(CommitHash)
    }

    /// Root hash of the underlying merkle tree as hexadecimal.
    pub fn root_hex(&self) -> Option<String> {
        self.tree.root().map(hex::encode)
    }

    /// Given a proof from another tree determine if this
    /// tree contains the other proof.
    pub fn contains(
        &self,
        other_proof: &CommitProof,
    ) -> Result<Option<CommitProof>> {
        Ok(match self.compare(other_proof)? {
            Comparison::Contains(indices) => Some(self.proof(&indices)?),
            _ => None,
        })
    }
}

#[cfg(debug_assertions)]
impl From<&crate::vault::Vault> for CommitTree {
    fn from(value: &crate::vault::Vault) -> Self {
        let mut commit_tree = CommitTree::new();
        for (_, commit) in value.commits() {
            commit_tree.tree.insert(commit.into());
        }
        commit_tree.tree.commit();
        commit_tree
    }
}