use crate::{
atomic_batch_scope,
helpers::{Map, MapRead},
};
use console::{
network::prelude::*,
program::{Ciphertext, Plaintext},
types::Field,
};
use ledger_block::Input;
use aleo_std_storage::StorageMode;
use anyhow::Result;
use std::borrow::Cow;
pub trait InputStorage<N: Network>: Clone + Send + Sync {
type IDMap: for<'a> Map<'a, N::TransitionID, Vec<Field<N>>>;
type ReverseIDMap: for<'a> Map<'a, Field<N>, N::TransitionID>;
type ConstantMap: for<'a> Map<'a, Field<N>, Option<Plaintext<N>>>;
type PublicMap: for<'a> Map<'a, Field<N>, Option<Plaintext<N>>>;
type PrivateMap: for<'a> Map<'a, Field<N>, Option<Ciphertext<N>>>;
type RecordMap: for<'a> Map<'a, Field<N>, Field<N>>;
type RecordTagMap: for<'a> Map<'a, Field<N>, Field<N>>;
type ExternalRecordMap: for<'a> Map<'a, Field<N>, ()>;
fn open<S: Clone + Into<StorageMode>>(storage: S) -> Result<Self>;
fn id_map(&self) -> &Self::IDMap;
fn reverse_id_map(&self) -> &Self::ReverseIDMap;
fn constant_map(&self) -> &Self::ConstantMap;
fn public_map(&self) -> &Self::PublicMap;
fn private_map(&self) -> &Self::PrivateMap;
fn record_map(&self) -> &Self::RecordMap;
fn record_tag_map(&self) -> &Self::RecordTagMap;
fn external_record_map(&self) -> &Self::ExternalRecordMap;
fn storage_mode(&self) -> &StorageMode;
fn start_atomic(&self) {
self.id_map().start_atomic();
self.reverse_id_map().start_atomic();
self.constant_map().start_atomic();
self.public_map().start_atomic();
self.private_map().start_atomic();
self.record_map().start_atomic();
self.record_tag_map().start_atomic();
self.external_record_map().start_atomic();
}
fn is_atomic_in_progress(&self) -> bool {
self.id_map().is_atomic_in_progress()
|| self.reverse_id_map().is_atomic_in_progress()
|| self.constant_map().is_atomic_in_progress()
|| self.public_map().is_atomic_in_progress()
|| self.private_map().is_atomic_in_progress()
|| self.record_map().is_atomic_in_progress()
|| self.record_tag_map().is_atomic_in_progress()
|| self.external_record_map().is_atomic_in_progress()
}
fn atomic_checkpoint(&self) {
self.id_map().atomic_checkpoint();
self.reverse_id_map().atomic_checkpoint();
self.constant_map().atomic_checkpoint();
self.public_map().atomic_checkpoint();
self.private_map().atomic_checkpoint();
self.record_map().atomic_checkpoint();
self.record_tag_map().atomic_checkpoint();
self.external_record_map().atomic_checkpoint();
}
fn clear_latest_checkpoint(&self) {
self.id_map().clear_latest_checkpoint();
self.reverse_id_map().clear_latest_checkpoint();
self.constant_map().clear_latest_checkpoint();
self.public_map().clear_latest_checkpoint();
self.private_map().clear_latest_checkpoint();
self.record_map().clear_latest_checkpoint();
self.record_tag_map().clear_latest_checkpoint();
self.external_record_map().clear_latest_checkpoint();
}
fn atomic_rewind(&self) {
self.id_map().atomic_rewind();
self.reverse_id_map().atomic_rewind();
self.constant_map().atomic_rewind();
self.public_map().atomic_rewind();
self.private_map().atomic_rewind();
self.record_map().atomic_rewind();
self.record_tag_map().atomic_rewind();
self.external_record_map().atomic_rewind();
}
fn abort_atomic(&self) {
self.id_map().abort_atomic();
self.reverse_id_map().abort_atomic();
self.constant_map().abort_atomic();
self.public_map().abort_atomic();
self.private_map().abort_atomic();
self.record_map().abort_atomic();
self.record_tag_map().abort_atomic();
self.external_record_map().abort_atomic();
}
fn finish_atomic(&self) -> Result<()> {
self.id_map().finish_atomic()?;
self.reverse_id_map().finish_atomic()?;
self.constant_map().finish_atomic()?;
self.public_map().finish_atomic()?;
self.private_map().finish_atomic()?;
self.record_map().finish_atomic()?;
self.record_tag_map().finish_atomic()?;
self.external_record_map().finish_atomic()
}
fn insert(&self, transition_id: N::TransitionID, inputs: &[Input<N>]) -> Result<()> {
atomic_batch_scope!(self, {
self.id_map().insert(transition_id, inputs.iter().map(Input::id).copied().collect())?;
for input in inputs {
self.reverse_id_map().insert(*input.id(), transition_id)?;
match input.clone() {
Input::Constant(input_id, constant) => self.constant_map().insert(input_id, constant)?,
Input::Public(input_id, public) => self.public_map().insert(input_id, public)?,
Input::Private(input_id, private) => self.private_map().insert(input_id, private)?,
Input::Record(serial_number, tag) => {
self.record_tag_map().insert(tag, serial_number)?;
self.record_map().insert(serial_number, tag)?
}
Input::ExternalRecord(input_id) => self.external_record_map().insert(input_id, ())?,
}
}
Ok(())
})
}
fn remove(&self, transition_id: &N::TransitionID) -> Result<()> {
let input_ids: Vec<_> = match self.id_map().get_confirmed(transition_id)? {
Some(Cow::Borrowed(ids)) => ids.to_vec(),
Some(Cow::Owned(ids)) => ids.into_iter().collect(),
None => return Ok(()),
};
atomic_batch_scope!(self, {
self.id_map().remove(transition_id)?;
for input_id in input_ids {
self.reverse_id_map().remove(&input_id)?;
if let Some(tag) = self.record_map().get_confirmed(&input_id)? {
self.record_tag_map().remove(&tag)?;
}
self.constant_map().remove(&input_id)?;
self.public_map().remove(&input_id)?;
self.private_map().remove(&input_id)?;
self.record_map().remove(&input_id)?;
self.external_record_map().remove(&input_id)?;
}
Ok(())
})
}
fn find_transition_id(&self, input_id: &Field<N>) -> Result<Option<N::TransitionID>> {
match self.reverse_id_map().get_confirmed(input_id)? {
Some(Cow::Borrowed(transition_id)) => Ok(Some(*transition_id)),
Some(Cow::Owned(transition_id)) => Ok(Some(transition_id)),
None => Ok(None),
}
}
fn get_ids(&self, transition_id: &N::TransitionID) -> Result<Vec<Field<N>>> {
match self.id_map().get_confirmed(transition_id)? {
Some(Cow::Borrowed(inputs)) => Ok(inputs.to_vec()),
Some(Cow::Owned(inputs)) => Ok(inputs),
None => Ok(vec![]),
}
}
fn get(&self, transition_id: &N::TransitionID) -> Result<Vec<Input<N>>> {
macro_rules! into_input {
(Input::Record($input_id:ident, $input:expr)) => {
match $input {
Cow::Borrowed(tag) => Input::Record($input_id, *tag),
Cow::Owned(tag) => Input::Record($input_id, tag),
}
};
(Input::$Variant:ident($input_id:ident, $input:expr)) => {
match $input {
Cow::Borrowed(input) => Input::$Variant($input_id, input.clone()),
Cow::Owned(input) => Input::$Variant($input_id, input),
}
};
}
let construct_input = |input_id| {
let constant = self.constant_map().get_confirmed(&input_id)?;
let public = self.public_map().get_confirmed(&input_id)?;
let private = self.private_map().get_confirmed(&input_id)?;
let record = self.record_map().get_confirmed(&input_id)?;
let external_record = self.external_record_map().get_confirmed(&input_id)?;
let input = match (constant, public, private, record, external_record) {
(Some(constant), None, None, None, None) => into_input!(Input::Constant(input_id, constant)),
(None, Some(public), None, None, None) => into_input!(Input::Public(input_id, public)),
(None, None, Some(private), None, None) => into_input!(Input::Private(input_id, private)),
(None, None, None, Some(record), None) => into_input!(Input::Record(input_id, record)),
(None, None, None, None, Some(_)) => Input::ExternalRecord(input_id),
(None, None, None, None, None) => bail!("Missing input '{input_id}' in transition '{transition_id}'"),
_ => bail!("Found multiple inputs for the input ID '{input_id}' in transition '{transition_id}'"),
};
Ok(input)
};
match self.id_map().get_confirmed(transition_id)? {
Some(Cow::Borrowed(ids)) => ids.iter().map(|input_id| construct_input(*input_id)).collect(),
Some(Cow::Owned(ids)) => ids.iter().map(|input_id| construct_input(*input_id)).collect(),
None => Ok(vec![]),
}
}
}
#[derive(Clone)]
pub struct InputStore<N: Network, I: InputStorage<N>> {
constant: I::ConstantMap,
public: I::PublicMap,
private: I::PrivateMap,
record: I::RecordMap,
record_tag: I::RecordTagMap,
external_record: I::ExternalRecordMap,
storage: I,
}
impl<N: Network, I: InputStorage<N>> InputStore<N, I> {
pub fn open<S: Clone + Into<StorageMode>>(storage: S) -> Result<Self> {
let storage = I::open(storage)?;
Ok(Self {
constant: storage.constant_map().clone(),
public: storage.public_map().clone(),
private: storage.private_map().clone(),
record: storage.record_map().clone(),
record_tag: storage.record_tag_map().clone(),
external_record: storage.external_record_map().clone(),
storage,
})
}
pub fn from(storage: I) -> Self {
Self {
constant: storage.constant_map().clone(),
public: storage.public_map().clone(),
private: storage.private_map().clone(),
record: storage.record_map().clone(),
record_tag: storage.record_tag_map().clone(),
external_record: storage.external_record_map().clone(),
storage,
}
}
pub fn insert(&self, transition_id: N::TransitionID, inputs: &[Input<N>]) -> Result<()> {
self.storage.insert(transition_id, inputs)
}
pub fn remove(&self, transition_id: &N::TransitionID) -> Result<()> {
self.storage.remove(transition_id)
}
pub fn start_atomic(&self) {
self.storage.start_atomic();
}
pub fn is_atomic_in_progress(&self) -> bool {
self.storage.is_atomic_in_progress()
}
pub fn atomic_checkpoint(&self) {
self.storage.atomic_checkpoint();
}
pub fn clear_latest_checkpoint(&self) {
self.storage.clear_latest_checkpoint();
}
pub fn atomic_rewind(&self) {
self.storage.atomic_rewind();
}
pub fn abort_atomic(&self) {
self.storage.abort_atomic();
}
pub fn finish_atomic(&self) -> Result<()> {
self.storage.finish_atomic()
}
pub fn storage_mode(&self) -> &StorageMode {
self.storage.storage_mode()
}
}
impl<N: Network, I: InputStorage<N>> InputStore<N, I> {
pub fn get_input_ids(&self, transition_id: &N::TransitionID) -> Result<Vec<Field<N>>> {
self.storage.get_ids(transition_id)
}
pub fn get_inputs(&self, transition_id: &N::TransitionID) -> Result<Vec<Input<N>>> {
self.storage.get(transition_id)
}
}
impl<N: Network, I: InputStorage<N>> InputStore<N, I> {
pub fn find_transition_id(&self, input_id: &Field<N>) -> Result<Option<N::TransitionID>> {
self.storage.find_transition_id(input_id)
}
}
impl<N: Network, I: InputStorage<N>> InputStore<N, I> {
pub fn contains_input_id(&self, input_id: &Field<N>) -> Result<bool> {
self.storage.reverse_id_map().contains_key_confirmed(input_id)
}
pub fn contains_serial_number(&self, serial_number: &Field<N>) -> Result<bool> {
self.record.contains_key_confirmed(serial_number)
}
pub fn contains_tag(&self, tag: &Field<N>) -> Result<bool> {
self.record_tag.contains_key_confirmed(tag)
}
}
impl<N: Network, I: InputStorage<N>> InputStore<N, I> {
pub fn input_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
self.storage.reverse_id_map().keys_confirmed()
}
pub fn constant_input_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
self.constant.keys_confirmed()
}
pub fn public_input_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
self.public.keys_confirmed()
}
pub fn private_input_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
self.private.keys_confirmed()
}
pub fn serial_numbers(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
self.record.keys_confirmed()
}
pub fn external_input_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
self.external_record.keys_confirmed()
}
}
impl<N: Network, I: InputStorage<N>> InputStore<N, I> {
pub fn constant_inputs(&self) -> impl '_ + Iterator<Item = Cow<'_, Plaintext<N>>> {
self.constant.values_confirmed().flat_map(|input| match input {
Cow::Borrowed(Some(input)) => Some(Cow::Borrowed(input)),
Cow::Owned(Some(input)) => Some(Cow::Owned(input)),
_ => None,
})
}
pub fn public_inputs(&self) -> impl '_ + Iterator<Item = Cow<'_, Plaintext<N>>> {
self.public.values_confirmed().flat_map(|input| match input {
Cow::Borrowed(Some(input)) => Some(Cow::Borrowed(input)),
Cow::Owned(Some(input)) => Some(Cow::Owned(input)),
_ => None,
})
}
pub fn private_inputs(&self) -> impl '_ + Iterator<Item = Cow<'_, Ciphertext<N>>> {
self.private.values_confirmed().flat_map(|input| match input {
Cow::Borrowed(Some(input)) => Some(Cow::Borrowed(input)),
Cow::Owned(Some(input)) => Some(Cow::Owned(input)),
_ => None,
})
}
pub fn tags(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
self.record_tag.keys_confirmed()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::helpers::memory::InputMemory;
#[test]
fn test_insert_get_remove() {
for (transition_id, input) in ledger_test_helpers::sample_inputs() {
let input_store = InputMemory::open(None).unwrap();
let candidate = input_store.get(&transition_id).unwrap();
assert!(candidate.is_empty());
input_store.insert(transition_id, &[input.clone()]).unwrap();
let candidate = input_store.get(&transition_id).unwrap();
assert_eq!(vec![input.clone()], candidate);
input_store.remove(&transition_id).unwrap();
let candidate = input_store.get(&transition_id).unwrap();
assert!(candidate.is_empty());
}
}
#[test]
fn test_find_transition_id() {
for (transition_id, input) in ledger_test_helpers::sample_inputs() {
let input_store = InputMemory::open(None).unwrap();
let candidate = input_store.get(&transition_id).unwrap();
assert!(candidate.is_empty());
let candidate = input_store.find_transition_id(input.id()).unwrap();
assert!(candidate.is_none());
input_store.insert(transition_id, &[input.clone()]).unwrap();
let candidate = input_store.find_transition_id(input.id()).unwrap();
assert_eq!(Some(transition_id), candidate);
input_store.remove(&transition_id).unwrap();
let candidate = input_store.find_transition_id(input.id()).unwrap();
assert!(candidate.is_none());
}
}
}