penumbra_sdk_tct/storage/
in_memory.rs

1//! An in-memory storage backend, useful for testing.
2
3use super::*;
4
5/// An in-memory storage backend, useful for testing.
6#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
7pub struct InMemory {
8    sparse: bool,
9    position: StoredPosition,
10    forgotten: Forgotten,
11    hashes: BTreeMap<Position, BTreeMap<u8, Hash>>,
12    commitments: BTreeMap<Position, StateCommitment>,
13}
14
15impl InMemory {
16    /// Create a new in-memory storage backend.
17    pub fn new() -> Self {
18        Self::default()
19    }
20
21    /// Create a new in-memory storage backend that only stores essential hashes.
22    pub fn new_sparse() -> Self {
23        let mut new = Self::new();
24        new.sparse = true;
25        new
26    }
27
28    /// Get the position of the stored tree.
29    pub fn position(&self) -> StoredPosition {
30        self.position
31    }
32
33    /// Get the forgotten version of the stored tree.
34    pub fn forgotten(&self) -> Forgotten {
35        self.forgotten
36    }
37
38    /// Get an iterator of all the hashes stored.
39    pub fn hashes(&self) -> impl Iterator<Item = (Position, u8, Hash)> + '_ {
40        self.hashes.iter().flat_map(|(position, hashes)| {
41            hashes
42                .iter()
43                .map(move |(height, hash)| (*position, *height, *hash))
44        })
45    }
46
47    /// Get an iterator of all the commitments stored.
48    pub fn commitments(&self) -> impl Iterator<Item = (Position, StateCommitment)> + '_ {
49        self.commitments
50            .iter()
51            .map(|(position, commitment)| (*position, *commitment))
52    }
53}
54
55/// An error which can occur when using the in-memory storage backend.
56#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Error)]
57pub enum Error {
58    /// A write was attempted over an existing commitment.
59    #[error("refusing to overwrite existing commitment at position {position:?}")]
60    DuplicateWriteCommitment {
61        /// The position of the existing commitment.
62        position: Position,
63    },
64    /// An unnecessary write was performed.
65    #[error("repeated write of hash at position {position:?}, height {height}")]
66    RepeatedWriteHash {
67        /// The position of the hash.
68        position: Position,
69        /// The height of the hash.
70        height: u8,
71    },
72    /// A hash was overwritten with a different hash.
73    #[error("hash overwritten with different hash at position {position:?}, height {height}")]
74    OverwrittenHash {
75        /// The position of the hash.
76        position: Position,
77        /// The height of the hash.
78        height: u8,
79    },
80    /// A hash was marked essential, but it still had children.
81    #[error("recalculable hash marked essential at position {position:?}, height {height}")]
82    EssentialHashHasChildren {
83        /// The position of the hash.
84        position: Position,
85        /// The height of the hash.
86        height: u8,
87    },
88    /// The position was set, but it did not increase.
89    #[error("set position did not increase from {previous:?} to {new:?}")]
90    PositionDidNotIncrease {
91        /// The previous position.
92        previous: StoredPosition,
93        /// The new position.
94        new: StoredPosition,
95    },
96    /// The forgotten version was set, but it did not increase.
97    #[error("set forgotten version did not increase from {previous:?} to {new:?}")]
98    ForgottenDidNotIncrease {
99        /// The previous forgotten version.
100        previous: Forgotten,
101        /// The new forgotten version.
102        new: Forgotten,
103    },
104}
105
106impl Read for InMemory {
107    type Error = Error;
108
109    type HashesIter<'a> =
110        Box<dyn Iterator<Item = Result<(Position, u8, Hash), Self::Error>> + Send + 'a>;
111    type CommitmentsIter<'a> =
112        Box<dyn Iterator<Item = Result<(Position, StateCommitment), Self::Error>> + Send + 'a>;
113
114    fn position(&mut self) -> Result<StoredPosition, Self::Error> {
115        Ok(self.position)
116    }
117
118    fn forgotten(&mut self) -> Result<Forgotten, Self::Error> {
119        Ok(self.forgotten)
120    }
121
122    fn hash(&mut self, position: Position, height: u8) -> Result<Option<Hash>, Self::Error> {
123        Ok(self
124            .hashes
125            .get(&position)
126            .and_then(|h| h.get(&height))
127            .cloned())
128    }
129
130    fn hashes(&mut self) -> Self::HashesIter<'_> {
131        Box::new(InMemory::hashes(self).map(Ok))
132    }
133
134    fn commitment(&mut self, position: Position) -> Result<Option<StateCommitment>, Self::Error> {
135        Ok(self.commitments.get(&position).cloned())
136    }
137
138    fn commitments(&mut self) -> Self::CommitmentsIter<'_> {
139        Box::new(InMemory::commitments(self).map(Ok))
140    }
141}
142
143impl Write for InMemory {
144    fn add_hash(
145        &mut self,
146        position: Position,
147        height: u8,
148        hash: Hash,
149        essential: bool,
150    ) -> Result<(), Self::Error> {
151        if !essential && self.sparse {
152            // If running in sparse mode, non-essential hashes are not persisted
153            return Ok(());
154        }
155
156        let column = self.hashes.entry(position).or_default();
157        // Only insert if nothing is already there
158        match column.entry(height) {
159            Entry::Vacant(e) => {
160                e.insert(hash);
161            }
162            Entry::Occupied(e) => {
163                if !essential {
164                    return Err(Error::RepeatedWriteHash { position, height });
165                }
166                if *e.into_mut() != hash {
167                    return Err(Error::OverwrittenHash { position, height });
168                }
169            }
170        };
171        Ok(())
172    }
173
174    fn add_commitment(
175        &mut self,
176        position: Position,
177        commitment: StateCommitment,
178    ) -> Result<(), Self::Error> {
179        // Only insert if nothing is already there
180        match self.commitments.entry(position) {
181            Entry::Vacant(e) => e.insert(commitment),
182            Entry::Occupied(_) => return Err(Error::DuplicateWriteCommitment { position }),
183        };
184        Ok(())
185    }
186
187    fn delete_range(
188        &mut self,
189        below_height: u8,
190        range: Range<Position>,
191    ) -> Result<(), Self::Error> {
192        // Remove all the inner hashes below and in range
193        let empty_columns: Vec<Position> = self
194            .hashes
195            .range_mut(range.clone())
196            .filter_map(|(&position, column)| {
197                *column = column.split_off(&below_height);
198                if column.is_empty() {
199                    Some(position)
200                } else {
201                    None
202                }
203            })
204            .collect();
205
206        // Remove all the now-empty columns
207        for position in empty_columns {
208            self.hashes.remove(&position);
209        }
210
211        // Find the positions of the commitments within the range
212        let commitments_to_delete: Vec<Position> = self
213            .commitments
214            .range(range)
215            .map(|(&position, _)| position)
216            .collect();
217
218        // Remove all the commitments within the range
219        for position in commitments_to_delete {
220            self.commitments.remove(&position);
221        }
222
223        Ok(())
224    }
225
226    fn set_position(&mut self, position: StoredPosition) -> Result<(), Self::Error> {
227        if self.position >= position {
228            return Err(Error::PositionDidNotIncrease {
229                previous: self.position,
230                new: position,
231            });
232        }
233        self.position = position;
234        Ok(())
235    }
236
237    fn set_forgotten(&mut self, forgotten: Forgotten) -> Result<(), Self::Error> {
238        if self.forgotten >= forgotten {
239            return Err(Error::ForgottenDidNotIncrease {
240                previous: self.forgotten,
241                new: forgotten,
242            });
243        }
244        self.forgotten = forgotten;
245        Ok(())
246    }
247}