/ ledger / store / src / transition / output.rs
output.rs
  1  // Copyright (c) 2019-2025 Alpha-Delta Network Inc.
  2  // This file is part of the alphavm library.
  3  
  4  // Licensed under the Apache License, Version 2.0 (the "License");
  5  // you may not use this file except in compliance with the License.
  6  // You may obtain a copy of the License at:
  7  
  8  // http://www.apache.org/licenses/LICENSE-2.0
  9  
 10  // Unless required by applicable law or agreed to in writing, software
 11  // distributed under the License is distributed on an "AS IS" BASIS,
 12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  // See the License for the specific language governing permissions and
 14  // limitations under the License.
 15  
 16  use crate::{
 17      atomic_batch_scope,
 18      helpers::{Map, MapRead},
 19  };
 20  use alphavm_ledger_block::Output;
 21  use console::{
 22      network::prelude::*,
 23      program::{Ciphertext, Future, Plaintext, Record},
 24      types::{Field, Group},
 25  };
 26  
 27  use alphastd_storage::StorageMode;
 28  use anyhow::Result;
 29  use std::borrow::Cow;
 30  
 31  /// A trait for transition output storage.
 32  pub trait OutputStorage<N: Network>: Clone + Send + Sync {
 33      /// The mapping of `transition ID` to `output IDs`.
 34      type IDMap: for<'a> Map<'a, N::TransitionID, Vec<Field<N>>>;
 35      /// The mapping of `output ID` to `transition ID`.
 36      type ReverseIDMap: for<'a> Map<'a, Field<N>, N::TransitionID>;
 37      /// The mapping of `plaintext hash` to `(optional) plaintext`.
 38      type ConstantMap: for<'a> Map<'a, Field<N>, Option<Plaintext<N>>>;
 39      /// The mapping of `plaintext hash` to `(optional) plaintext`.
 40      type PublicMap: for<'a> Map<'a, Field<N>, Option<Plaintext<N>>>;
 41      /// The mapping of `ciphertext hash` to `(optional) ciphertext`.
 42      type PrivateMap: for<'a> Map<'a, Field<N>, Option<Ciphertext<N>>>;
 43      /// The mapping of `commitment` to `(checksum, (optional) record ciphertext)`.
 44      type RecordMap: for<'a> Map<'a, Field<N>, (Field<N>, Option<Record<N, Ciphertext<N>>>)>;
 45      /// The mapping of `record nonce` to `commitment`.
 46      type RecordNonceMap: for<'a> Map<'a, Group<N>, Field<N>>;
 47      /// The mapping of `record nonce` to `sender ciphertext`.
 48      type RecordSenderMap: for<'a> Map<'a, Group<N>, Option<Field<N>>>;
 49      /// The mapping of `external hash` to `()`. Note: This is **not** the record commitment.
 50      type ExternalRecordMap: for<'a> Map<'a, Field<N>, ()>;
 51      /// The mapping of `future hash` to `(optional) future`.
 52      type FutureMap: for<'a> Map<'a, Field<N>, Option<Future<N>>>;
 53  
 54      /// Initializes the transition output storage.
 55      fn open<S: Into<StorageMode>>(storage: S) -> Result<Self>;
 56  
 57      /// Returns the ID map.
 58      fn id_map(&self) -> &Self::IDMap;
 59      /// Returns the reverse ID map.
 60      fn reverse_id_map(&self) -> &Self::ReverseIDMap;
 61      /// Returns the constant map.
 62      fn constant_map(&self) -> &Self::ConstantMap;
 63      /// Returns the public map.
 64      fn public_map(&self) -> &Self::PublicMap;
 65      /// Returns the private map.
 66      fn private_map(&self) -> &Self::PrivateMap;
 67      /// Returns the record map.
 68      fn record_map(&self) -> &Self::RecordMap;
 69      /// Returns the record nonce map.
 70      fn record_nonce_map(&self) -> &Self::RecordNonceMap;
 71      /// Returns the record sender map.
 72      fn record_sender_map(&self) -> &Self::RecordSenderMap;
 73      /// Returns the external record map.
 74      fn external_record_map(&self) -> &Self::ExternalRecordMap;
 75      /// Returns the future map.
 76      fn future_map(&self) -> &Self::FutureMap;
 77  
 78      /// Returns the storage mode.
 79      fn storage_mode(&self) -> &StorageMode;
 80  
 81      /// Starts an atomic batch write operation.
 82      fn start_atomic(&self) {
 83          self.id_map().start_atomic();
 84          self.reverse_id_map().start_atomic();
 85          self.constant_map().start_atomic();
 86          self.public_map().start_atomic();
 87          self.private_map().start_atomic();
 88          self.record_map().start_atomic();
 89          self.record_nonce_map().start_atomic();
 90          self.record_sender_map().start_atomic();
 91          self.external_record_map().start_atomic();
 92          self.future_map().start_atomic();
 93      }
 94  
 95      /// Checks if an atomic batch is in progress.
 96      fn is_atomic_in_progress(&self) -> bool {
 97          self.id_map().is_atomic_in_progress()
 98              || self.reverse_id_map().is_atomic_in_progress()
 99              || self.constant_map().is_atomic_in_progress()
100              || self.public_map().is_atomic_in_progress()
101              || self.private_map().is_atomic_in_progress()
102              || self.record_map().is_atomic_in_progress()
103              || self.record_nonce_map().is_atomic_in_progress()
104              || self.record_sender_map().is_atomic_in_progress()
105              || self.external_record_map().is_atomic_in_progress()
106              || self.future_map().is_atomic_in_progress()
107      }
108  
109      /// Checkpoints the atomic batch.
110      fn atomic_checkpoint(&self) {
111          self.id_map().atomic_checkpoint();
112          self.reverse_id_map().atomic_checkpoint();
113          self.constant_map().atomic_checkpoint();
114          self.public_map().atomic_checkpoint();
115          self.private_map().atomic_checkpoint();
116          self.record_map().atomic_checkpoint();
117          self.record_nonce_map().atomic_checkpoint();
118          self.record_sender_map().atomic_checkpoint();
119          self.external_record_map().atomic_checkpoint();
120          self.future_map().atomic_checkpoint();
121      }
122  
123      /// Clears the latest atomic batch checkpoint.
124      fn clear_latest_checkpoint(&self) {
125          self.id_map().clear_latest_checkpoint();
126          self.reverse_id_map().clear_latest_checkpoint();
127          self.constant_map().clear_latest_checkpoint();
128          self.public_map().clear_latest_checkpoint();
129          self.private_map().clear_latest_checkpoint();
130          self.record_map().clear_latest_checkpoint();
131          self.record_nonce_map().clear_latest_checkpoint();
132          self.record_sender_map().clear_latest_checkpoint();
133          self.external_record_map().clear_latest_checkpoint();
134          self.future_map().clear_latest_checkpoint();
135      }
136  
137      /// Rewinds the atomic batch to the previous checkpoint.
138      fn atomic_rewind(&self) {
139          self.id_map().atomic_rewind();
140          self.reverse_id_map().atomic_rewind();
141          self.constant_map().atomic_rewind();
142          self.public_map().atomic_rewind();
143          self.private_map().atomic_rewind();
144          self.record_map().atomic_rewind();
145          self.record_nonce_map().atomic_rewind();
146          self.record_sender_map().atomic_rewind();
147          self.external_record_map().atomic_rewind();
148          self.future_map().atomic_rewind();
149      }
150  
151      /// Aborts an atomic batch write operation.
152      fn abort_atomic(&self) {
153          self.id_map().abort_atomic();
154          self.reverse_id_map().abort_atomic();
155          self.constant_map().abort_atomic();
156          self.public_map().abort_atomic();
157          self.private_map().abort_atomic();
158          self.record_map().abort_atomic();
159          self.record_nonce_map().abort_atomic();
160          self.record_sender_map().abort_atomic();
161          self.external_record_map().abort_atomic();
162          self.future_map().abort_atomic();
163      }
164  
165      /// Finishes an atomic batch write operation.
166      fn finish_atomic(&self) -> Result<()> {
167          self.id_map().finish_atomic()?;
168          self.reverse_id_map().finish_atomic()?;
169          self.constant_map().finish_atomic()?;
170          self.public_map().finish_atomic()?;
171          self.private_map().finish_atomic()?;
172          self.record_map().finish_atomic()?;
173          self.record_nonce_map().finish_atomic()?;
174          self.record_sender_map().finish_atomic()?;
175          self.external_record_map().finish_atomic()?;
176          self.future_map().finish_atomic()
177      }
178  
179      /// Stores the given `(transition ID, output)` pair into storage.
180      fn insert(&self, transition_id: N::TransitionID, outputs: &[Output<N>]) -> Result<()> {
181          atomic_batch_scope!(self, {
182              // Store the output IDs.
183              self.id_map().insert(transition_id, outputs.iter().map(Output::id).copied().collect())?;
184  
185              // Store the outputs.
186              for output in outputs {
187                  // Store the reverse output ID.
188                  self.reverse_id_map().insert(*output.id(), transition_id)?;
189                  // Store the output.
190                  match output.clone() {
191                      Output::Constant(output_id, constant) => self.constant_map().insert(output_id, constant)?,
192                      Output::Public(output_id, public) => self.public_map().insert(output_id, public)?,
193                      Output::Private(output_id, private) => self.private_map().insert(output_id, private)?,
194                      Output::Record(commitment, checksum, optional_record, optional_sender) => {
195                          // If the optional record ciphertext exists, insert the record nonce.
196                          if let Some(record) = &optional_record {
197                              // Insert the record nonce to commitment.
198                              self.record_nonce_map().insert(*record.nonce(), commitment)?;
199                              // If the optional sender ciphertext exists, insert the record sender.
200                              self.record_sender_map().insert(*record.nonce(), optional_sender)?;
201                          }
202                          // Insert the record entry.
203                          self.record_map().insert(commitment, (checksum, optional_record))?
204                      }
205                      Output::ExternalRecord(output_id) => self.external_record_map().insert(output_id, ())?,
206                      Output::Future(output_id, future) => self.future_map().insert(output_id, future)?,
207                  }
208              }
209  
210              Ok(())
211          })
212      }
213  
214      /// Removes the output for the given `transition ID`.
215      fn remove(&self, transition_id: &N::TransitionID) -> Result<()> {
216          // Retrieve the output IDs.
217          let output_ids: Vec<_> = match self.id_map().get_confirmed(transition_id)? {
218              Some(Cow::Borrowed(ids)) => ids.to_vec(),
219              Some(Cow::Owned(ids)) => ids.into_iter().collect(),
220              None => return Ok(()),
221          };
222  
223          atomic_batch_scope!(self, {
224              // Remove the output IDs.
225              self.id_map().remove(transition_id)?;
226  
227              // Remove the outputs.
228              for output_id in output_ids {
229                  // Remove the reverse output ID.
230                  self.reverse_id_map().remove(&output_id)?;
231  
232                  // If the output is a record, remove the record nonce and record sender.
233                  if let Some(record) = self.record_map().get_confirmed(&output_id)? {
234                      if let Some(record) = &record.1 {
235                          // Remove the record nonce.
236                          self.record_nonce_map().remove(record.nonce())?;
237                          // Remove the record sender, if it exists.
238                          self.record_sender_map().remove(record.nonce())?;
239                      }
240                  }
241  
242                  // Remove the output.
243                  self.constant_map().remove(&output_id)?;
244                  self.public_map().remove(&output_id)?;
245                  self.private_map().remove(&output_id)?;
246                  self.record_map().remove(&output_id)?;
247                  self.external_record_map().remove(&output_id)?;
248                  self.future_map().remove(&output_id)?;
249              }
250  
251              Ok(())
252          })
253      }
254  
255      /// Returns the transition ID that contains the given `output ID`.
256      fn find_transition_id(&self, output_id: &Field<N>) -> Result<Option<N::TransitionID>> {
257          match self.reverse_id_map().get_confirmed(output_id)? {
258              Some(Cow::Borrowed(transition_id)) => Ok(Some(*transition_id)),
259              Some(Cow::Owned(transition_id)) => Ok(Some(transition_id)),
260              None => Ok(None),
261          }
262      }
263  
264      /// Returns the output IDs for the given `transition ID`.
265      fn get_ids(&self, transition_id: &N::TransitionID) -> Result<Vec<Field<N>>> {
266          // Retrieve the output IDs.
267          match self.id_map().get_confirmed(transition_id)? {
268              Some(Cow::Borrowed(outputs)) => Ok(outputs.to_vec()),
269              Some(Cow::Owned(outputs)) => Ok(outputs),
270              None => Ok(vec![]),
271          }
272      }
273  
274      /// Returns the output for the given `transition ID`.
275      fn get(&self, transition_id: &N::TransitionID) -> Result<Vec<Output<N>>> {
276          // Constructs the output given the output ID and output value.
277          macro_rules! into_output {
278              (Output::Record($output_id:ident, $output:expr)) => {
279                  match $output {
280                      Cow::Borrowed((checksum, Some(record))) => Output::Record($output_id, *checksum, Some(record.clone()), match self.record_sender_map().get_confirmed(&record.nonce())? {
281                          Some(sender) => *sender,
282                          None => None,
283                      }),
284                      Cow::Borrowed((checksum, None)) => Output::Record($output_id, *checksum, None, None),
285                      Cow::Owned((checksum, Some(record))) => {
286                          let record_nonce = *record.nonce(); // We extract the nonce here to avoid cloning the entire record.
287                          Output::Record($output_id, checksum, Some(record), match self.record_sender_map().get_confirmed(&record_nonce)? {
288                              Some(sender) => *sender,
289                              None => None,
290                          })
291                      },
292                      Cow::Owned((checksum, None)) => Output::Record($output_id, checksum, None, None),
293                  }
294              };
295              (Output::$Variant:ident($output_id:ident, $output:expr)) => {
296                  match $output {
297                      Cow::Borrowed(output) => Output::$Variant($output_id, output.clone()),
298                      Cow::Owned(output) => Output::$Variant($output_id, output),
299                  }
300              };
301          }
302  
303          // A helper function to construct the output given the output ID.
304          let construct_output = |output_id| {
305              if let Some(constant) = self.constant_map().get_confirmed(&output_id)? {
306                  return Ok(into_output!(Output::Constant(output_id, constant)));
307              }
308              if let Some(public) = self.public_map().get_confirmed(&output_id)? {
309                  return Ok(into_output!(Output::Public(output_id, public)));
310              }
311              if let Some(private) = self.private_map().get_confirmed(&output_id)? {
312                  return Ok(into_output!(Output::Private(output_id, private)));
313              }
314              if let Some(record) = self.record_map().get_confirmed(&output_id)? {
315                  return Ok(into_output!(Output::Record(output_id, record)));
316              }
317              if self.external_record_map().get_confirmed(&output_id)?.is_some() {
318                  return Ok(Output::ExternalRecord(output_id));
319              }
320              if let Some(future) = self.future_map().get_confirmed(&output_id)? {
321                  return Ok(into_output!(Output::Future(output_id, future)));
322              }
323  
324              bail!("Missing output '{output_id}' in transition '{transition_id}'")
325          };
326  
327          // Retrieve the output IDs.
328          match self.id_map().get_confirmed(transition_id)? {
329              Some(Cow::Borrowed(ids)) => ids.iter().map(|output_id| construct_output(*output_id)).collect(),
330              Some(Cow::Owned(ids)) => ids.iter().map(|output_id| construct_output(*output_id)).collect(),
331              None => Ok(vec![]),
332          }
333      }
334  }
335  
336  /// The transition output store.
337  #[derive(Clone)]
338  pub struct OutputStore<N: Network, O: OutputStorage<N>> {
339      /// The map of constant outputs.
340      constant: O::ConstantMap,
341      /// The map of public outputs.
342      public: O::PublicMap,
343      /// The map of private outputs.
344      private: O::PrivateMap,
345      /// The map of record outputs.
346      record: O::RecordMap,
347      /// The map of record nonces.
348      record_nonce: O::RecordNonceMap,
349      /// The map of record senders.
350      record_sender: O::RecordSenderMap,
351      /// The map of external record outputs.
352      external_record: O::ExternalRecordMap,
353      /// The map of future outputs.
354      future: O::FutureMap,
355      /// The output storage.
356      storage: O,
357  }
358  
359  impl<N: Network, O: OutputStorage<N>> OutputStore<N, O> {
360      /// Initializes the transition output store.
361      pub fn open<S: Into<StorageMode>>(storage: S) -> Result<Self> {
362          // Initialize a new transition output storage.
363          let storage = O::open(storage)?;
364          // Return the transition output store.
365          Ok(Self {
366              constant: storage.constant_map().clone(),
367              public: storage.public_map().clone(),
368              private: storage.private_map().clone(),
369              record: storage.record_map().clone(),
370              record_nonce: storage.record_nonce_map().clone(),
371              record_sender: storage.record_sender_map().clone(),
372              external_record: storage.external_record_map().clone(),
373              future: storage.future_map().clone(),
374              storage,
375          })
376      }
377  
378      /// Initializes a transition output store from storage.
379      pub fn from(storage: O) -> Self {
380          Self {
381              constant: storage.constant_map().clone(),
382              public: storage.public_map().clone(),
383              private: storage.private_map().clone(),
384              record: storage.record_map().clone(),
385              record_nonce: storage.record_nonce_map().clone(),
386              record_sender: storage.record_sender_map().clone(),
387              external_record: storage.external_record_map().clone(),
388              future: storage.future_map().clone(),
389              storage,
390          }
391      }
392  
393      /// Stores the given `(transition ID, output)` pair into storage.
394      pub fn insert(&self, transition_id: N::TransitionID, outputs: &[Output<N>]) -> Result<()> {
395          self.storage.insert(transition_id, outputs)
396      }
397  
398      /// Removes the output for the given `transition ID`.
399      pub fn remove(&self, transition_id: &N::TransitionID) -> Result<()> {
400          self.storage.remove(transition_id)
401      }
402  
403      /// Starts an atomic batch write operation.
404      pub fn start_atomic(&self) {
405          self.storage.start_atomic();
406      }
407  
408      /// Checks if an atomic batch is in progress.
409      pub fn is_atomic_in_progress(&self) -> bool {
410          self.storage.is_atomic_in_progress()
411      }
412  
413      /// Checkpoints the atomic batch.
414      pub fn atomic_checkpoint(&self) {
415          self.storage.atomic_checkpoint();
416      }
417  
418      /// Clears the latest atomic batch checkpoint.
419      pub fn clear_latest_checkpoint(&self) {
420          self.storage.clear_latest_checkpoint();
421      }
422  
423      /// Rewinds the atomic batch to the previous checkpoint.
424      pub fn atomic_rewind(&self) {
425          self.storage.atomic_rewind();
426      }
427  
428      /// Aborts an atomic batch write operation.
429      pub fn abort_atomic(&self) {
430          self.storage.abort_atomic();
431      }
432  
433      /// Finishes an atomic batch write operation.
434      pub fn finish_atomic(&self) -> Result<()> {
435          self.storage.finish_atomic()
436      }
437  
438      /// Returns the storage mode.
439      pub fn storage_mode(&self) -> &StorageMode {
440          self.storage.storage_mode()
441      }
442  }
443  
444  impl<N: Network, O: OutputStorage<N>> OutputStore<N, O> {
445      /// Returns the output IDs for the given `transition ID`.
446      pub fn get_output_ids(&self, transition_id: &N::TransitionID) -> Result<Vec<Field<N>>> {
447          self.storage.get_ids(transition_id)
448      }
449  
450      /// Returns the outputs for the given `transition ID`.
451      pub fn get_outputs(&self, transition_id: &N::TransitionID) -> Result<Vec<Output<N>>> {
452          self.storage.get(transition_id)
453      }
454  
455      /// Returns the record for the given `commitment`.
456      ///
457      /// If the record exists, `Ok(Some(record))` is returned.
458      /// If the record was purged, `Ok(None)` is returned.
459      /// If the record does not exist, `Err(error)` is returned.
460      pub fn get_record(&self, commitment: &Field<N>) -> Result<Option<Record<N, Ciphertext<N>>>> {
461          match self.record.get_confirmed(commitment) {
462              Ok(Some(Cow::Borrowed((_, Some(record))))) => Ok(Some((*record).clone())),
463              Ok(Some(Cow::Owned((_, Some(record))))) => Ok(Some(record)),
464              Ok(Some(Cow::Borrowed((_, None)))) => Ok(None),
465              Ok(Some(Cow::Owned((_, None)))) => Ok(None),
466              Ok(None) => bail!("Record '{commitment}' not found"),
467              Err(e) => Err(e),
468          }
469      }
470  
471      /// Returns the record sender ciphertext for the given `nonce`.
472      pub fn get_record_sender_ciphertext(&self, nonce: &Group<N>) -> Result<Option<Field<N>>> {
473          match self.record_sender.get_confirmed(nonce)? {
474              Some(Cow::Borrowed(sender)) => Ok(*sender),
475              Some(Cow::Owned(sender)) => Ok(sender),
476              None => Ok(None),
477          }
478      }
479  }
480  
481  impl<N: Network, O: OutputStorage<N>> OutputStore<N, O> {
482      /// Returns the transition ID that contains the given `output ID`.
483      pub fn find_transition_id(&self, output_id: &Field<N>) -> Result<Option<N::TransitionID>> {
484          self.storage.find_transition_id(output_id)
485      }
486  }
487  
488  impl<N: Network, O: OutputStorage<N>> OutputStore<N, O> {
489      /// Returns `true` if the given output ID exists.
490      pub fn contains_output_id(&self, output_id: &Field<N>) -> Result<bool> {
491          self.storage.reverse_id_map().contains_key_confirmed(output_id)
492      }
493  
494      /// Returns `true` if the given commitment exists.
495      pub fn contains_commitment(&self, commitment: &Field<N>) -> Result<bool> {
496          self.record.contains_key_confirmed(commitment)
497      }
498  
499      /// Returns `true` if the given checksum exists.
500      pub fn contains_checksum(&self, checksum: &Field<N>) -> bool {
501          self.checksums().contains(checksum)
502      }
503  
504      /// Returns `true` if the given nonce exists.
505      pub fn contains_nonce(&self, nonce: &Group<N>) -> Result<bool> {
506          self.record_nonce.contains_key_confirmed(nonce)
507      }
508  }
509  
510  impl<N: Network, O: OutputStorage<N>> OutputStore<N, O> {
511      /// Returns an iterator over the output IDs, for all transition outputs.
512      pub fn output_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
513          self.storage.reverse_id_map().keys_confirmed()
514      }
515  
516      /// Returns an iterator over the constant output IDs, for all transition outputs that are constant.
517      pub fn constant_output_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
518          self.constant.keys_confirmed()
519      }
520  
521      /// Returns an iterator over the public output IDs, for all transition outputs that are public.
522      pub fn public_output_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
523          self.public.keys_confirmed()
524      }
525  
526      /// Returns an iterator over the private output IDs, for all transition outputs that are private.
527      pub fn private_output_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
528          self.private.keys_confirmed()
529      }
530  
531      /// Returns an iterator over the commitments, for all transition outputs that are records.
532      pub fn commitments(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
533          self.record.keys_confirmed()
534      }
535  
536      /// Returns an iterator over the external record output IDs, for all transition outputs that are external records.
537      pub fn external_output_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
538          self.external_record.keys_confirmed()
539      }
540  
541      /// Returns an iterator over the future output IDs, for all transition outputs that are future outputs.
542      pub fn future_output_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
543          self.future.keys_confirmed()
544      }
545  }
546  
547  impl<N: Network, I: OutputStorage<N>> OutputStore<N, I> {
548      /// Returns an iterator over the constant outputs, for all transitions.
549      pub fn constant_outputs(&self) -> impl '_ + Iterator<Item = Cow<'_, Plaintext<N>>> {
550          self.constant.values_confirmed().flat_map(|output| match output {
551              Cow::Borrowed(Some(output)) => Some(Cow::Borrowed(output)),
552              Cow::Owned(Some(output)) => Some(Cow::Owned(output)),
553              _ => None,
554          })
555      }
556  
557      /// Returns an iterator over the constant outputs, for all transitions.
558      pub fn public_outputs(&self) -> impl '_ + Iterator<Item = Cow<'_, Plaintext<N>>> {
559          self.public.values_confirmed().flat_map(|output| match output {
560              Cow::Borrowed(Some(output)) => Some(Cow::Borrowed(output)),
561              Cow::Owned(Some(output)) => Some(Cow::Owned(output)),
562              _ => None,
563          })
564      }
565  
566      /// Returns an iterator over the private outputs, for all transitions.
567      pub fn private_outputs(&self) -> impl '_ + Iterator<Item = Cow<'_, Ciphertext<N>>> {
568          self.private.values_confirmed().flat_map(|output| match output {
569              Cow::Borrowed(Some(output)) => Some(Cow::Borrowed(output)),
570              Cow::Owned(Some(output)) => Some(Cow::Owned(output)),
571              _ => None,
572          })
573      }
574  
575      /// Returns an iterator over the checksums, for all transition outputs that are records.
576      pub fn checksums(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
577          self.record.values_confirmed().map(|output| match output {
578              Cow::Borrowed((checksum, _)) => Cow::Borrowed(checksum),
579              Cow::Owned((checksum, _)) => Cow::Owned(checksum),
580          })
581      }
582  
583      /// Returns an iterator over the nonces, for all transition outputs that are records.
584      pub fn nonces(&self) -> impl '_ + Iterator<Item = Cow<'_, Group<N>>> {
585          self.record_nonce.keys_confirmed()
586      }
587  
588      /// Returns an iterator over the `(commitment, record)` pairs, for all transition outputs that are records.
589      #[allow(clippy::type_complexity)]
590      pub fn records(&self) -> impl '_ + Iterator<Item = (Cow<'_, Field<N>>, Cow<'_, Record<N, Ciphertext<N>>>)> {
591          self.record.iter_confirmed().flat_map(|(commitment, output)| match output {
592              Cow::Borrowed((_, Some(record))) => Some((commitment, Cow::Borrowed(record))),
593              Cow::Owned((_, Some(record))) => Some((commitment, Cow::Owned(record))),
594              _ => None,
595          })
596      }
597  
598      /// Returns an iterator over the future outputs, for all transitions.
599      pub fn future_outputs(&self) -> impl '_ + Iterator<Item = Cow<'_, Future<N>>> {
600          self.future.values_confirmed().flat_map(|output| match output {
601              Cow::Borrowed(Some(output)) => Some(Cow::Borrowed(output)),
602              Cow::Owned(Some(output)) => Some(Cow::Owned(output)),
603              _ => None,
604          })
605      }
606  }
607  
608  #[cfg(test)]
609  mod tests {
610      use super::*;
611      use crate::helpers::memory::OutputMemory;
612  
613      #[test]
614      fn test_insert_get_remove() {
615          // Sample the transition outputs.
616          for (transition_id, output) in alphavm_ledger_test_helpers::sample_outputs() {
617              // Initialize a new output store.
618              let output_store = OutputMemory::open(StorageMode::Test(None)).unwrap();
619  
620              // Ensure the transition output does not exist.
621              let candidate = output_store.get(&transition_id).unwrap();
622              assert!(candidate.is_empty());
623  
624              // Insert the transition output.
625              output_store.insert(transition_id, &[output.clone()]).unwrap();
626  
627              // Retrieve the transition output.
628              let candidate = output_store.get(&transition_id).unwrap();
629              assert_eq!(vec![output.clone()], candidate);
630  
631              // Remove the transition output.
632              output_store.remove(&transition_id).unwrap();
633  
634              // Retrieve the transition output.
635              let candidate = output_store.get(&transition_id).unwrap();
636              assert!(candidate.is_empty());
637          }
638      }
639  
640      #[test]
641      fn test_find_transition_id() {
642          // Sample the transition outputs.
643          for (transition_id, output) in alphavm_ledger_test_helpers::sample_outputs() {
644              // Initialize a new output store.
645              let output_store = OutputMemory::open(StorageMode::Test(None)).unwrap();
646  
647              // Ensure the transition output does not exist.
648              let candidate = output_store.get(&transition_id).unwrap();
649              assert!(candidate.is_empty());
650  
651              // Ensure the transition ID is not found.
652              let candidate = output_store.find_transition_id(output.id()).unwrap();
653              assert!(candidate.is_none());
654  
655              // Insert the transition output.
656              output_store.insert(transition_id, &[output.clone()]).unwrap();
657  
658              // Find the transition ID.
659              let candidate = output_store.find_transition_id(output.id()).unwrap();
660              assert_eq!(Some(transition_id), candidate);
661  
662              // Remove the transition output.
663              output_store.remove(&transition_id).unwrap();
664  
665              // Ensure the transition ID is not found.
666              let candidate = output_store.find_transition_id(output.id()).unwrap();
667              assert!(candidate.is_none());
668          }
669      }
670  }