/ ledger / store / src / transition / mod.rs
mod.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  mod input;
 17  pub use input::*;
 18  
 19  mod output;
 20  pub use output::*;
 21  
 22  use crate::{
 23      atomic_batch_scope,
 24      helpers::{Map, MapRead},
 25  };
 26  use alphavm_ledger_block::{Input, Output, Transition};
 27  use console::{
 28      network::prelude::*,
 29      program::{Ciphertext, Identifier, Plaintext, ProgramID, Record},
 30      types::{Field, Group},
 31  };
 32  
 33  use alphastd_storage::StorageMode;
 34  use anyhow::Result;
 35  use std::borrow::Cow;
 36  
 37  /// A trait for transition storage.
 38  pub trait TransitionStorage<N: Network>: Clone + Send + Sync {
 39      /// The transition program IDs and function names.
 40      type LocatorMap: for<'a> Map<'a, N::TransitionID, (ProgramID<N>, Identifier<N>)>;
 41      /// The transition inputs.
 42      type InputStorage: InputStorage<N>;
 43      /// The transition outputs.
 44      type OutputStorage: OutputStorage<N>;
 45      /// The transition public keys.
 46      type TPKMap: for<'a> Map<'a, N::TransitionID, Group<N>>;
 47      /// The mapping of `transition public key` to `transition ID`.
 48      type ReverseTPKMap: for<'a> Map<'a, Group<N>, N::TransitionID>;
 49      /// The transition commitments.
 50      type TCMMap: for<'a> Map<'a, N::TransitionID, Field<N>>;
 51      /// The mapping of `transition commitment` to `transition ID`.
 52      type ReverseTCMMap: for<'a> Map<'a, Field<N>, N::TransitionID>;
 53      /// The signer commitments.
 54      type SCMMap: for<'a> Map<'a, N::TransitionID, Field<N>>;
 55  
 56      /// Initializes the transition storage.
 57      fn open<S: Into<StorageMode>>(storage: S) -> Result<Self>;
 58  
 59      /// Returns the transition program IDs and function names.
 60      fn locator_map(&self) -> &Self::LocatorMap;
 61      /// Returns the transition input store.
 62      fn input_store(&self) -> &InputStore<N, Self::InputStorage>;
 63      /// Returns the transition output store.
 64      fn output_store(&self) -> &OutputStore<N, Self::OutputStorage>;
 65      /// Returns the transition public keys map.
 66      fn tpk_map(&self) -> &Self::TPKMap;
 67      /// Returns the reverse `tpk` map.
 68      fn reverse_tpk_map(&self) -> &Self::ReverseTPKMap;
 69      /// Returns the transition commitments map.
 70      fn tcm_map(&self) -> &Self::TCMMap;
 71      /// Returns the reverse `tcm` map.
 72      fn reverse_tcm_map(&self) -> &Self::ReverseTCMMap;
 73      /// Returns the signer commitments map.
 74      fn scm_map(&self) -> &Self::SCMMap;
 75  
 76      /// Returns the storage mode.
 77      fn storage_mode(&self) -> &StorageMode {
 78          debug_assert!(self.input_store().storage_mode() == self.output_store().storage_mode());
 79          self.input_store().storage_mode()
 80      }
 81  
 82      /// Starts an atomic batch write operation.
 83      fn start_atomic(&self) {
 84          self.locator_map().start_atomic();
 85          self.input_store().start_atomic();
 86          self.output_store().start_atomic();
 87          self.tpk_map().start_atomic();
 88          self.reverse_tpk_map().start_atomic();
 89          self.tcm_map().start_atomic();
 90          self.reverse_tcm_map().start_atomic();
 91          self.scm_map().start_atomic();
 92      }
 93  
 94      /// Checks if an atomic batch is in progress.
 95      fn is_atomic_in_progress(&self) -> bool {
 96          self.locator_map().is_atomic_in_progress()
 97              || self.input_store().is_atomic_in_progress()
 98              || self.output_store().is_atomic_in_progress()
 99              || self.tpk_map().is_atomic_in_progress()
100              || self.reverse_tpk_map().is_atomic_in_progress()
101              || self.tcm_map().is_atomic_in_progress()
102              || self.reverse_tcm_map().is_atomic_in_progress()
103              || self.scm_map().is_atomic_in_progress()
104      }
105  
106      /// Checkpoints the atomic batch.
107      fn atomic_checkpoint(&self) {
108          self.locator_map().atomic_checkpoint();
109          self.input_store().atomic_checkpoint();
110          self.output_store().atomic_checkpoint();
111          self.tpk_map().atomic_checkpoint();
112          self.reverse_tpk_map().atomic_checkpoint();
113          self.tcm_map().atomic_checkpoint();
114          self.reverse_tcm_map().atomic_checkpoint();
115          self.scm_map().atomic_checkpoint();
116      }
117  
118      /// Clears the latest atomic batch checkpoint.
119      fn clear_latest_checkpoint(&self) {
120          self.locator_map().clear_latest_checkpoint();
121          self.input_store().clear_latest_checkpoint();
122          self.output_store().clear_latest_checkpoint();
123          self.tpk_map().clear_latest_checkpoint();
124          self.reverse_tpk_map().clear_latest_checkpoint();
125          self.tcm_map().clear_latest_checkpoint();
126          self.reverse_tcm_map().clear_latest_checkpoint();
127          self.scm_map().clear_latest_checkpoint();
128      }
129  
130      /// Rewinds the atomic batch to the previous checkpoint.
131      fn atomic_rewind(&self) {
132          self.locator_map().atomic_rewind();
133          self.input_store().atomic_rewind();
134          self.output_store().atomic_rewind();
135          self.tpk_map().atomic_rewind();
136          self.reverse_tpk_map().atomic_rewind();
137          self.tcm_map().atomic_rewind();
138          self.reverse_tcm_map().atomic_rewind();
139          self.scm_map().atomic_rewind();
140      }
141  
142      /// Aborts an atomic batch write operation.
143      fn abort_atomic(&self) {
144          self.locator_map().abort_atomic();
145          self.input_store().abort_atomic();
146          self.output_store().abort_atomic();
147          self.tpk_map().abort_atomic();
148          self.reverse_tpk_map().abort_atomic();
149          self.tcm_map().abort_atomic();
150          self.reverse_tcm_map().abort_atomic();
151          self.scm_map().abort_atomic();
152      }
153  
154      /// Finishes an atomic batch write operation.
155      fn finish_atomic(&self) -> Result<()> {
156          self.locator_map().finish_atomic()?;
157          self.input_store().finish_atomic()?;
158          self.output_store().finish_atomic()?;
159          self.tpk_map().finish_atomic()?;
160          self.reverse_tpk_map().finish_atomic()?;
161          self.tcm_map().finish_atomic()?;
162          self.reverse_tcm_map().finish_atomic()?;
163          self.scm_map().finish_atomic()
164      }
165  
166      /// Stores the given `transition` into storage.
167      fn insert(&self, transition: &Transition<N>) -> Result<()> {
168          atomic_batch_scope!(self, {
169              // Retrieve the transition ID.
170              let transition_id = *transition.id();
171              // Store the program ID and function name.
172              self.locator_map().insert(transition_id, (*transition.program_id(), *transition.function_name()))?;
173              // Store the inputs.
174              self.input_store().insert(transition_id, transition.inputs())?;
175              // Store the outputs.
176              self.output_store().insert(transition_id, transition.outputs())?;
177              // Store `tpk`.
178              self.tpk_map().insert(transition_id, *transition.tpk())?;
179              // Store the reverse `tpk` entry.
180              self.reverse_tpk_map().insert(*transition.tpk(), transition_id)?;
181              // Store `tcm`.
182              self.tcm_map().insert(transition_id, *transition.tcm())?;
183              // Store the reverse `tcm` entry.
184              self.reverse_tcm_map().insert(*transition.tcm(), transition_id)?;
185              // Store `scm`.
186              self.scm_map().insert(transition_id, *transition.scm())?;
187  
188              Ok(())
189          })
190      }
191  
192      /// Removes the input for the given `transition ID`.
193      fn remove(&self, transition_id: &N::TransitionID) -> Result<()> {
194          // Retrieve the `tpk`.
195          let Some(tpk) = self.tpk_map().get_confirmed(transition_id)?.map(|x| *x) else {
196              return Ok(());
197          };
198          // Retrieve the `tcm`.
199          let Some(tcm) = self.tcm_map().get_confirmed(transition_id)?.map(|x| *x) else {
200              return Ok(());
201          };
202  
203          atomic_batch_scope!(self, {
204              // Remove the program ID and function name.
205              self.locator_map().remove(transition_id)?;
206              // Remove the inputs.
207              self.input_store().remove(transition_id)?;
208              // Remove the outputs.
209              self.output_store().remove(transition_id)?;
210              // Remove `tpk`.
211              self.tpk_map().remove(transition_id)?;
212              // Remove the reverse `tpk` entry.
213              self.reverse_tpk_map().remove(&tpk)?;
214              // Remove `tcm`.
215              self.tcm_map().remove(transition_id)?;
216              // Remove the reverse `tcm` entry.
217              self.reverse_tcm_map().remove(&tcm)?;
218              // Remove `scm`.
219              self.scm_map().remove(transition_id)?;
220  
221              Ok(())
222          })
223      }
224  
225      /// Returns the transition for the given `transition ID`.
226      fn get(&self, transition_id: &N::TransitionID) -> Result<Option<Transition<N>>> {
227          // Retrieve the program ID and function name.
228          let Some((program_id, function_name)) =
229              self.locator_map().get_confirmed(transition_id)?.map(|x| x.into_owned())
230          else {
231              return Ok(None);
232          };
233          // Retrieve the inputs.
234          let inputs = self.input_store().get_inputs(transition_id)?;
235          // Retrieve the outputs.
236          let outputs = self.output_store().get_outputs(transition_id)?;
237          // Retrieve `tpk`.
238          let tpk = self.tpk_map().get_confirmed(transition_id)?;
239          // Retrieve `tcm`.
240          let tcm = self.tcm_map().get_confirmed(transition_id)?;
241          // Retrieve `scm`.
242          let scm = self.scm_map().get_confirmed(transition_id)?;
243  
244          match (tpk, tcm, scm) {
245              (Some(tpk), Some(tcm), Some(scm)) => {
246                  // Construct the transition.
247                  let transition = Transition::new(
248                      program_id,
249                      function_name,
250                      inputs,
251                      outputs,
252                      tpk.into_owned(),
253                      tcm.into_owned(),
254                      scm.into_owned(),
255                  )?;
256                  // Ensure the transition ID matches.
257                  match transition.id() == transition_id {
258                      true => Ok(Some(transition)),
259                      false => bail!("Mismatch in the transition ID '{transition_id}'"),
260                  }
261              }
262              _ => bail!("Transition '{transition_id}' is missing some data (possible corruption)"),
263          }
264      }
265  }
266  
267  /// The transition store.
268  #[derive(Clone)]
269  pub struct TransitionStore<N: Network, T: TransitionStorage<N>> {
270      /// The map of transition program IDs and function names.
271      locator: T::LocatorMap,
272      /// The map of transition inputs.
273      inputs: InputStore<N, T::InputStorage>,
274      /// The map of transition outputs.
275      outputs: OutputStore<N, T::OutputStorage>,
276      /// The map of transition public keys.
277      tpk: T::TPKMap,
278      /// The reverse `tpk` map.
279      reverse_tpk: T::ReverseTPKMap,
280      /// The map of transition commitments.
281      tcm: T::TCMMap,
282      /// The reverse `tcm` map.
283      reverse_tcm: T::ReverseTCMMap,
284      /// The map of signer commitments.
285      scm: T::SCMMap,
286      /// The transition storage.
287      storage: T,
288  }
289  
290  impl<N: Network, T: TransitionStorage<N>> TransitionStore<N, T> {
291      /// Initializes the transition store.
292      pub fn open<S: Into<StorageMode>>(storage: S) -> Result<Self> {
293          // Initialize the transition storage.
294          let storage = T::open(storage)?;
295          // Return the transition store.
296          Ok(Self {
297              locator: storage.locator_map().clone(),
298              inputs: (*storage.input_store()).clone(),
299              outputs: (*storage.output_store()).clone(),
300              tpk: storage.tpk_map().clone(),
301              reverse_tpk: storage.reverse_tpk_map().clone(),
302              tcm: storage.tcm_map().clone(),
303              reverse_tcm: storage.reverse_tcm_map().clone(),
304              scm: storage.scm_map().clone(),
305              storage,
306          })
307      }
308  
309      /// Initializes a transition store from storage.
310      pub fn from(storage: T) -> Self {
311          Self {
312              locator: storage.locator_map().clone(),
313              inputs: (*storage.input_store()).clone(),
314              outputs: (*storage.output_store()).clone(),
315              tpk: storage.tpk_map().clone(),
316              reverse_tpk: storage.reverse_tpk_map().clone(),
317              tcm: storage.tcm_map().clone(),
318              reverse_tcm: storage.reverse_tcm_map().clone(),
319              scm: storage.scm_map().clone(),
320              storage,
321          }
322      }
323  
324      /// Stores the given `transition` into storage.
325      pub fn insert(&self, transition: &Transition<N>) -> Result<()> {
326          self.storage.insert(transition)
327      }
328  
329      /// Removes the input for the given `transition ID`.
330      pub fn remove(&self, transition_id: &N::TransitionID) -> Result<()> {
331          self.storage.remove(transition_id)
332      }
333  
334      /// Starts an atomic batch write operation.
335      pub fn start_atomic(&self) {
336          self.storage.start_atomic();
337      }
338  
339      /// Checks if an atomic batch is in progress.
340      pub fn is_atomic_in_progress(&self) -> bool {
341          self.storage.is_atomic_in_progress()
342      }
343  
344      /// Checkpoints the atomic batch.
345      pub fn atomic_checkpoint(&self) {
346          self.storage.atomic_checkpoint();
347      }
348  
349      /// Clears the latest atomic batch checkpoint.
350      pub fn clear_latest_checkpoint(&self) {
351          self.storage.clear_latest_checkpoint();
352      }
353  
354      /// Rewinds the atomic batch to the previous checkpoint.
355      pub fn atomic_rewind(&self) {
356          self.storage.atomic_rewind();
357      }
358  
359      /// Aborts an atomic batch write operation.
360      pub fn abort_atomic(&self) {
361          self.storage.abort_atomic();
362      }
363  
364      /// Finishes an atomic batch write operation.
365      pub fn finish_atomic(&self) -> Result<()> {
366          self.storage.finish_atomic()
367      }
368  
369      /// Returns the storage mode.
370      pub fn storage_mode(&self) -> &StorageMode {
371          self.storage.storage_mode()
372      }
373  }
374  
375  impl<N: Network, T: TransitionStorage<N>> TransitionStore<N, T> {
376      /// Returns the transition ID that contains the given `input ID` or `output ID`.
377      pub fn find_transition_id(&self, id: &Field<N>) -> Result<N::TransitionID> {
378          // Start by checking the output IDs (which is the more likely case).
379          if let Some(transition_id) = self.outputs.find_transition_id(id)? {
380              return Ok(transition_id);
381          }
382          // Then check the input IDs.
383          if let Some(transition_id) = self.inputs.find_transition_id(id)? {
384              return Ok(transition_id);
385          }
386          // Throw an error.
387          bail!("Failed to find the transition ID for the given input or output ID '{id}'")
388      }
389  }
390  
391  impl<N: Network, T: TransitionStorage<N>> TransitionStore<N, T> {
392      /// Returns the transition for the given `transition ID`.
393      pub fn get_transition(&self, transition_id: &N::TransitionID) -> Result<Option<Transition<N>>> {
394          self.storage.get(transition_id)
395      }
396  
397      /// Returns the program ID for the given `transition ID`.
398      pub fn get_program_id(&self, transition_id: &N::TransitionID) -> Result<Option<ProgramID<N>>> {
399          Ok(self.locator.get_confirmed(transition_id)?.map(|locator| match locator {
400              Cow::Borrowed((program_id, _)) => *program_id,
401              Cow::Owned((program_id, _)) => program_id,
402          }))
403      }
404  
405      /// Returns the function name for the given `transition ID`.
406      pub fn get_function_name(&self, transition_id: &N::TransitionID) -> Result<Option<Identifier<N>>> {
407          Ok(self.locator.get_confirmed(transition_id)?.map(|locator| match locator {
408              Cow::Borrowed((_, function_name)) => *function_name,
409              Cow::Owned((_, function_name)) => function_name,
410          }))
411      }
412  
413      /// Returns the input IDs for the given `transition ID`.
414      pub fn get_input_ids(&self, transition_id: &N::TransitionID) -> Result<Vec<Field<N>>> {
415          self.inputs.get_input_ids(transition_id)
416      }
417  
418      /// Returns the inputs for the given `transition ID`.
419      pub fn get_inputs(&self, transition_id: &N::TransitionID) -> Result<Vec<Input<N>>> {
420          self.inputs.get_inputs(transition_id)
421      }
422  
423      /// Returns the output IDs for the given `transition ID`.
424      pub fn get_output_ids(&self, transition_id: &N::TransitionID) -> Result<Vec<Field<N>>> {
425          self.outputs.get_output_ids(transition_id)
426      }
427  
428      /// Returns the outputs for the given `transition ID`.
429      pub fn get_outputs(&self, transition_id: &N::TransitionID) -> Result<Vec<Output<N>>> {
430          self.outputs.get_outputs(transition_id)
431      }
432  
433      /// Returns the record for the given `commitment`.
434      ///
435      /// If the record exists, `Ok(Some(record))` is returned.
436      /// If the record was purged, `Ok(None)` is returned.
437      /// If the record does not exist, `Err(error)` is returned.
438      pub fn get_record(&self, commitment: &Field<N>) -> Result<Option<Record<N, Ciphertext<N>>>> {
439          self.outputs.get_record(commitment)
440      }
441  }
442  
443  impl<N: Network, T: TransitionStorage<N>> TransitionStore<N, T> {
444      /// Returns `true` if the given transition ID exists.
445      pub fn contains_transition_id(&self, transition_id: &N::TransitionID) -> Result<bool> {
446          self.locator.contains_key_confirmed(transition_id)
447      }
448  
449      /* Input */
450  
451      /// Returns `true` if the given input ID exists.
452      pub fn contains_input_id(&self, input_id: &Field<N>) -> Result<bool> {
453          self.inputs.contains_input_id(input_id)
454      }
455  
456      /// Returns `true` if the given serial number exists.
457      pub fn contains_serial_number(&self, serial_number: &Field<N>) -> Result<bool> {
458          self.inputs.contains_serial_number(serial_number)
459      }
460  
461      /// Returns `true` if the given tag exists.
462      pub fn contains_tag(&self, tag: &Field<N>) -> Result<bool> {
463          self.inputs.contains_tag(tag)
464      }
465  
466      /* Output */
467  
468      /// Returns `true` if the given output ID exists.
469      pub fn contains_output_id(&self, output_id: &Field<N>) -> Result<bool> {
470          self.outputs.contains_output_id(output_id)
471      }
472  
473      /// Returns `true` if the given commitment exists.
474      pub fn contains_commitment(&self, commitment: &Field<N>) -> Result<bool> {
475          self.outputs.contains_commitment(commitment)
476      }
477  
478      /// Returns `true` if the given checksum exists.
479      pub fn contains_checksum(&self, checksum: &Field<N>) -> bool {
480          self.outputs.contains_checksum(checksum)
481      }
482  
483      /// Returns `true` if the given nonce exists.
484      pub fn contains_nonce(&self, nonce: &Group<N>) -> Result<bool> {
485          self.outputs.contains_nonce(nonce)
486      }
487  
488      /* Metadata */
489  
490      /// Returns `true` if the given transition public key exists.
491      pub fn contains_tpk(&self, tpk: &Group<N>) -> Result<bool> {
492          self.reverse_tpk.contains_key_confirmed(tpk)
493      }
494  
495      /// Returns `true` if the given transition commitment exists.
496      pub fn contains_tcm(&self, tcm: &Field<N>) -> Result<bool> {
497          self.reverse_tcm.contains_key_confirmed(tcm)
498      }
499  }
500  
501  impl<N: Network, T: TransitionStorage<N>> TransitionStore<N, T> {
502      /// Returns an iterator over the transition IDs, for all transitions.
503      pub fn transition_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, N::TransitionID>> {
504          self.tcm.keys_confirmed()
505      }
506  
507      /* Input */
508  
509      /// Returns an iterator over the input IDs, for all transition inputs.
510      pub fn input_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
511          self.inputs.input_ids()
512      }
513  
514      /// Returns an iterator over the constant input IDs, for all transition inputs that are constant.
515      pub fn constant_input_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
516          self.inputs.constant_input_ids()
517      }
518  
519      /// Returns an iterator over the public input IDs, for all transition inputs that are public.
520      pub fn public_input_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
521          self.inputs.public_input_ids()
522      }
523  
524      /// Returns an iterator over the private input IDs, for all transition inputs that are private.
525      pub fn private_input_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
526          self.inputs.private_input_ids()
527      }
528  
529      /// Returns an iterator over the serial numbers, for all transition inputs that are records.
530      pub fn serial_numbers(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
531          self.inputs.serial_numbers()
532      }
533  
534      /// Returns an iterator over the external record input IDs, for all transition inputs that are external records.
535      pub fn external_input_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
536          self.inputs.external_input_ids()
537      }
538  
539      /* Output */
540  
541      /// Returns an iterator over the output IDs, for all transition outputs.
542      pub fn output_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
543          self.outputs.output_ids()
544      }
545  
546      /// Returns an iterator over the constant output IDs, for all transition outputs that are constant.
547      pub fn constant_output_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
548          self.outputs.constant_output_ids()
549      }
550  
551      /// Returns an iterator over the public output IDs, for all transition outputs that are public.
552      pub fn public_output_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
553          self.outputs.public_output_ids()
554      }
555  
556      /// Returns an iterator over the private output IDs, for all transition outputs that are private.
557      pub fn private_output_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
558          self.outputs.private_output_ids()
559      }
560  
561      /// Returns an iterator over the commitments, for all transition outputs that are records.
562      pub fn commitments(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
563          self.outputs.commitments()
564      }
565  
566      /// Returns an iterator over the external record output IDs, for all transition outputs that are external records.
567      pub fn external_output_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
568          self.outputs.external_output_ids()
569      }
570  }
571  
572  impl<N: Network, T: TransitionStorage<N>> TransitionStore<N, T> {
573      /* Input */
574  
575      /// Returns an iterator over the constant inputs, for all transitions.
576      pub fn constant_inputs(&self) -> impl '_ + Iterator<Item = Cow<'_, Plaintext<N>>> {
577          self.inputs.constant_inputs()
578      }
579  
580      /// Returns an iterator over the constant inputs, for all transitions.
581      pub fn public_inputs(&self) -> impl '_ + Iterator<Item = Cow<'_, Plaintext<N>>> {
582          self.inputs.public_inputs()
583      }
584  
585      /// Returns an iterator over the private inputs, for all transitions.
586      pub fn private_inputs(&self) -> impl '_ + Iterator<Item = Cow<'_, Ciphertext<N>>> {
587          self.inputs.private_inputs()
588      }
589  
590      /// Returns an iterator over the tags, for all transition inputs that are records.
591      pub fn tags(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
592          self.inputs.tags()
593      }
594  
595      /* Output */
596  
597      /// Returns an iterator over the constant outputs, for all transitions.
598      pub fn constant_outputs(&self) -> impl '_ + Iterator<Item = Cow<'_, Plaintext<N>>> {
599          self.outputs.constant_outputs()
600      }
601  
602      /// Returns an iterator over the constant outputs, for all transitions.
603      pub fn public_outputs(&self) -> impl '_ + Iterator<Item = Cow<'_, Plaintext<N>>> {
604          self.outputs.public_outputs()
605      }
606  
607      /// Returns an iterator over the private outputs, for all transitions.
608      pub fn private_outputs(&self) -> impl '_ + Iterator<Item = Cow<'_, Ciphertext<N>>> {
609          self.outputs.private_outputs()
610      }
611  
612      /// Returns an iterator over the checksums, for all transition outputs that are records.
613      pub fn checksums(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
614          self.outputs.checksums()
615      }
616  
617      /// Returns an iterator over the nonces, for all transition outputs that are records.
618      pub fn nonces(&self) -> impl '_ + Iterator<Item = Cow<'_, Group<N>>> {
619          self.outputs.nonces()
620      }
621  
622      /// Returns an iterator over the `(commitment, record)` pairs, for all transition outputs that are records.
623      #[allow(clippy::type_complexity)]
624      pub fn records(&self) -> impl '_ + Iterator<Item = (Cow<'_, Field<N>>, Cow<'_, Record<N, Ciphertext<N>>>)> {
625          self.outputs.records()
626      }
627  
628      /* Metadata */
629  
630      /// Returns an iterator over the transition public keys, for all transitions.
631      pub fn tpks(&self) -> impl '_ + Iterator<Item = Cow<'_, Group<N>>> {
632          self.tpk.values_confirmed()
633      }
634  
635      /// Returns an iterator over the transition commitments, for all transitions.
636      pub fn tcms(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
637          self.tcm.values_confirmed()
638      }
639  
640      /// Returns an iterator over the signer commitments, for all transitions.
641      pub fn scms(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
642          self.scm.values_confirmed()
643      }
644  }
645  
646  #[cfg(test)]
647  mod tests {
648      use super::*;
649      use crate::helpers::memory::TransitionMemory;
650  
651      #[test]
652      fn test_insert_get_remove() {
653          let rng = &mut TestRng::default();
654  
655          // Sample the transactions.
656          let transaction_0 = alphavm_ledger_test_helpers::sample_execution_transaction_with_fee(true, rng, 0);
657          let transaction_1 = alphavm_ledger_test_helpers::sample_execution_transaction_with_fee(false, rng, 0);
658          let transactions = vec![transaction_0, transaction_1];
659  
660          for transaction in transactions {
661              let transitions = transaction.transitions().cloned().collect::<Vec<_>>();
662  
663              // Ensure there is at least 2 transition.
664              println!("\n\nNumber of transitions: {}\n", transitions.len());
665              assert!(transitions.len() > 1, "\n\nNumber of transitions: {}\n", transitions.len());
666  
667              // Initialize a new transition store.
668              let transition_store = TransitionMemory::open(StorageMode::Test(None)).unwrap();
669  
670              // Test each transition in isolation.
671              for transition in transitions.iter() {
672                  // Retrieve the transition ID.
673                  let transition_id = *transition.id();
674  
675                  // Ensure the transition does not exist.
676                  let candidate = transition_store.get(&transition_id).unwrap();
677                  assert_eq!(None, candidate);
678  
679                  // Insert the transition.
680                  transition_store.insert(transition).unwrap();
681  
682                  // Retrieve the transition.
683                  let candidate = transition_store.get(&transition_id).unwrap();
684                  assert_eq!(Some(transition.clone()), candidate);
685  
686                  // Remove the transition.
687                  transition_store.remove(&transition_id).unwrap();
688  
689                  // Retrieve the transition.
690                  let candidate = transition_store.get(&transition_id).unwrap();
691                  assert_eq!(None, candidate);
692              }
693  
694              // Insert every transition.
695              for transition in transitions.iter() {
696                  // Retrieve the transition ID.
697                  let transition_id = *transition.id();
698  
699                  // Ensure the transition does not exist.
700                  let candidate = transition_store.get(&transition_id).unwrap();
701                  assert_eq!(None, candidate);
702  
703                  // Insert the transition.
704                  transition_store.insert(transition).unwrap();
705  
706                  // Ensure the transition exists.
707                  let candidate = transition_store.get(&transition_id).unwrap();
708                  assert_eq!(Some(transition.clone()), candidate);
709              }
710  
711              // Get every transition (in reverse).
712              for transition in transitions.iter().rev() {
713                  // Retrieve the transition ID.
714                  let transition_id = *transition.id();
715  
716                  // Retrieve the transition.
717                  let candidate = transition_store.get(&transition_id).unwrap();
718                  assert_eq!(Some(transition.clone()), candidate);
719              }
720  
721              // Remove every transition (in reverse).
722              for transition in transitions.iter().rev() {
723                  // Retrieve the transition ID.
724                  let transition_id = *transition.id();
725  
726                  // Remove the transition.
727                  transition_store.remove(&transition_id).unwrap();
728  
729                  // Ensure the transition does not exist.
730                  let candidate = transition_store.get(&transition_id).unwrap();
731                  assert_eq!(None, candidate);
732              }
733          }
734      }
735  }