1use core::fmt::Debug;
2use std::marker::PhantomData;
3
4use alloc::{vec, vec::Vec};
5use anyhow::Error;
6use prost::Message;
7
8use warg_crypto::hash::{Hash, SupportedDigest};
9use warg_crypto::VisitBytes;
10use warg_protobuf::internal as protobuf;
11
12use super::node::{Node, Side};
13use super::{hash_branch, hash_empty, hash_leaf, Checkpoint, LogBuilder, LogData};
14
15#[derive(Debug, Clone)]
18pub struct VecLog<D, V>
19where
20 D: SupportedDigest,
21 V: VisitBytes,
22{
23 length: usize,
25 tree: Vec<Hash<D>>,
27 _value: PhantomData<V>,
29}
30
31impl<D, V> VecLog<D, V>
36where
37 D: SupportedDigest,
38 V: VisitBytes,
39{
40 pub fn length(&self) -> usize {
42 self.length
43 }
44
45 fn get_digest(&self, node: Node) -> Hash<D> {
46 self.tree[node.index()].clone()
47 }
48
49 fn set_digest(&mut self, node: Node, digest: Hash<D>) {
50 self.tree[node.index()] = digest;
51 }
52
53 fn root_at(&self, length: usize) -> Option<Hash<D>> {
55 if length > self.length {
56 return None;
57 }
58
59 let roots = Node::broots_for_len(length);
60
61 let result = roots
62 .into_iter()
63 .rev()
64 .map(|node| self.hash_for(node).unwrap())
65 .reduce(|old, new| {
66 hash_branch::<D>(new, old)
68 })
69 .unwrap_or_else(hash_empty::<D>);
70
71 Some(result)
72 }
73
74 pub fn to_protobuf(self) -> Vec<u8> {
76 let proto: protobuf::VecLog = self.into();
77 proto.encode_to_vec()
78 }
79
80 pub fn from_protobuf(bytes: &[u8]) -> Result<Self, Error> {
82 let proto = protobuf::VecLog::decode(bytes)?;
83 let value = proto.try_into()?;
84 Ok(value)
85 }
86}
87
88impl<D, V> Default for VecLog<D, V>
89where
90 D: SupportedDigest,
91 V: VisitBytes,
92{
93 fn default() -> Self {
94 VecLog {
95 length: 0,
96 tree: vec![],
97 _value: PhantomData,
98 }
99 }
100}
101
102impl<D, V> LogBuilder<D, V> for VecLog<D, V>
103where
104 D: SupportedDigest,
105 V: VisitBytes,
106{
107 fn checkpoint(&self) -> Checkpoint<D> {
108 Checkpoint {
109 root: self.root_at(self.length).unwrap(),
110 length: self.length,
111 }
112 }
113
114 fn push(&mut self, entry: &V) -> Node {
115 let leaf_digest = hash_leaf::<D>(entry);
117
118 self.length += 1;
120
121 if self.length != 1 {
123 self.tree.push(hash_empty::<D>());
124 }
125 let leaf_node = Node(self.tree.len());
126 self.tree.push(leaf_digest.clone());
127
128 let mut current_digest = leaf_digest;
130 let mut current_node = leaf_node;
131 while current_node.side() == Side::Right {
132 let sibling = current_node.left_sibling();
133 let parent = current_node.parent();
134
135 let lhs = self.get_digest(sibling);
136 let rhs = current_digest;
137
138 current_digest = hash_branch::<D>(lhs, rhs);
139 current_node = parent;
140
141 self.set_digest(current_node, current_digest.clone());
142 }
143
144 leaf_node
145 }
146}
147
148impl<D, V> AsRef<[Hash<D>]> for VecLog<D, V>
149where
150 D: SupportedDigest,
151 V: VisitBytes,
152{
153 fn as_ref(&self) -> &[Hash<D>] {
154 &self.tree[..]
155 }
156}
157
158impl<D, V> LogData<D, V> for VecLog<D, V>
159where
160 D: SupportedDigest,
161 V: VisitBytes,
162{
163 fn hash_for(&self, node: Node) -> Option<Hash<D>> {
164 self.tree.get(node.index()).cloned()
165 }
166
167 fn has_hash(&self, node: Node) -> bool {
168 self.tree.len() > node.index()
169 }
170}
171
172impl<D, V> From<VecLog<D, V>> for protobuf::VecLog
173where
174 D: SupportedDigest,
175 V: VisitBytes,
176{
177 fn from(value: VecLog<D, V>) -> Self {
178 let tree = value
179 .tree
180 .into_iter()
181 .map(|hash| hash.bytes().to_vec())
182 .collect();
183 protobuf::VecLog {
184 length: value.length as u32,
185 tree,
186 }
187 }
188}
189
190impl<D, V> TryFrom<protobuf::VecLog> for VecLog<D, V>
191where
192 D: SupportedDigest,
193 V: VisitBytes,
194{
195 type Error = Error;
196
197 fn try_from(value: protobuf::VecLog) -> Result<Self, Self::Error> {
198 let length = value.length as usize;
199 let mut tree = Vec::with_capacity(length);
200 for entry in value.tree {
201 tree.push(entry.try_into()?)
202 }
203 let vec_log = VecLog {
204 length,
205 tree,
206 _value: PhantomData,
207 };
208 Ok(vec_log)
209 }
210}
211
212#[cfg(test)]
213mod tests {
214 use warg_crypto::hash::Sha256;
215
216 use crate::log::proof::InclusionProofError;
217
218 use super::*;
219
220 fn naive_merkle<D: SupportedDigest, E: VisitBytes>(elements: &[E]) -> Hash<D> {
221 match elements.len() {
222 0 => hash_empty::<D>(),
223 1 => hash_leaf::<D>(&elements[0]),
224 _ => {
225 let k = elements.len().next_power_of_two() / 2;
226 let left = naive_merkle::<D, E>(&elements[..k]);
227 let right = naive_merkle::<D, E>(&elements[k..]);
228 hash_branch::<D>(left, right)
229 }
230 }
231 }
232
233 #[test]
234 fn test_log_modifications() {
235 let data = [
236 "93", "67", "30", "37", "23", "75", "57", "89", "76", "42", "9", "14", "40", "59",
237 "26", "66", "77", "38", "47", "34", "8", "81", "101", "102", "103",
238 ];
239
240 let mut tree: VecLog<Sha256, &str> = VecLog::default();
241 let mut roots = Vec::new();
242
243 for i in 0..data.len() {
244 tree.push(&data[i]);
245
246 let naive_root = naive_merkle::<Sha256, _>(&data[..i + 1]);
247
248 let tree_root = tree.checkpoint().root();
249 assert_eq!(
250 tree_root, naive_root,
251 "at {}: (in-order) {:?} != (naive) {:?}",
252 i, tree_root, naive_root
253 );
254
255 roots.push(tree_root);
256 }
257
258 for (i, _) in data.iter().enumerate() {
260 let leaf_node = Node(i * 2);
261
262 for (j, root) in roots.iter().enumerate() {
263 let log_length = j + 1;
264 let inc_proof = tree.prove_inclusion(leaf_node, log_length);
265 let result = inc_proof.evaluate_value(&tree, &data[i]);
266 if j >= i {
267 assert!(result.is_ok());
268 assert_eq!(root.clone(), result.unwrap());
269 } else {
270 assert!(result.is_err());
271 assert_eq!(result.unwrap_err(), InclusionProofError::LeafTooNew);
272 }
273 }
274 }
275
276 for (i, _) in data.iter().enumerate() {
278 let old_length = i + 1;
279 let old_root = tree.root_at(old_length).unwrap();
280
281 for (j, new_root) in roots.iter().enumerate().skip(i) {
282 let new_root = new_root.clone();
283 let new_length = j + 1;
284
285 let proof = tree.prove_consistency(old_length, new_length);
286 let results = proof.evaluate(&tree).unwrap();
287 let (found_old_root, found_new_root) = results;
288 assert_eq!(old_root, found_old_root);
289 assert_eq!(new_root, found_new_root);
290 }
291 }
292 }
293}