use crate::{
atomic_write_batch,
ledger::{
map::{memory_map::MemoryMap, Map, MapRead},
transition::{Input, Origin},
},
};
use console::{
network::prelude::*,
program::{Ciphertext, Plaintext},
types::Field,
};
use anyhow::Result;
use std::borrow::Cow;
pub trait InputStorage<N: Network>: Clone + 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>, Origin<N>)>;
type RecordTagMap: for<'a> Map<'a, Field<N>, Field<N>>;
type ExternalRecordMap: for<'a> Map<'a, Field<N>, ()>;
fn open() -> 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 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 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_write_batch!(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, origin) => {
self.record_tag_map().insert(tag, serial_number)?;
self.record_map().insert(serial_number, (tag, origin))?
}
Input::ExternalRecord(input_id) => self.external_record_map().insert(input_id, ())?,
}
}
Ok(())
});
Ok(())
}
fn remove(&self, transition_id: &N::TransitionID) -> Result<()> {
let input_ids: Vec<_> = match self.id_map().get(transition_id)? {
Some(Cow::Borrowed(ids)) => ids.to_vec(),
Some(Cow::Owned(ids)) => ids.into_iter().collect(),
None => return Ok(()),
};
atomic_write_batch!(self, {
self.id_map().remove(transition_id)?;
for input_id in input_ids {
self.reverse_id_map().remove(&input_id)?;
if let Some(record) = self.record_map().get(&input_id)? {
self.record_tag_map().remove(&record.0)?;
}
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(())
});
Ok(())
}
fn find_transition_id(&self, input_id: &Field<N>) -> Result<Option<N::TransitionID>> {
match self.reverse_id_map().get(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(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, origin)) => Input::Record($input_id, *tag, *origin),
Cow::Owned((tag, origin)) => Input::Record($input_id, tag, origin),
}
};
(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(&input_id)?;
let public = self.public_map().get(&input_id)?;
let private = self.private_map().get(&input_id)?;
let record = self.record_map().get(&input_id)?;
let external_record = self.external_record_map().get(&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(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 InputMemory<N: Network> {
id_map: MemoryMap<N::TransitionID, Vec<Field<N>>>,
reverse_id_map: MemoryMap<Field<N>, N::TransitionID>,
constant: MemoryMap<Field<N>, Option<Plaintext<N>>>,
public: MemoryMap<Field<N>, Option<Plaintext<N>>>,
private: MemoryMap<Field<N>, Option<Ciphertext<N>>>,
record: MemoryMap<Field<N>, (Field<N>, Origin<N>)>,
record_tag: MemoryMap<Field<N>, Field<N>>,
external_record: MemoryMap<Field<N>, ()>,
}
#[rustfmt::skip]
impl<N: Network> InputStorage<N> for InputMemory<N> {
type IDMap = MemoryMap<N::TransitionID, Vec<Field<N>>>;
type ReverseIDMap = MemoryMap<Field<N>, N::TransitionID>;
type ConstantMap = MemoryMap<Field<N>, Option<Plaintext<N>>>;
type PublicMap = MemoryMap<Field<N>, Option<Plaintext<N>>>;
type PrivateMap = MemoryMap<Field<N>, Option<Ciphertext<N>>>;
type RecordMap = MemoryMap<Field<N>, (Field<N>, Origin<N>)>;
type RecordTagMap = MemoryMap<Field<N>, Field<N>>;
type ExternalRecordMap = MemoryMap<Field<N>, ()>;
fn open() -> Result<Self> {
Ok(Self {
id_map: MemoryMap::default(),
reverse_id_map: MemoryMap::default(),
constant: MemoryMap::default(),
public: MemoryMap::default(),
private: MemoryMap::default(),
record: MemoryMap::default(),
record_tag: MemoryMap::default(),
external_record: MemoryMap::default(),
})
}
fn id_map(&self) -> &Self::IDMap {
&self.id_map
}
fn reverse_id_map(&self) -> &Self::ReverseIDMap {
&self.reverse_id_map
}
fn constant_map(&self) -> &Self::ConstantMap {
&self.constant
}
fn public_map(&self) -> &Self::PublicMap {
&self.public
}
fn private_map(&self) -> &Self::PrivateMap {
&self.private
}
fn record_map(&self) -> &Self::RecordMap {
&self.record
}
fn record_tag_map(&self) -> &Self::RecordTagMap {
&self.record_tag
}
fn external_record_map(&self) -> &Self::ExternalRecordMap {
&self.external_record
}
}
#[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() -> Result<Self> {
let storage = I::open()?;
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 abort_atomic(&self) {
self.storage.abort_atomic();
}
pub fn finish_atomic(&self) -> Result<()> {
self.storage.finish_atomic()
}
}
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(input_id)
}
pub fn contains_serial_number(&self, serial_number: &Field<N>) -> Result<bool> {
self.record.contains_key(serial_number)
}
pub fn contains_tag(&self, tag: &Field<N>) -> Result<bool> {
self.record_tag.contains_key(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()
}
pub fn constant_input_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
self.constant.keys()
}
pub fn public_input_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
self.public.keys()
}
pub fn private_input_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
self.private.keys()
}
pub fn serial_numbers(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
self.record.keys()
}
pub fn external_input_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
self.external_record.keys()
}
}
impl<N: Network, I: InputStorage<N>> InputStore<N, I> {
pub fn constant_inputs(&self) -> impl '_ + Iterator<Item = Cow<'_, Plaintext<N>>> {
self.constant.values().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().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().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()
}
pub fn origins(&self) -> impl '_ + Iterator<Item = Cow<'_, Origin<N>>> {
self.record.values().map(|input| match input {
Cow::Borrowed((_, origin)) => Cow::Borrowed(origin),
Cow::Owned((_, origin)) => Cow::Owned(origin),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_insert_get_remove() {
for (transition_id, input) in crate::ledger::transition::input::test_helpers::sample_inputs() {
let input_store = InputMemory::open().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 crate::ledger::transition::input::test_helpers::sample_inputs() {
let input_store = InputMemory::open().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());
}
}
}