1use super::resource::{HeapError, Resource, ResourceId};
23use std::collections::{BTreeMap, BTreeSet};
24use telltale_types::{DefaultContentHasher, Hasher};
25
26#[derive(Debug, Clone, Default)]
31pub struct Heap<H: Hasher = DefaultContentHasher> {
32 resources: BTreeMap<ResourceId<H>, Resource>,
34 nullifiers: BTreeSet<ResourceId<H>>,
36 counter: u64,
38}
39
40impl<H: Hasher> Heap<H> {
41 pub fn new() -> Self {
43 Self::default()
44 }
45
46 pub fn size(&self) -> usize {
50 self.resources.len()
51 }
52
53 pub fn nullified_count(&self) -> usize {
55 self.nullifiers.len()
56 }
57
58 pub fn contains(&self, rid: &ResourceId<H>) -> bool {
60 self.resources.contains_key(rid)
61 }
62
63 pub fn is_consumed(&self, rid: &ResourceId<H>) -> bool {
65 self.nullifiers.contains(rid)
66 }
67
68 pub fn is_active(&self, rid: &ResourceId<H>) -> bool {
70 self.contains(rid) && !self.is_consumed(rid)
71 }
72
73 pub fn alloc_counter(&self) -> u64 {
75 self.counter
76 }
77
78 pub fn alloc(&self, resource: Resource) -> (ResourceId<H>, Heap<H>) {
83 let rid = ResourceId::<H>::from_resource(&resource, self.counter);
84 let mut new_heap = self.clone();
85 new_heap.resources.insert(rid.clone(), resource);
86 new_heap.counter += 1;
87 (rid, new_heap)
88 }
89
90 pub fn alloc_mut(&mut self, resource: Resource) -> ResourceId<H> {
92 let rid = ResourceId::<H>::from_resource(&resource, self.counter);
93 self.resources.insert(rid.clone(), resource);
94 self.counter += 1;
95 rid
96 }
97
98 pub fn read(&self, rid: &ResourceId<H>) -> Result<&Resource, HeapError<H>> {
102 if self.is_consumed(rid) {
103 return Err(HeapError::AlreadyConsumed(rid.clone()));
104 }
105 self.resources
106 .get(rid)
107 .ok_or_else(|| HeapError::NotFound(rid.clone()))
108 }
109
110 pub fn consume(&self, rid: &ResourceId<H>) -> Result<Heap<H>, HeapError<H>> {
115 if self.is_consumed(rid) {
116 return Err(HeapError::AlreadyConsumed(rid.clone()));
117 }
118 if !self.contains(rid) {
119 return Err(HeapError::NotFound(rid.clone()));
120 }
121 let mut new_heap = self.clone();
122 new_heap.nullifiers.insert(rid.clone());
123 Ok(new_heap)
124 }
125
126 pub fn consume_mut(&mut self, rid: &ResourceId<H>) -> Result<(), HeapError<H>> {
128 if self.is_consumed(rid) {
129 return Err(HeapError::AlreadyConsumed(rid.clone()));
130 }
131 if !self.contains(rid) {
132 return Err(HeapError::NotFound(rid.clone()));
133 }
134 self.nullifiers.insert(rid.clone());
135 Ok(())
136 }
137
138 pub fn remove(&self, rid: &ResourceId<H>) -> Result<Heap<H>, HeapError<H>> {
143 if !self.contains(rid) {
144 return Err(HeapError::NotFound(rid.clone()));
145 }
146 let mut new_heap = self.clone();
147 new_heap.resources.remove(rid);
148 new_heap.nullifiers.remove(rid);
149 Ok(new_heap)
150 }
151
152 pub fn active_resources(&self) -> impl Iterator<Item = (&ResourceId<H>, &Resource)> {
154 self.resources
155 .iter()
156 .filter(|(rid, _)| !self.nullifiers.contains(*rid))
157 }
158
159 pub fn consumed_ids(&self) -> impl Iterator<Item = &ResourceId<H>> {
161 self.nullifiers.iter()
162 }
163
164 pub fn alloc_many(
166 &self,
167 resources: impl IntoIterator<Item = Resource>,
168 ) -> (Vec<ResourceId<H>>, Heap<H>) {
169 let mut new_heap = self.clone();
170 let rids: Vec<_> = resources
171 .into_iter()
172 .map(|r| new_heap.alloc_mut(r))
173 .collect();
174 (rids, new_heap)
175 }
176
177 pub fn consume_many(&self, rids: &[ResourceId<H>]) -> Result<Heap<H>, HeapError<H>> {
181 for rid in rids {
183 if self.is_consumed(rid) {
184 return Err(HeapError::AlreadyConsumed(rid.clone()));
185 }
186 if !self.contains(rid) {
187 return Err(HeapError::NotFound(rid.clone()));
188 }
189 }
190 let mut new_heap = self.clone();
192 for rid in rids {
193 new_heap.nullifiers.insert(rid.clone());
194 }
195 Ok(new_heap)
196 }
197
198 pub fn alloc_channel(&self, sender: &str, receiver: &str) -> (ResourceId<H>, Heap<H>) {
200 self.alloc(Resource::channel(sender, receiver))
201 }
202
203 pub fn alloc_message(
205 &self,
206 source: &str,
207 dest: &str,
208 label: &str,
209 payload: Vec<u8>,
210 seq_no: u64,
211 ) -> (ResourceId<H>, Heap<H>) {
212 self.alloc(Resource::message(source, dest, label, payload, seq_no))
213 }
214
215 pub fn alloc_session(&self, role: &str, type_hash: u64) -> (ResourceId<H>, Heap<H>) {
217 self.alloc(Resource::session(role, type_hash))
218 }
219}
220
221#[cfg(test)]
222mod tests {
223 use super::*;
224 use crate::heap::{HeapCommitment, MerkleProof, MerkleTree};
225 use telltale_types::DefaultContentHasher;
226
227 #[test]
228 fn test_heap_alloc() {
229 let heap = Heap::<DefaultContentHasher>::new();
230 assert_eq!(heap.size(), 0);
231 assert_eq!(heap.alloc_counter(), 0);
232
233 let resource = Resource::channel("Alice", "Bob");
234 let (rid, heap) = heap.alloc(resource);
235
236 assert_eq!(heap.size(), 1);
237 assert_eq!(heap.alloc_counter(), 1);
238 assert!(heap.contains(&rid));
239 assert!(heap.is_active(&rid));
240 }
241
242 #[test]
243 fn test_heap_read() {
244 let heap = Heap::<DefaultContentHasher>::new();
245 let resource = Resource::channel("Alice", "Bob");
246 let (rid, heap) = heap.alloc(resource);
247
248 let read_resource = heap.read(&rid).unwrap();
249 assert_eq!(read_resource.kind(), "channel");
250
251 let fake_rid = ResourceId::<DefaultContentHasher>::new([0u8; 32], 999);
253 assert!(heap.read(&fake_rid).is_err());
254 }
255
256 #[test]
257 fn test_heap_consume() {
258 let heap = Heap::<DefaultContentHasher>::new();
259 let resource = Resource::channel("Alice", "Bob");
260 let (rid, heap) = heap.alloc(resource);
261
262 assert!(!heap.is_consumed(&rid));
263 let heap = heap.consume(&rid).unwrap();
264 assert!(heap.is_consumed(&rid));
265 assert!(!heap.is_active(&rid));
266
267 assert!(heap.contains(&rid));
269
270 assert!(heap.read(&rid).is_err());
272
273 assert!(heap.consume(&rid).is_err());
275 }
276
277 #[test]
278 fn test_heap_remove() {
279 let heap = Heap::<DefaultContentHasher>::new();
280 let resource = Resource::channel("Alice", "Bob");
281 let (rid, heap) = heap.alloc(resource);
282
283 let heap = heap.remove(&rid).unwrap();
284 assert!(!heap.contains(&rid));
285 assert_eq!(heap.size(), 0);
286 }
287
288 #[test]
289 fn test_heap_alloc_many() {
290 let heap = Heap::<DefaultContentHasher>::new();
291 let resources = vec![
292 Resource::channel("Alice", "Bob"),
293 Resource::channel("Bob", "Carol"),
294 Resource::message("Alice", "Bob", "Hello", vec![], 0),
295 ];
296
297 let (rids, heap) = heap.alloc_many(resources);
298
299 assert_eq!(rids.len(), 3);
300 assert_eq!(heap.size(), 3);
301 assert_eq!(heap.alloc_counter(), 3);
302 }
303
304 #[test]
305 fn test_heap_consume_many() {
306 let heap = Heap::<DefaultContentHasher>::new();
307 let (rids, heap) = heap.alloc_many(vec![
308 Resource::channel("Alice", "Bob"),
309 Resource::channel("Bob", "Carol"),
310 ]);
311
312 let heap = heap.consume_many(&rids).unwrap();
313 assert!(heap.is_consumed(&rids[0]));
314 assert!(heap.is_consumed(&rids[1]));
315 assert_eq!(heap.nullified_count(), 2);
316 }
317
318 #[test]
319 fn test_heap_active_resources() {
320 let heap = Heap::<DefaultContentHasher>::new();
321 let (rids, heap) = heap.alloc_many(vec![
322 Resource::channel("Alice", "Bob"),
323 Resource::channel("Bob", "Carol"),
324 ]);
325
326 let heap = heap.consume(&rids[0]).unwrap();
327
328 let active: Vec<_> = heap.active_resources().collect();
329 assert_eq!(active.len(), 1);
330 assert_eq!(active[0].0, &rids[1]);
331 }
332
333 #[test]
334 fn test_heap_determinism() {
335 let h1 = Heap::<DefaultContentHasher>::new();
336 let h2 = Heap::<DefaultContentHasher>::new();
337
338 let r1 = Resource::channel("Alice", "Bob");
339 let r2 = Resource::channel("Alice", "Bob");
340
341 let (rid1, h1) = h1.alloc(r1);
342 let (rid2, h2) = h2.alloc(r2);
343
344 assert_eq!(rid1, rid2);
346 assert_eq!(
347 HeapCommitment::<DefaultContentHasher>::from_heap(&h1).hash(),
348 HeapCommitment::<DefaultContentHasher>::from_heap(&h2).hash()
349 );
350 }
351
352 #[test]
353 fn test_consume_many_is_order_independent() {
354 let heap = Heap::<DefaultContentHasher>::new();
355 let (rids, heap) = heap.alloc_many(vec![
356 Resource::channel("Alice", "Bob"),
357 Resource::channel("Bob", "Carol"),
358 Resource::session("Alice", 7),
359 ]);
360
361 let left = heap
362 .consume_many(&[rids[0].clone(), rids[1].clone()])
363 .unwrap();
364 let right = heap
365 .consume_many(&[rids[1].clone(), rids[0].clone()])
366 .unwrap();
367
368 assert_eq!(
369 HeapCommitment::<DefaultContentHasher>::from_heap(&left),
370 HeapCommitment::<DefaultContentHasher>::from_heap(&right)
371 );
372 let consumed_left: Vec<_> = left.consumed_ids().cloned().collect();
373 let consumed_right: Vec<_> = right.consumed_ids().cloned().collect();
374 assert_eq!(consumed_left, consumed_right);
375 }
376
377 #[test]
378 fn test_repeated_operation_sequences_yield_identical_proofs_and_commitments() {
379 fn build_fixture() -> (
380 Vec<ResourceId<DefaultContentHasher>>,
381 HeapCommitment<DefaultContentHasher>,
382 MerkleProof<DefaultContentHasher>,
383 ) {
384 let heap = Heap::<DefaultContentHasher>::new();
385 let (rid0, heap) = heap.alloc_channel("Alice", "Bob");
386 let (rid1, heap) = heap.alloc_message("Alice", "Bob", "Hello", vec![1, 2, 3], 7);
387 let (rid2, heap) = heap.alloc_session("Alice", 12345);
388 let heap = heap.consume(&rid0).unwrap();
389
390 let commitment = HeapCommitment::<DefaultContentHasher>::from_heap(&heap);
391 let proof = MerkleTree::<DefaultContentHasher>::from_heap(&heap)
392 .prove(0)
393 .expect("proof should exist");
394 (vec![rid0, rid1, rid2], commitment, proof)
395 }
396
397 let left = build_fixture();
398 let right = build_fixture();
399
400 assert_eq!(left.0, right.0);
401 assert_eq!(left.1, right.1);
402 assert_eq!(left.2, right.2);
403 }
404
405 #[test]
406 fn test_remove_changes_commitment_and_clears_nullifier_history() {
407 let heap = Heap::<DefaultContentHasher>::new();
408 let (rid, heap) = heap.alloc_channel("Alice", "Bob");
409 let consumed = heap.consume(&rid).unwrap();
410 let removed = consumed.remove(&rid).unwrap();
411
412 let consumed_commitment = HeapCommitment::<DefaultContentHasher>::from_heap(&consumed);
413 let removed_commitment = HeapCommitment::<DefaultContentHasher>::from_heap(&removed);
414
415 assert_ne!(consumed_commitment, removed_commitment);
416 assert_eq!(removed.nullified_count(), 0);
417 assert_eq!(removed.size(), 0);
418 }
419
420 #[test]
421 fn test_helper_methods() {
422 let heap = Heap::<DefaultContentHasher>::new();
423
424 let (rid1, heap) = heap.alloc_channel("Alice", "Bob");
425 assert!(matches!(heap.read(&rid1), Ok(Resource::Channel(_))));
426
427 let (rid2, heap) = heap.alloc_message("Alice", "Bob", "Hello", vec![1, 2, 3], 0);
428 assert!(matches!(heap.read(&rid2), Ok(Resource::Message(_))));
429
430 let (rid3, heap) = heap.alloc_session("Alice", 12345);
431 assert!(matches!(heap.read(&rid3), Ok(Resource::Session { .. })));
432
433 assert_eq!(heap.size(), 3);
434 }
435}