mod input;
pub use input::*;
mod output;
pub use output::*;
use crate::{
atomic_batch_scope,
cow_to_cloned,
cow_to_copied,
helpers::{Map, MapRead},
};
use console::{
network::prelude::*,
program::{Ciphertext, Identifier, Plaintext, ProgramID, Record},
types::{Field, Group},
};
use ledger_block::{Input, Output, Transition};
use aleo_std_storage::StorageMode;
use anyhow::Result;
use std::borrow::Cow;
pub trait TransitionStorage<N: Network>: Clone + Send + Sync {
type LocatorMap: for<'a> Map<'a, N::TransitionID, (ProgramID<N>, Identifier<N>)>;
type InputStorage: InputStorage<N>;
type OutputStorage: OutputStorage<N>;
type TPKMap: for<'a> Map<'a, N::TransitionID, Group<N>>;
type ReverseTPKMap: for<'a> Map<'a, Group<N>, N::TransitionID>;
type TCMMap: for<'a> Map<'a, N::TransitionID, Field<N>>;
type ReverseTCMMap: for<'a> Map<'a, Field<N>, N::TransitionID>;
type SCMMap: for<'a> Map<'a, N::TransitionID, Field<N>>;
fn open<S: Clone + Into<StorageMode>>(storage: S) -> Result<Self>;
fn locator_map(&self) -> &Self::LocatorMap;
fn input_store(&self) -> &InputStore<N, Self::InputStorage>;
fn output_store(&self) -> &OutputStore<N, Self::OutputStorage>;
fn tpk_map(&self) -> &Self::TPKMap;
fn reverse_tpk_map(&self) -> &Self::ReverseTPKMap;
fn tcm_map(&self) -> &Self::TCMMap;
fn reverse_tcm_map(&self) -> &Self::ReverseTCMMap;
fn scm_map(&self) -> &Self::SCMMap;
fn storage_mode(&self) -> &StorageMode {
debug_assert!(self.input_store().storage_mode() == self.output_store().storage_mode());
self.input_store().storage_mode()
}
fn start_atomic(&self) {
self.locator_map().start_atomic();
self.input_store().start_atomic();
self.output_store().start_atomic();
self.tpk_map().start_atomic();
self.reverse_tpk_map().start_atomic();
self.tcm_map().start_atomic();
self.reverse_tcm_map().start_atomic();
self.scm_map().start_atomic();
}
fn is_atomic_in_progress(&self) -> bool {
self.locator_map().is_atomic_in_progress()
|| self.input_store().is_atomic_in_progress()
|| self.output_store().is_atomic_in_progress()
|| self.tpk_map().is_atomic_in_progress()
|| self.reverse_tpk_map().is_atomic_in_progress()
|| self.tcm_map().is_atomic_in_progress()
|| self.reverse_tcm_map().is_atomic_in_progress()
|| self.scm_map().is_atomic_in_progress()
}
fn atomic_checkpoint(&self) {
self.locator_map().atomic_checkpoint();
self.input_store().atomic_checkpoint();
self.output_store().atomic_checkpoint();
self.tpk_map().atomic_checkpoint();
self.reverse_tpk_map().atomic_checkpoint();
self.tcm_map().atomic_checkpoint();
self.reverse_tcm_map().atomic_checkpoint();
self.scm_map().atomic_checkpoint();
}
fn clear_latest_checkpoint(&self) {
self.locator_map().clear_latest_checkpoint();
self.input_store().clear_latest_checkpoint();
self.output_store().clear_latest_checkpoint();
self.tpk_map().clear_latest_checkpoint();
self.reverse_tpk_map().clear_latest_checkpoint();
self.tcm_map().clear_latest_checkpoint();
self.reverse_tcm_map().clear_latest_checkpoint();
self.scm_map().clear_latest_checkpoint();
}
fn atomic_rewind(&self) {
self.locator_map().atomic_rewind();
self.input_store().atomic_rewind();
self.output_store().atomic_rewind();
self.tpk_map().atomic_rewind();
self.reverse_tpk_map().atomic_rewind();
self.tcm_map().atomic_rewind();
self.reverse_tcm_map().atomic_rewind();
self.scm_map().atomic_rewind();
}
fn abort_atomic(&self) {
self.locator_map().abort_atomic();
self.input_store().abort_atomic();
self.output_store().abort_atomic();
self.tpk_map().abort_atomic();
self.reverse_tpk_map().abort_atomic();
self.tcm_map().abort_atomic();
self.reverse_tcm_map().abort_atomic();
self.scm_map().abort_atomic();
}
fn finish_atomic(&self) -> Result<()> {
self.locator_map().finish_atomic()?;
self.input_store().finish_atomic()?;
self.output_store().finish_atomic()?;
self.tpk_map().finish_atomic()?;
self.reverse_tpk_map().finish_atomic()?;
self.tcm_map().finish_atomic()?;
self.reverse_tcm_map().finish_atomic()?;
self.scm_map().finish_atomic()
}
fn insert(&self, transition: &Transition<N>) -> Result<()> {
atomic_batch_scope!(self, {
let transition_id = *transition.id();
self.locator_map().insert(transition_id, (*transition.program_id(), *transition.function_name()))?;
self.input_store().insert(transition_id, transition.inputs())?;
self.output_store().insert(transition_id, transition.outputs())?;
self.tpk_map().insert(transition_id, *transition.tpk())?;
self.reverse_tpk_map().insert(*transition.tpk(), transition_id)?;
self.tcm_map().insert(transition_id, *transition.tcm())?;
self.reverse_tcm_map().insert(*transition.tcm(), transition_id)?;
self.scm_map().insert(transition_id, *transition.scm())?;
Ok(())
})
}
fn remove(&self, transition_id: &N::TransitionID) -> Result<()> {
let tpk = match self.tpk_map().get_confirmed(transition_id)? {
Some(tpk) => cow_to_copied!(tpk),
None => return Ok(()),
};
let tcm = match self.tcm_map().get_confirmed(transition_id)? {
Some(tcm) => cow_to_copied!(tcm),
None => return Ok(()),
};
atomic_batch_scope!(self, {
self.locator_map().remove(transition_id)?;
self.input_store().remove(transition_id)?;
self.output_store().remove(transition_id)?;
self.tpk_map().remove(transition_id)?;
self.reverse_tpk_map().remove(&tpk)?;
self.tcm_map().remove(transition_id)?;
self.reverse_tcm_map().remove(&tcm)?;
self.scm_map().remove(transition_id)?;
Ok(())
})
}
fn get(&self, transition_id: &N::TransitionID) -> Result<Option<Transition<N>>> {
let (program_id, function_name) = match self.locator_map().get_confirmed(transition_id)? {
Some(locator) => cow_to_cloned!(locator),
None => return Ok(None),
};
let inputs = self.input_store().get_inputs(transition_id)?;
let outputs = self.output_store().get_outputs(transition_id)?;
let tpk = self.tpk_map().get_confirmed(transition_id)?;
let tcm = self.tcm_map().get_confirmed(transition_id)?;
let scm = self.scm_map().get_confirmed(transition_id)?;
match (tpk, tcm, scm) {
(Some(tpk), Some(tcm), Some(scm)) => {
let transition = Transition::new(
program_id,
function_name,
inputs,
outputs,
cow_to_cloned!(tpk),
cow_to_cloned!(tcm),
cow_to_cloned!(scm),
)?;
match transition.id() == transition_id {
true => Ok(Some(transition)),
false => bail!("Mismatch in the transition ID '{transition_id}'"),
}
}
_ => bail!("Transition '{transition_id}' is missing some data (possible corruption)"),
}
}
}
#[derive(Clone)]
pub struct TransitionStore<N: Network, T: TransitionStorage<N>> {
locator: T::LocatorMap,
inputs: InputStore<N, T::InputStorage>,
outputs: OutputStore<N, T::OutputStorage>,
tpk: T::TPKMap,
reverse_tpk: T::ReverseTPKMap,
tcm: T::TCMMap,
reverse_tcm: T::ReverseTCMMap,
scm: T::SCMMap,
storage: T,
}
impl<N: Network, T: TransitionStorage<N>> TransitionStore<N, T> {
pub fn open<S: Clone + Into<StorageMode>>(storage: S) -> Result<Self> {
let storage = T::open(storage)?;
Ok(Self {
locator: storage.locator_map().clone(),
inputs: (*storage.input_store()).clone(),
outputs: (*storage.output_store()).clone(),
tpk: storage.tpk_map().clone(),
reverse_tpk: storage.reverse_tpk_map().clone(),
tcm: storage.tcm_map().clone(),
reverse_tcm: storage.reverse_tcm_map().clone(),
scm: storage.scm_map().clone(),
storage,
})
}
pub fn from(storage: T) -> Self {
Self {
locator: storage.locator_map().clone(),
inputs: (*storage.input_store()).clone(),
outputs: (*storage.output_store()).clone(),
tpk: storage.tpk_map().clone(),
reverse_tpk: storage.reverse_tpk_map().clone(),
tcm: storage.tcm_map().clone(),
reverse_tcm: storage.reverse_tcm_map().clone(),
scm: storage.scm_map().clone(),
storage,
}
}
pub fn insert(&self, transition: &Transition<N>) -> Result<()> {
self.storage.insert(transition)
}
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, T: TransitionStorage<N>> TransitionStore<N, T> {
pub fn find_transition_id(&self, id: &Field<N>) -> Result<N::TransitionID> {
if let Some(transition_id) = self.outputs.find_transition_id(id)? {
return Ok(transition_id);
}
if let Some(transition_id) = self.inputs.find_transition_id(id)? {
return Ok(transition_id);
}
bail!("Failed to find the transition ID for the given input or output ID '{id}'")
}
}
impl<N: Network, T: TransitionStorage<N>> TransitionStore<N, T> {
pub fn get_transition(&self, transition_id: &N::TransitionID) -> Result<Option<Transition<N>>> {
self.storage.get(transition_id)
}
pub fn get_program_id(&self, transition_id: &N::TransitionID) -> Result<Option<ProgramID<N>>> {
Ok(self.locator.get_confirmed(transition_id)?.map(|locator| match locator {
Cow::Borrowed((program_id, _)) => *program_id,
Cow::Owned((program_id, _)) => program_id,
}))
}
pub fn get_function_name(&self, transition_id: &N::TransitionID) -> Result<Option<Identifier<N>>> {
Ok(self.locator.get_confirmed(transition_id)?.map(|locator| match locator {
Cow::Borrowed((_, function_name)) => *function_name,
Cow::Owned((_, function_name)) => function_name,
}))
}
pub fn get_input_ids(&self, transition_id: &N::TransitionID) -> Result<Vec<Field<N>>> {
self.inputs.get_input_ids(transition_id)
}
pub fn get_inputs(&self, transition_id: &N::TransitionID) -> Result<Vec<Input<N>>> {
self.inputs.get_inputs(transition_id)
}
pub fn get_output_ids(&self, transition_id: &N::TransitionID) -> Result<Vec<Field<N>>> {
self.outputs.get_output_ids(transition_id)
}
pub fn get_outputs(&self, transition_id: &N::TransitionID) -> Result<Vec<Output<N>>> {
self.outputs.get_outputs(transition_id)
}
pub fn get_record(&self, commitment: &Field<N>) -> Result<Option<Record<N, Ciphertext<N>>>> {
self.outputs.get_record(commitment)
}
}
impl<N: Network, T: TransitionStorage<N>> TransitionStore<N, T> {
pub fn contains_transition_id(&self, transition_id: &N::TransitionID) -> Result<bool> {
self.locator.contains_key_confirmed(transition_id)
}
pub fn contains_input_id(&self, input_id: &Field<N>) -> Result<bool> {
self.inputs.contains_input_id(input_id)
}
pub fn contains_serial_number(&self, serial_number: &Field<N>) -> Result<bool> {
self.inputs.contains_serial_number(serial_number)
}
pub fn contains_tag(&self, tag: &Field<N>) -> Result<bool> {
self.inputs.contains_tag(tag)
}
pub fn contains_output_id(&self, output_id: &Field<N>) -> Result<bool> {
self.outputs.contains_output_id(output_id)
}
pub fn contains_commitment(&self, commitment: &Field<N>) -> Result<bool> {
self.outputs.contains_commitment(commitment)
}
pub fn contains_checksum(&self, checksum: &Field<N>) -> bool {
self.outputs.contains_checksum(checksum)
}
pub fn contains_nonce(&self, nonce: &Group<N>) -> Result<bool> {
self.outputs.contains_nonce(nonce)
}
pub fn contains_tpk(&self, tpk: &Group<N>) -> Result<bool> {
self.reverse_tpk.contains_key_confirmed(tpk)
}
pub fn contains_tcm(&self, tcm: &Field<N>) -> Result<bool> {
self.reverse_tcm.contains_key_confirmed(tcm)
}
}
impl<N: Network, T: TransitionStorage<N>> TransitionStore<N, T> {
pub fn transition_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, N::TransitionID>> {
self.tcm.keys_confirmed()
}
pub fn input_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
self.inputs.input_ids()
}
pub fn constant_input_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
self.inputs.constant_input_ids()
}
pub fn public_input_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
self.inputs.public_input_ids()
}
pub fn private_input_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
self.inputs.private_input_ids()
}
pub fn serial_numbers(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
self.inputs.serial_numbers()
}
pub fn external_input_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
self.inputs.external_input_ids()
}
pub fn output_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
self.outputs.output_ids()
}
pub fn constant_output_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
self.outputs.constant_output_ids()
}
pub fn public_output_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
self.outputs.public_output_ids()
}
pub fn private_output_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
self.outputs.private_output_ids()
}
pub fn commitments(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
self.outputs.commitments()
}
pub fn external_output_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
self.outputs.external_output_ids()
}
}
impl<N: Network, T: TransitionStorage<N>> TransitionStore<N, T> {
pub fn constant_inputs(&self) -> impl '_ + Iterator<Item = Cow<'_, Plaintext<N>>> {
self.inputs.constant_inputs()
}
pub fn public_inputs(&self) -> impl '_ + Iterator<Item = Cow<'_, Plaintext<N>>> {
self.inputs.public_inputs()
}
pub fn private_inputs(&self) -> impl '_ + Iterator<Item = Cow<'_, Ciphertext<N>>> {
self.inputs.private_inputs()
}
pub fn tags(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
self.inputs.tags()
}
pub fn constant_outputs(&self) -> impl '_ + Iterator<Item = Cow<'_, Plaintext<N>>> {
self.outputs.constant_outputs()
}
pub fn public_outputs(&self) -> impl '_ + Iterator<Item = Cow<'_, Plaintext<N>>> {
self.outputs.public_outputs()
}
pub fn private_outputs(&self) -> impl '_ + Iterator<Item = Cow<'_, Ciphertext<N>>> {
self.outputs.private_outputs()
}
pub fn checksums(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
self.outputs.checksums()
}
pub fn nonces(&self) -> impl '_ + Iterator<Item = Cow<'_, Group<N>>> {
self.outputs.nonces()
}
pub fn records(&self) -> impl '_ + Iterator<Item = (Cow<'_, Field<N>>, Cow<'_, Record<N, Ciphertext<N>>>)> {
self.outputs.records()
}
pub fn tpks(&self) -> impl '_ + Iterator<Item = Cow<'_, Group<N>>> {
self.tpk.values_confirmed()
}
pub fn tcms(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
self.tcm.values_confirmed()
}
pub fn scms(&self) -> impl '_ + Iterator<Item = Cow<'_, Field<N>>> {
self.scm.values_confirmed()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::helpers::memory::TransitionMemory;
#[test]
fn test_insert_get_remove() {
let rng = &mut TestRng::default();
let transaction_0 = ledger_test_helpers::sample_execution_transaction_with_fee(true, rng);
let transaction_1 = ledger_test_helpers::sample_execution_transaction_with_fee(false, rng);
let transactions = vec![transaction_0, transaction_1];
for transaction in transactions {
let transitions = transaction.transitions().cloned().collect::<Vec<_>>();
println!("\n\nNumber of transitions: {}\n", transitions.len());
assert!(transitions.len() > 1, "\n\nNumber of transitions: {}\n", transitions.len());
let transition_store = TransitionMemory::open(None).unwrap();
for transition in transitions.iter() {
let transition_id = *transition.id();
let candidate = transition_store.get(&transition_id).unwrap();
assert_eq!(None, candidate);
transition_store.insert(transition).unwrap();
let candidate = transition_store.get(&transition_id).unwrap();
assert_eq!(Some(transition.clone()), candidate);
transition_store.remove(&transition_id).unwrap();
let candidate = transition_store.get(&transition_id).unwrap();
assert_eq!(None, candidate);
}
for transition in transitions.iter() {
let transition_id = *transition.id();
let candidate = transition_store.get(&transition_id).unwrap();
assert_eq!(None, candidate);
transition_store.insert(transition).unwrap();
let candidate = transition_store.get(&transition_id).unwrap();
assert_eq!(Some(transition.clone()), candidate);
}
for transition in transitions.iter().rev() {
let transition_id = *transition.id();
let candidate = transition_store.get(&transition_id).unwrap();
assert_eq!(Some(transition.clone()), candidate);
}
for transition in transitions.iter().rev() {
let transition_id = *transition.id();
transition_store.remove(&transition_id).unwrap();
let candidate = transition_store.get(&transition_id).unwrap();
assert_eq!(None, candidate);
}
}
}
}