/ ledger / store / src / transition / input.rs
input.rs
  1  // Copyright (c) 2019-2025 Alpha-Delta Network Inc.
  2  // This file is part of the deltavm 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 deltavm_ledger_block::Input;
 21  use console::{
 22      network::prelude::*,
 23      program::{Ciphertext, Plaintext},
 24      types::Field,
 25  };
 26  
 27  use alphastd_storage::StorageMode;
 28  use anyhow::Result;
 29  use std::borrow::Cow;
 30  
 31  /// A trait for transition input storage.
 32  pub trait InputStorage<N: Network>: Clone + Send + Sync {
 33      /// The mapping of `transition ID` to `input IDs`.
 34      type IDMap: for<'a> Map<'a, N::TransitionID, Vec<Field<N>>>;
 35      /// The mapping of `input 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 `serial number` to `tag`.
 44      type RecordMap: for<'a> Map<'a, Field<N>, Field<N>>;
 45      /// The mapping of `tag` to `serial number`.
 46      type RecordTagMap: for<'a> Map<'a, Field<N>, Field<N>>;
 47      /// The mapping of `external hash` to `()`. Note: This is **not** the record commitment.
 48      type ExternalRecordMap: for<'a> Map<'a, Field<N>, ()>;
 49  
 50      /// Initializes the transition input storage.
 51      fn open<S: Into<StorageMode>>(storage: S) -> Result<Self>;
 52  
 53      /// Returns the ID map.
 54      fn id_map(&self) -> &Self::IDMap;
 55      /// Returns the reverse ID map.
 56      fn reverse_id_map(&self) -> &Self::ReverseIDMap;
 57      /// Returns the constant map.
 58      fn constant_map(&self) -> &Self::ConstantMap;
 59      /// Returns the public map.
 60      fn public_map(&self) -> &Self::PublicMap;
 61      /// Returns the private map.
 62      fn private_map(&self) -> &Self::PrivateMap;
 63      /// Returns the record map.
 64      fn record_map(&self) -> &Self::RecordMap;
 65      /// Returns the record tag map.
 66      fn record_tag_map(&self) -> &Self::RecordTagMap;
 67      /// Returns the external record map.
 68      fn external_record_map(&self) -> &Self::ExternalRecordMap;
 69  
 70      /// Returns the storage mode.
 71      fn storage_mode(&self) -> &StorageMode;
 72  
 73      /// Starts an atomic batch write operation.
 74      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      /// Checks if an atomic batch is in progress.
 86      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      /// Checkpoints the atomic batch.
 98      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      /// Clears the latest atomic batch checkpoint.
110      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      /// Rewinds the atomic batch to the previous checkpoint.
122      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      /// Aborts an atomic batch write operation.
134      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      /// Finishes an atomic batch write operation.
146      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      /// Stores the given `(transition ID, input)` pair into storage.
158      fn insert(&self, transition_id: N::TransitionID, inputs: &[Input<N>]) -> Result<()> {
159          atomic_batch_scope!(self, {
160              // Store the input IDs.
161              self.id_map().insert(transition_id, inputs.iter().map(Input::id).copied().collect())?;
162  
163              // Store the inputs.
164              for input in inputs {
165                  // Store the reverse input ID.
166                  self.reverse_id_map().insert(*input.id(), transition_id)?;
167                  // Store the input.
168                  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                          // Store the record tag.
174                          self.record_tag_map().insert(tag, serial_number)?;
175                          // Store the record.
176                          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      /// Removes the input for the given `transition ID`.
187      fn remove(&self, transition_id: &N::TransitionID) -> Result<()> {
188          // Retrieve the input IDs.
189          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              // Remove the input IDs.
197              self.id_map().remove(transition_id)?;
198  
199              // Remove the inputs.
200              for input_id in input_ids {
201                  // Remove the reverse input ID.
202                  self.reverse_id_map().remove(&input_id)?;
203  
204                  // If the input is a record, remove the record tag.
205                  if let Some(tag) = self.record_map().get_confirmed(&input_id)? {
206                      self.record_tag_map().remove(&tag)?;
207                  }
208  
209                  // Remove the input.
210                  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      /// Returns the transition ID that contains the given `input ID`.
222      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      /// Returns the input IDs for the given `transition ID`.
231      fn get_ids(&self, transition_id: &N::TransitionID) -> Result<Vec<Field<N>>> {
232          // Retrieve the input IDs.
233          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      /// Returns the input for the given `transition ID`.
241      fn get(&self, transition_id: &N::TransitionID) -> Result<Vec<Input<N>>> {
242          // Constructs the input given the input ID and input value.
243          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          // A helper function to construct the input given the input ID.
259          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              // Retrieve the input.
267              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          // Retrieve the input IDs.
281          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  /// The transition input store.
290  #[derive(Clone)]
291  pub struct InputStore<N: Network, I: InputStorage<N>> {
292      /// The map of constant inputs.
293      constant: I::ConstantMap,
294      /// The map of public inputs.
295      public: I::PublicMap,
296      /// The map of private inputs.
297      private: I::PrivateMap,
298      /// The map of record inputs.
299      record: I::RecordMap,
300      /// The map of record tags.
301      record_tag: I::RecordTagMap,
302      /// The map of external record inputs.
303      external_record: I::ExternalRecordMap,
304      /// The input storage.
305      storage: I,
306  }
307  
308  impl<N: Network, I: InputStorage<N>> InputStore<N, I> {
309      /// Initializes the transition input store.
310      pub fn open<S: Into<StorageMode>>(storage: S) -> Result<Self> {
311          // Initialize a new transition input storage.
312          let storage = I::open(storage)?;
313          // Return the transition input store.
314          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      /// Initializes a transition input store from storage.
326      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      /// Stores the given `(transition ID, input)` pair into storage.
339      pub fn insert(&self, transition_id: N::TransitionID, inputs: &[Input<N>]) -> Result<()> {
340          self.storage.insert(transition_id, inputs)
341      }
342  
343      /// Removes the input for the given `transition ID`.
344      pub fn remove(&self, transition_id: &N::TransitionID) -> Result<()> {
345          self.storage.remove(transition_id)
346      }
347  
348      /// Starts an atomic batch write operation.
349      pub fn start_atomic(&self) {
350          self.storage.start_atomic();
351      }
352  
353      /// Checks if an atomic batch is in progress.
354      pub fn is_atomic_in_progress(&self) -> bool {
355          self.storage.is_atomic_in_progress()
356      }
357  
358      /// Checkpoints the atomic batch.
359      pub fn atomic_checkpoint(&self) {
360          self.storage.atomic_checkpoint();
361      }
362  
363      /// Clears the latest atomic batch checkpoint.
364      pub fn clear_latest_checkpoint(&self) {
365          self.storage.clear_latest_checkpoint();
366      }
367  
368      /// Rewinds the atomic batch to the previous checkpoint.
369      pub fn atomic_rewind(&self) {
370          self.storage.atomic_rewind();
371      }
372  
373      /// Aborts an atomic batch write operation.
374      pub fn abort_atomic(&self) {
375          self.storage.abort_atomic();
376      }
377  
378      /// Finishes an atomic batch write operation.
379      pub fn finish_atomic(&self) -> Result<()> {
380          self.storage.finish_atomic()
381      }
382  
383      /// Returns the storage mode.
384      pub fn storage_mode(&self) -> &StorageMode {
385          self.storage.storage_mode()
386      }
387  }
388  
389  impl<N: Network, I: InputStorage<N>> InputStore<N, I> {
390      /// Returns the input IDs for the given `transition ID`.
391      pub fn get_input_ids(&self, transition_id: &N::TransitionID) -> Result<Vec<Field<N>>> {
392          self.storage.get_ids(transition_id)
393      }
394  
395      /// Returns the inputs for the given `transition ID`.
396      pub fn get_inputs(&self, transition_id: &N::TransitionID) -> Result<Vec<Input<N>>> {
397          self.storage.get(transition_id)
398      }
399  }
400  
401  impl<N: Network, I: InputStorage<N>> InputStore<N, I> {
402      /// Returns the transition ID that contains the given `input ID`.
403      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  
408  impl<N: Network, I: InputStorage<N>> InputStore<N, I> {
409      /// Returns `true` if the given input ID exists.
410      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      /// Returns `true` if the given serial number exists.
415      pub fn contains_serial_number(&self, serial_number: &Field<N>) -> Result<bool> {
416          self.record.contains_key_confirmed(serial_number)
417      }
418  
419      /// Returns `true` if the given tag exists.
420      pub fn contains_tag(&self, tag: &Field<N>) -> Result<bool> {
421          self.record_tag.contains_key_confirmed(tag)
422      }
423  }
424  
425  impl<N: Network, I: InputStorage<N>> InputStore<N, I> {
426      /// Returns an iterator over the input IDs, for all transition inputs.
427      pub fn input_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
428          self.storage.reverse_id_map().keys_confirmed()
429      }
430  
431      /// Returns an iterator over the constant input IDs, for all transition inputs that are constant.
432      pub fn constant_input_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
433          self.constant.keys_confirmed()
434      }
435  
436      /// Returns an iterator over the public input IDs, for all transition inputs that are public.
437      pub fn public_input_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
438          self.public.keys_confirmed()
439      }
440  
441      /// Returns an iterator over the private input IDs, for all transition inputs that are private.
442      pub fn private_input_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
443          self.private.keys_confirmed()
444      }
445  
446      /// Returns an iterator over the serial numbers, for all transition inputs that are records.
447      pub fn serial_numbers(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
448          self.record.keys_confirmed()
449      }
450  
451      /// Returns an iterator over the external record input IDs, for all transition inputs that are external records.
452      pub fn external_input_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
453          self.external_record.keys_confirmed()
454      }
455  }
456  
457  impl<N: Network, I: InputStorage<N>> InputStore<N, I> {
458      /// Returns an iterator over the constant inputs, for all transitions.
459      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      /// Returns an iterator over the constant inputs, for all transitions.
468      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      /// Returns an iterator over the private inputs, for all transitions.
477      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      /// Returns an iterator over the tags, for all transition inputs that are records.
486      pub fn tags(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
487          self.record_tag.keys_confirmed()
488      }
489  }
490  
491  #[cfg(test)]
492  mod tests {
493      use super::*;
494      use crate::helpers::memory::InputMemory;
495  
496      #[test]
497      fn test_insert_get_remove() {
498          // Sample the transition inputs.
499          for (transition_id, input) in deltavm_ledger_test_helpers::sample_inputs() {
500              // Initialize a new input store.
501              let input_store = InputMemory::open(StorageMode::Test(None)).unwrap();
502  
503              // Ensure the transition input does not exist.
504              let candidate = input_store.get(&transition_id).unwrap();
505              assert!(candidate.is_empty());
506  
507              // Insert the transition input.
508              input_store.insert(transition_id, &[input.clone()]).unwrap();
509  
510              // Retrieve the transition input.
511              let candidate = input_store.get(&transition_id).unwrap();
512              assert_eq!(vec![input.clone()], candidate);
513  
514              // Remove the transition input.
515              input_store.remove(&transition_id).unwrap();
516  
517              // Retrieve the transition input.
518              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          // Sample the transition inputs.
526          for (transition_id, input) in deltavm_ledger_test_helpers::sample_inputs() {
527              // Initialize a new input store.
528              let input_store = InputMemory::open(StorageMode::Test(None)).unwrap();
529  
530              // Ensure the transition input does not exist.
531              let candidate = input_store.get(&transition_id).unwrap();
532              assert!(candidate.is_empty());
533  
534              // Ensure the transition ID is not found.
535              let candidate = input_store.find_transition_id(input.id()).unwrap();
536              assert!(candidate.is_none());
537  
538              // Insert the transition input.
539              input_store.insert(transition_id, &[input.clone()]).unwrap();
540  
541              // Find the transition ID.
542              let candidate = input_store.find_transition_id(input.id()).unwrap();
543              assert_eq!(Some(transition_id), candidate);
544  
545              // Remove the transition input.
546              input_store.remove(&transition_id).unwrap();
547  
548              // Ensure the transition ID is not found.
549              let candidate = input_store.find_transition_id(input.id()).unwrap();
550              assert!(candidate.is_none());
551          }
552      }
553  }