1use crate::{
17 atomic_batch_scope,
18 helpers::{Map, MapRead},
19};
20use console::{
21 network::prelude::*,
22 program::{Ciphertext, Plaintext},
23 types::Field,
24};
25use ledger_block::Input;
26
27use aleo_std_storage::StorageMode;
28use anyhow::Result;
29use std::borrow::Cow;
30
31pub trait InputStorage<N: Network>: Clone + Send + Sync {
33 type IDMap: for<'a> Map<'a, N::TransitionID, Vec<Field<N>>>;
35 type ReverseIDMap: for<'a> Map<'a, Field<N>, N::TransitionID>;
37 type ConstantMap: for<'a> Map<'a, Field<N>, Option<Plaintext<N>>>;
39 type PublicMap: for<'a> Map<'a, Field<N>, Option<Plaintext<N>>>;
41 type PrivateMap: for<'a> Map<'a, Field<N>, Option<Ciphertext<N>>>;
43 type RecordMap: for<'a> Map<'a, Field<N>, Field<N>>;
45 type RecordTagMap: for<'a> Map<'a, Field<N>, Field<N>>;
47 type ExternalRecordMap: for<'a> Map<'a, Field<N>, ()>;
49
50 fn open<S: Into<StorageMode>>(storage: S) -> Result<Self>;
52
53 fn id_map(&self) -> &Self::IDMap;
55 fn reverse_id_map(&self) -> &Self::ReverseIDMap;
57 fn constant_map(&self) -> &Self::ConstantMap;
59 fn public_map(&self) -> &Self::PublicMap;
61 fn private_map(&self) -> &Self::PrivateMap;
63 fn record_map(&self) -> &Self::RecordMap;
65 fn record_tag_map(&self) -> &Self::RecordTagMap;
67 fn external_record_map(&self) -> &Self::ExternalRecordMap;
69
70 fn storage_mode(&self) -> &StorageMode;
72
73 fn start_atomic(&self) {
75 self.id_map().start_atomic();
76 self.reverse_id_map().start_atomic();
77 self.constant_map().start_atomic();
78 self.public_map().start_atomic();
79 self.private_map().start_atomic();
80 self.record_map().start_atomic();
81 self.record_tag_map().start_atomic();
82 self.external_record_map().start_atomic();
83 }
84
85 fn is_atomic_in_progress(&self) -> bool {
87 self.id_map().is_atomic_in_progress()
88 || self.reverse_id_map().is_atomic_in_progress()
89 || self.constant_map().is_atomic_in_progress()
90 || self.public_map().is_atomic_in_progress()
91 || self.private_map().is_atomic_in_progress()
92 || self.record_map().is_atomic_in_progress()
93 || self.record_tag_map().is_atomic_in_progress()
94 || self.external_record_map().is_atomic_in_progress()
95 }
96
97 fn atomic_checkpoint(&self) {
99 self.id_map().atomic_checkpoint();
100 self.reverse_id_map().atomic_checkpoint();
101 self.constant_map().atomic_checkpoint();
102 self.public_map().atomic_checkpoint();
103 self.private_map().atomic_checkpoint();
104 self.record_map().atomic_checkpoint();
105 self.record_tag_map().atomic_checkpoint();
106 self.external_record_map().atomic_checkpoint();
107 }
108
109 fn clear_latest_checkpoint(&self) {
111 self.id_map().clear_latest_checkpoint();
112 self.reverse_id_map().clear_latest_checkpoint();
113 self.constant_map().clear_latest_checkpoint();
114 self.public_map().clear_latest_checkpoint();
115 self.private_map().clear_latest_checkpoint();
116 self.record_map().clear_latest_checkpoint();
117 self.record_tag_map().clear_latest_checkpoint();
118 self.external_record_map().clear_latest_checkpoint();
119 }
120
121 fn atomic_rewind(&self) {
123 self.id_map().atomic_rewind();
124 self.reverse_id_map().atomic_rewind();
125 self.constant_map().atomic_rewind();
126 self.public_map().atomic_rewind();
127 self.private_map().atomic_rewind();
128 self.record_map().atomic_rewind();
129 self.record_tag_map().atomic_rewind();
130 self.external_record_map().atomic_rewind();
131 }
132
133 fn abort_atomic(&self) {
135 self.id_map().abort_atomic();
136 self.reverse_id_map().abort_atomic();
137 self.constant_map().abort_atomic();
138 self.public_map().abort_atomic();
139 self.private_map().abort_atomic();
140 self.record_map().abort_atomic();
141 self.record_tag_map().abort_atomic();
142 self.external_record_map().abort_atomic();
143 }
144
145 fn finish_atomic(&self) -> Result<()> {
147 self.id_map().finish_atomic()?;
148 self.reverse_id_map().finish_atomic()?;
149 self.constant_map().finish_atomic()?;
150 self.public_map().finish_atomic()?;
151 self.private_map().finish_atomic()?;
152 self.record_map().finish_atomic()?;
153 self.record_tag_map().finish_atomic()?;
154 self.external_record_map().finish_atomic()
155 }
156
157 fn insert(&self, transition_id: N::TransitionID, inputs: &[Input<N>]) -> Result<()> {
159 atomic_batch_scope!(self, {
160 self.id_map().insert(transition_id, inputs.iter().map(Input::id).copied().collect())?;
162
163 for input in inputs {
165 self.reverse_id_map().insert(*input.id(), transition_id)?;
167 match input.clone() {
169 Input::Constant(input_id, constant) => self.constant_map().insert(input_id, constant)?,
170 Input::Public(input_id, public) => self.public_map().insert(input_id, public)?,
171 Input::Private(input_id, private) => self.private_map().insert(input_id, private)?,
172 Input::Record(serial_number, tag) => {
173 self.record_tag_map().insert(tag, serial_number)?;
175 self.record_map().insert(serial_number, tag)?
177 }
178 Input::ExternalRecord(input_id) => self.external_record_map().insert(input_id, ())?,
179 }
180 }
181
182 Ok(())
183 })
184 }
185
186 fn remove(&self, transition_id: &N::TransitionID) -> Result<()> {
188 let input_ids: Vec<_> = match self.id_map().get_confirmed(transition_id)? {
190 Some(Cow::Borrowed(ids)) => ids.to_vec(),
191 Some(Cow::Owned(ids)) => ids.into_iter().collect(),
192 None => return Ok(()),
193 };
194
195 atomic_batch_scope!(self, {
196 self.id_map().remove(transition_id)?;
198
199 for input_id in input_ids {
201 self.reverse_id_map().remove(&input_id)?;
203
204 if let Some(tag) = self.record_map().get_confirmed(&input_id)? {
206 self.record_tag_map().remove(&tag)?;
207 }
208
209 self.constant_map().remove(&input_id)?;
211 self.public_map().remove(&input_id)?;
212 self.private_map().remove(&input_id)?;
213 self.record_map().remove(&input_id)?;
214 self.external_record_map().remove(&input_id)?;
215 }
216
217 Ok(())
218 })
219 }
220
221 fn find_transition_id(&self, input_id: &Field<N>) -> Result<Option<N::TransitionID>> {
223 match self.reverse_id_map().get_confirmed(input_id)? {
224 Some(Cow::Borrowed(transition_id)) => Ok(Some(*transition_id)),
225 Some(Cow::Owned(transition_id)) => Ok(Some(transition_id)),
226 None => Ok(None),
227 }
228 }
229
230 fn get_ids(&self, transition_id: &N::TransitionID) -> Result<Vec<Field<N>>> {
232 match self.id_map().get_confirmed(transition_id)? {
234 Some(Cow::Borrowed(inputs)) => Ok(inputs.to_vec()),
235 Some(Cow::Owned(inputs)) => Ok(inputs),
236 None => Ok(vec![]),
237 }
238 }
239
240 fn get(&self, transition_id: &N::TransitionID) -> Result<Vec<Input<N>>> {
242 macro_rules! into_input {
244 (Input::Record($input_id:ident, $input:expr)) => {
245 match $input {
246 Cow::Borrowed(tag) => Input::Record($input_id, *tag),
247 Cow::Owned(tag) => Input::Record($input_id, tag),
248 }
249 };
250 (Input::$Variant:ident($input_id:ident, $input:expr)) => {
251 match $input {
252 Cow::Borrowed(input) => Input::$Variant($input_id, input.clone()),
253 Cow::Owned(input) => Input::$Variant($input_id, input),
254 }
255 };
256 }
257
258 let construct_input = |input_id| {
260 let constant = self.constant_map().get_confirmed(&input_id)?;
261 let public = self.public_map().get_confirmed(&input_id)?;
262 let private = self.private_map().get_confirmed(&input_id)?;
263 let record = self.record_map().get_confirmed(&input_id)?;
264 let external_record = self.external_record_map().get_confirmed(&input_id)?;
265
266 let input = match (constant, public, private, record, external_record) {
268 (Some(constant), None, None, None, None) => into_input!(Input::Constant(input_id, constant)),
269 (None, Some(public), None, None, None) => into_input!(Input::Public(input_id, public)),
270 (None, None, Some(private), None, None) => into_input!(Input::Private(input_id, private)),
271 (None, None, None, Some(record), None) => into_input!(Input::Record(input_id, record)),
272 (None, None, None, None, Some(_)) => Input::ExternalRecord(input_id),
273 (None, None, None, None, None) => bail!("Missing input '{input_id}' in transition '{transition_id}'"),
274 _ => bail!("Found multiple inputs for the input ID '{input_id}' in transition '{transition_id}'"),
275 };
276
277 Ok(input)
278 };
279
280 match self.id_map().get_confirmed(transition_id)? {
282 Some(Cow::Borrowed(ids)) => ids.iter().map(|input_id| construct_input(*input_id)).collect(),
283 Some(Cow::Owned(ids)) => ids.iter().map(|input_id| construct_input(*input_id)).collect(),
284 None => Ok(vec![]),
285 }
286 }
287}
288
289#[derive(Clone)]
291pub struct InputStore<N: Network, I: InputStorage<N>> {
292 constant: I::ConstantMap,
294 public: I::PublicMap,
296 private: I::PrivateMap,
298 record: I::RecordMap,
300 record_tag: I::RecordTagMap,
302 external_record: I::ExternalRecordMap,
304 storage: I,
306}
307
308impl<N: Network, I: InputStorage<N>> InputStore<N, I> {
309 pub fn open<S: Into<StorageMode>>(storage: S) -> Result<Self> {
311 let storage = I::open(storage)?;
313 Ok(Self {
315 constant: storage.constant_map().clone(),
316 public: storage.public_map().clone(),
317 private: storage.private_map().clone(),
318 record: storage.record_map().clone(),
319 record_tag: storage.record_tag_map().clone(),
320 external_record: storage.external_record_map().clone(),
321 storage,
322 })
323 }
324
325 pub fn from(storage: I) -> Self {
327 Self {
328 constant: storage.constant_map().clone(),
329 public: storage.public_map().clone(),
330 private: storage.private_map().clone(),
331 record: storage.record_map().clone(),
332 record_tag: storage.record_tag_map().clone(),
333 external_record: storage.external_record_map().clone(),
334 storage,
335 }
336 }
337
338 pub fn insert(&self, transition_id: N::TransitionID, inputs: &[Input<N>]) -> Result<()> {
340 self.storage.insert(transition_id, inputs)
341 }
342
343 pub fn remove(&self, transition_id: &N::TransitionID) -> Result<()> {
345 self.storage.remove(transition_id)
346 }
347
348 pub fn start_atomic(&self) {
350 self.storage.start_atomic();
351 }
352
353 pub fn is_atomic_in_progress(&self) -> bool {
355 self.storage.is_atomic_in_progress()
356 }
357
358 pub fn atomic_checkpoint(&self) {
360 self.storage.atomic_checkpoint();
361 }
362
363 pub fn clear_latest_checkpoint(&self) {
365 self.storage.clear_latest_checkpoint();
366 }
367
368 pub fn atomic_rewind(&self) {
370 self.storage.atomic_rewind();
371 }
372
373 pub fn abort_atomic(&self) {
375 self.storage.abort_atomic();
376 }
377
378 pub fn finish_atomic(&self) -> Result<()> {
380 self.storage.finish_atomic()
381 }
382
383 pub fn storage_mode(&self) -> &StorageMode {
385 self.storage.storage_mode()
386 }
387}
388
389impl<N: Network, I: InputStorage<N>> InputStore<N, I> {
390 pub fn get_input_ids(&self, transition_id: &N::TransitionID) -> Result<Vec<Field<N>>> {
392 self.storage.get_ids(transition_id)
393 }
394
395 pub fn get_inputs(&self, transition_id: &N::TransitionID) -> Result<Vec<Input<N>>> {
397 self.storage.get(transition_id)
398 }
399}
400
401impl<N: Network, I: InputStorage<N>> InputStore<N, I> {
402 pub fn find_transition_id(&self, input_id: &Field<N>) -> Result<Option<N::TransitionID>> {
404 self.storage.find_transition_id(input_id)
405 }
406}
407
408impl<N: Network, I: InputStorage<N>> InputStore<N, I> {
409 pub fn contains_input_id(&self, input_id: &Field<N>) -> Result<bool> {
411 self.storage.reverse_id_map().contains_key_confirmed(input_id)
412 }
413
414 pub fn contains_serial_number(&self, serial_number: &Field<N>) -> Result<bool> {
416 self.record.contains_key_confirmed(serial_number)
417 }
418
419 pub fn contains_tag(&self, tag: &Field<N>) -> Result<bool> {
421 self.record_tag.contains_key_confirmed(tag)
422 }
423}
424
425impl<N: Network, I: InputStorage<N>> InputStore<N, I> {
426 pub fn input_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
428 self.storage.reverse_id_map().keys_confirmed()
429 }
430
431 pub fn constant_input_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
433 self.constant.keys_confirmed()
434 }
435
436 pub fn public_input_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
438 self.public.keys_confirmed()
439 }
440
441 pub fn private_input_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
443 self.private.keys_confirmed()
444 }
445
446 pub fn serial_numbers(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
448 self.record.keys_confirmed()
449 }
450
451 pub fn external_input_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
453 self.external_record.keys_confirmed()
454 }
455}
456
457impl<N: Network, I: InputStorage<N>> InputStore<N, I> {
458 pub fn constant_inputs(&self) -> impl '_ + Iterator<Item = Cow<'_, Plaintext<N>>> {
460 self.constant.values_confirmed().flat_map(|input| match input {
461 Cow::Borrowed(Some(input)) => Some(Cow::Borrowed(input)),
462 Cow::Owned(Some(input)) => Some(Cow::Owned(input)),
463 _ => None,
464 })
465 }
466
467 pub fn public_inputs(&self) -> impl '_ + Iterator<Item = Cow<'_, Plaintext<N>>> {
469 self.public.values_confirmed().flat_map(|input| match input {
470 Cow::Borrowed(Some(input)) => Some(Cow::Borrowed(input)),
471 Cow::Owned(Some(input)) => Some(Cow::Owned(input)),
472 _ => None,
473 })
474 }
475
476 pub fn private_inputs(&self) -> impl '_ + Iterator<Item = Cow<'_, Ciphertext<N>>> {
478 self.private.values_confirmed().flat_map(|input| match input {
479 Cow::Borrowed(Some(input)) => Some(Cow::Borrowed(input)),
480 Cow::Owned(Some(input)) => Some(Cow::Owned(input)),
481 _ => None,
482 })
483 }
484
485 pub fn tags(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
487 self.record_tag.keys_confirmed()
488 }
489}
490
491#[cfg(test)]
492mod tests {
493 use super::*;
494 use crate::helpers::memory::InputMemory;
495
496 #[test]
497 fn test_insert_get_remove() {
498 for (transition_id, input) in ledger_test_helpers::sample_inputs() {
500 let input_store = InputMemory::open(StorageMode::Test(None)).unwrap();
502
503 let candidate = input_store.get(&transition_id).unwrap();
505 assert!(candidate.is_empty());
506
507 input_store.insert(transition_id, &[input.clone()]).unwrap();
509
510 let candidate = input_store.get(&transition_id).unwrap();
512 assert_eq!(vec![input.clone()], candidate);
513
514 input_store.remove(&transition_id).unwrap();
516
517 let candidate = input_store.get(&transition_id).unwrap();
519 assert!(candidate.is_empty());
520 }
521 }
522
523 #[test]
524 fn test_find_transition_id() {
525 for (transition_id, input) in ledger_test_helpers::sample_inputs() {
527 let input_store = InputMemory::open(StorageMode::Test(None)).unwrap();
529
530 let candidate = input_store.get(&transition_id).unwrap();
532 assert!(candidate.is_empty());
533
534 let candidate = input_store.find_transition_id(input.id()).unwrap();
536 assert!(candidate.is_none());
537
538 input_store.insert(transition_id, &[input.clone()]).unwrap();
540
541 let candidate = input_store.find_transition_id(input.id()).unwrap();
543 assert_eq!(Some(transition_id), candidate);
544
545 input_store.remove(&transition_id).unwrap();
547
548 let candidate = input_store.find_transition_id(input.id()).unwrap();
550 assert!(candidate.is_none());
551 }
552 }
553}