use crate::{
atomic_batch_scope,
cow_to_cloned,
cow_to_copied,
helpers::{Map, MapRead, NestedMap, NestedMapRead},
program::{CommitteeStorage, CommitteeStore},
};
use console::{
network::prelude::*,
program::{Identifier, Plaintext, ProgramID, Value},
types::Field,
};
use synthesizer_program::{FinalizeOperation, FinalizeStoreTrait};
use aleo_std_storage::StorageMode;
use anyhow::Result;
use core::marker::PhantomData;
use indexmap::IndexSet;
fn to_mapping_id<N: Network>(program_id: &ProgramID<N>, mapping_name: &Identifier<N>) -> Result<Field<N>> {
let mut preimage = Vec::new();
program_id.write_bits_le(&mut preimage);
false.write_bits_le(&mut preimage); mapping_name.write_bits_le(&mut preimage);
N::hash_bhp1024(&preimage)
}
fn to_key_id<N: Network>(
program_id: &ProgramID<N>,
mapping_name: &Identifier<N>,
key: &Plaintext<N>,
) -> Result<Field<N>> {
let mut preimage = Vec::new();
program_id.write_bits_le(&mut preimage);
false.write_bits_le(&mut preimage); mapping_name.write_bits_le(&mut preimage);
false.write_bits_le(&mut preimage); key.write_bits_le(&mut preimage);
N::hash_bhp1024(&preimage)
}
pub trait FinalizeStorage<N: Network>: 'static + Clone + Send + Sync {
type CommitteeStorage: CommitteeStorage<N>;
type ProgramIDMap: for<'a> Map<'a, ProgramID<N>, IndexSet<Identifier<N>>>;
type KeyValueMap: for<'a> NestedMap<'a, (ProgramID<N>, Identifier<N>), Plaintext<N>, Value<N>>;
fn open<S: Clone + Into<StorageMode>>(storage: S) -> Result<Self>;
#[cfg(any(test, feature = "test"))]
fn open_testing(temp_dir: std::path::PathBuf, dev: Option<u16>) -> Result<Self>;
fn committee_store(&self) -> &CommitteeStore<N, Self::CommitteeStorage>;
fn program_id_map(&self) -> &Self::ProgramIDMap;
fn key_value_map(&self) -> &Self::KeyValueMap;
fn storage_mode(&self) -> &StorageMode;
fn start_atomic(&self) {
self.committee_store().start_atomic();
self.program_id_map().start_atomic();
self.key_value_map().start_atomic();
}
fn is_atomic_in_progress(&self) -> bool {
self.committee_store().is_atomic_in_progress()
|| self.program_id_map().is_atomic_in_progress()
|| self.key_value_map().is_atomic_in_progress()
}
fn atomic_checkpoint(&self) {
self.committee_store().atomic_checkpoint();
self.program_id_map().atomic_checkpoint();
self.key_value_map().atomic_checkpoint();
}
fn clear_latest_checkpoint(&self) {
self.committee_store().clear_latest_checkpoint();
self.program_id_map().clear_latest_checkpoint();
self.key_value_map().clear_latest_checkpoint();
}
fn atomic_rewind(&self) {
self.committee_store().atomic_rewind();
self.program_id_map().atomic_rewind();
self.key_value_map().atomic_rewind();
}
fn abort_atomic(&self) {
self.committee_store().abort_atomic();
self.program_id_map().abort_atomic();
self.key_value_map().abort_atomic();
}
fn finish_atomic(&self) -> Result<()> {
self.committee_store().finish_atomic()?;
self.program_id_map().finish_atomic()?;
self.key_value_map().finish_atomic()
}
fn initialize_mapping(
&self,
program_id: ProgramID<N>,
mapping_name: Identifier<N>,
) -> Result<FinalizeOperation<N>> {
let mut mapping_names = match self.program_id_map().get_speculative(&program_id)? {
Some(mapping_names) => cow_to_cloned!(mapping_names),
None => IndexSet::new(),
};
if mapping_names.contains(&mapping_name) {
bail!("Illegal operation: mapping name '{mapping_name}' already exists in storage - cannot re-initialize.")
}
mapping_names.insert(mapping_name);
atomic_batch_scope!(self, {
self.program_id_map().insert(program_id, mapping_names)?;
Ok(())
})?;
Ok(FinalizeOperation::InitializeMapping(to_mapping_id(&program_id, &mapping_name)?))
}
fn insert_key_value(
&self,
program_id: ProgramID<N>,
mapping_name: Identifier<N>,
key: Plaintext<N>,
value: Value<N>,
) -> Result<FinalizeOperation<N>> {
if !self.contains_mapping_speculative(&program_id, &mapping_name)? {
bail!("Illegal operation: '{program_id}/{mapping_name}' is not initialized - cannot insert key-value.")
}
if self.contains_key_speculative(program_id, mapping_name, &key)? {
bail!(
"Illegal operation: '{program_id}/{mapping_name}' key '{key}' already exists in storage - cannot insert key-value"
);
}
let key_id = to_key_id(&program_id, &mapping_name, &key)?;
let value_id = N::hash_bhp1024(&(key_id, N::hash_bhp1024(&value.to_bits_le())?).to_bits_le())?;
atomic_batch_scope!(self, {
self.key_value_map().insert((program_id, mapping_name), key, value)?;
Ok(())
})?;
Ok(FinalizeOperation::InsertKeyValue(to_mapping_id(&program_id, &mapping_name)?, key_id, value_id))
}
fn update_key_value(
&self,
program_id: ProgramID<N>,
mapping_name: Identifier<N>,
key: Plaintext<N>,
value: Value<N>,
) -> Result<FinalizeOperation<N>> {
if !self.contains_mapping_speculative(&program_id, &mapping_name)? {
bail!("Illegal operation: '{program_id}/{mapping_name}' is not initialized - cannot update key-value.")
}
let key_id = to_key_id(&program_id, &mapping_name, &key)?;
let value_id = N::hash_bhp1024(&(key_id, N::hash_bhp1024(&value.to_bits_le())?).to_bits_le())?;
atomic_batch_scope!(self, {
self.key_value_map().insert((program_id, mapping_name), key, value)?;
Ok(())
})?;
Ok(FinalizeOperation::UpdateKeyValue(to_mapping_id(&program_id, &mapping_name)?, key_id, value_id))
}
fn remove_key_value(
&self,
program_id: ProgramID<N>,
mapping_name: Identifier<N>,
key: &Plaintext<N>,
) -> Result<Option<FinalizeOperation<N>>> {
if !self.contains_mapping_speculative(&program_id, &mapping_name)? {
bail!("Illegal operation: '{program_id}/{mapping_name}' is not initialized - cannot remove key-value.")
}
if !self.contains_key_speculative(program_id, mapping_name, key)? {
return Ok(None);
}
let key_id = to_key_id(&program_id, &mapping_name, key)?;
atomic_batch_scope!(self, {
self.key_value_map().remove_key(&(program_id, mapping_name), key)?;
Ok(())
})?;
Ok(Some(FinalizeOperation::RemoveKeyValue(to_mapping_id(&program_id, &mapping_name)?, key_id)))
}
fn replace_mapping(
&self,
program_id: ProgramID<N>,
mapping_name: Identifier<N>,
entries: Vec<(Plaintext<N>, Value<N>)>,
) -> Result<FinalizeOperation<N>> {
if !self.contains_mapping_speculative(&program_id, &mapping_name)? {
bail!("Illegal operation: '{program_id}/{mapping_name}' is not initialized - cannot replace mapping.")
}
atomic_batch_scope!(self, {
self.key_value_map().remove_map(&(program_id, mapping_name))?;
for (key, value) in entries {
self.key_value_map().insert((program_id, mapping_name), key, value)?;
}
Ok(())
})?;
Ok(FinalizeOperation::ReplaceMapping(to_mapping_id(&program_id, &mapping_name)?))
}
fn remove_mapping(&self, program_id: ProgramID<N>, mapping_name: Identifier<N>) -> Result<FinalizeOperation<N>> {
let mut mapping_names = match self.program_id_map().get_speculative(&program_id)? {
Some(mapping_names) => cow_to_cloned!(mapping_names),
None => bail!("Illegal operation: program ID '{program_id}' is not initialized - cannot remove mapping."),
};
if !mapping_names.shift_remove(&mapping_name) {
bail!("Illegal operation: mapping '{mapping_name}' does not exist in storage - cannot remove mapping.");
}
atomic_batch_scope!(self, {
self.program_id_map().insert(program_id, mapping_names)?;
self.key_value_map().remove_map(&(program_id, mapping_name))?;
Ok(())
})?;
Ok(FinalizeOperation::RemoveMapping(to_mapping_id(&program_id, &mapping_name)?))
}
fn remove_program(&self, program_id: &ProgramID<N>) -> Result<()> {
let Some(mapping_names) = self.program_id_map().get_speculative(program_id)? else {
bail!("Illegal operation: program ID '{program_id}' is not initialized - cannot remove mapping.")
};
atomic_batch_scope!(self, {
self.program_id_map().remove(program_id)?;
for mapping_name in mapping_names.iter() {
self.key_value_map().remove_map(&(*program_id, *mapping_name))?;
}
Ok(())
})
}
fn contains_program_confirmed(&self, program_id: &ProgramID<N>) -> Result<bool> {
self.program_id_map().contains_key_confirmed(program_id)
}
fn contains_mapping_confirmed(&self, program_id: &ProgramID<N>, mapping_name: &Identifier<N>) -> Result<bool> {
Ok(self.program_id_map().get_confirmed(program_id)?.map_or(false, |m| m.contains(mapping_name)))
}
fn contains_mapping_speculative(&self, program_id: &ProgramID<N>, mapping_name: &Identifier<N>) -> Result<bool> {
Ok(self.program_id_map().get_speculative(program_id)?.map_or(false, |m| m.contains(mapping_name)))
}
fn contains_key_confirmed(
&self,
program_id: ProgramID<N>,
mapping_name: Identifier<N>,
key: &Plaintext<N>,
) -> Result<bool> {
self.key_value_map().contains_key_confirmed(&(program_id, mapping_name), key)
}
fn contains_key_speculative(
&self,
program_id: ProgramID<N>,
mapping_name: Identifier<N>,
key: &Plaintext<N>,
) -> Result<bool> {
self.key_value_map().contains_key_speculative(&(program_id, mapping_name), key)
}
fn get_mapping_names_confirmed(&self, program_id: &ProgramID<N>) -> Result<Option<IndexSet<Identifier<N>>>> {
Ok(self.program_id_map().get_confirmed(program_id)?.map(|names| cow_to_cloned!(names)))
}
fn get_mapping_names_speculative(&self, program_id: &ProgramID<N>) -> Result<Option<IndexSet<Identifier<N>>>> {
Ok(self.program_id_map().get_speculative(program_id)?.map(|names| cow_to_cloned!(names)))
}
fn get_mapping_confirmed(
&self,
program_id: ProgramID<N>,
mapping_name: Identifier<N>,
) -> Result<Vec<(Plaintext<N>, Value<N>)>> {
if !self.contains_mapping_confirmed(&program_id, &mapping_name)? {
bail!("Illegal operation: '{program_id}/{mapping_name}' is not initialized - cannot get mapping (C).")
}
self.key_value_map().get_map_confirmed(&(program_id, mapping_name))
}
fn get_mapping_speculative(
&self,
program_id: ProgramID<N>,
mapping_name: Identifier<N>,
) -> Result<Vec<(Plaintext<N>, Value<N>)>> {
if !self.contains_mapping_speculative(&program_id, &mapping_name)? {
bail!("Illegal operation: '{program_id}/{mapping_name}' is not initialized - cannot get mapping (S).")
}
self.key_value_map().get_map_speculative(&(program_id, mapping_name))
}
fn get_value_confirmed(
&self,
program_id: ProgramID<N>,
mapping_name: Identifier<N>,
key: &Plaintext<N>,
) -> Result<Option<Value<N>>> {
match self.key_value_map().get_value_confirmed(&(program_id, mapping_name), key)? {
Some(value) => Ok(Some(cow_to_cloned!(value))),
None => Ok(None),
}
}
fn get_value_speculative(
&self,
program_id: ProgramID<N>,
mapping_name: Identifier<N>,
key: &Plaintext<N>,
) -> Result<Option<Value<N>>> {
match self.key_value_map().get_value_speculative(&(program_id, mapping_name), key)? {
Some(value) => Ok(Some(cow_to_cloned!(value))),
None => Ok(None),
}
}
fn get_checksum_confirmed(&self) -> Result<Field<N>> {
let preimage: std::collections::BTreeMap<_, _> = self
.key_value_map()
.iter_confirmed()
.map(|(m, k, v)| {
let m = cow_to_copied!(m);
let k = cow_to_cloned!(k);
let v = cow_to_cloned!(v);
let mut preimage = Vec::new();
m.write_bits_le(&mut preimage);
false.write_bits_le(&mut preimage); k.write_bits_le(&mut preimage);
false.write_bits_le(&mut preimage); let mapping_checksum = N::hash_bhp1024(&preimage)?;
v.write_bits_le(&mut preimage);
false.write_bits_le(&mut preimage); let entry_checksum = N::hash_bhp1024(&preimage)?;
Ok::<_, Error>((mapping_checksum, entry_checksum.to_bits_le()))
})
.try_collect()?;
N::hash_bhp1024(&preimage.into_values().flatten().collect::<Vec<_>>())
}
fn get_checksum_pending(&self) -> Result<Field<N>> {
let preimage: std::collections::BTreeMap<_, _> = self
.key_value_map()
.iter_pending()
.map(|(m, k, v)| {
let m = cow_to_copied!(m);
let mut preimage = Vec::new();
m.write_bits_le(&mut preimage);
false.write_bits_le(&mut preimage); if let Some(k) = k {
cow_to_cloned!(k).write_bits_le(&mut preimage);
}
false.write_bits_le(&mut preimage); let mapping_checksum = N::hash_bhp1024(&preimage)?;
if let Some(v) = v {
cow_to_cloned!(v).write_bits_le(&mut preimage);
}
false.write_bits_le(&mut preimage); let entry_checksum = N::hash_bhp1024(&preimage)?;
Ok::<_, Error>((mapping_checksum, entry_checksum.to_bits_le()))
})
.try_collect()?;
N::hash_bhp1024(&preimage.into_values().flatten().collect::<Vec<_>>())
}
}
#[derive(Clone)]
pub struct FinalizeStore<N: Network, P: FinalizeStorage<N>> {
storage: P,
_phantom: PhantomData<N>,
}
impl<N: Network, P: FinalizeStorage<N>> FinalizeStore<N, P> {
pub fn open<S: Clone + Into<StorageMode>>(storage: S) -> Result<Self> {
Self::from(P::open(storage)?)
}
#[cfg(any(test, feature = "test"))]
pub fn open_testing(temp_dir: std::path::PathBuf, dev: Option<u16>) -> Result<Self> {
Self::from(P::open_testing(temp_dir, dev)?)
}
pub fn from(storage: P) -> Result<Self> {
Ok(Self { storage, _phantom: PhantomData })
}
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, P: FinalizeStorage<N>> FinalizeStore<N, P> {
pub fn committee_store(&self) -> &CommitteeStore<N, P::CommitteeStorage> {
self.storage.committee_store()
}
}
impl<N: Network, P: FinalizeStorage<N>> FinalizeStoreTrait<N> for FinalizeStore<N, P> {
fn contains_mapping_confirmed(&self, program_id: &ProgramID<N>, mapping_name: &Identifier<N>) -> Result<bool> {
self.storage.contains_mapping_confirmed(program_id, mapping_name)
}
fn contains_key_speculative(
&self,
program_id: ProgramID<N>,
mapping_name: Identifier<N>,
key: &Plaintext<N>,
) -> Result<bool> {
self.storage.contains_key_speculative(program_id, mapping_name, key)
}
fn get_value_speculative(
&self,
program_id: ProgramID<N>,
mapping_name: Identifier<N>,
key: &Plaintext<N>,
) -> Result<Option<Value<N>>> {
self.storage.get_value_speculative(program_id, mapping_name, key)
}
fn insert_key_value(
&self,
program_id: ProgramID<N>,
mapping_name: Identifier<N>,
key: Plaintext<N>,
value: Value<N>,
) -> Result<FinalizeOperation<N>> {
self.storage.insert_key_value(program_id, mapping_name, key, value)
}
fn update_key_value(
&self,
program_id: ProgramID<N>,
mapping_name: Identifier<N>,
key: Plaintext<N>,
value: Value<N>,
) -> Result<FinalizeOperation<N>> {
self.storage.update_key_value(program_id, mapping_name, key, value)
}
fn remove_key_value(
&self,
program_id: ProgramID<N>,
mapping_name: Identifier<N>,
key: &Plaintext<N>,
) -> Result<Option<FinalizeOperation<N>>> {
self.storage.remove_key_value(program_id, mapping_name, key)
}
}
impl<N: Network, P: FinalizeStorage<N>> FinalizeStore<N, P> {
pub fn initialize_mapping(
&self,
program_id: ProgramID<N>,
mapping_name: Identifier<N>,
) -> Result<FinalizeOperation<N>> {
self.storage.initialize_mapping(program_id, mapping_name)
}
pub fn replace_mapping(
&self,
program_id: ProgramID<N>,
mapping_name: Identifier<N>,
entries: Vec<(Plaintext<N>, Value<N>)>,
) -> Result<FinalizeOperation<N>> {
self.storage.replace_mapping(program_id, mapping_name, entries)
}
pub fn remove_mapping(
&self,
program_id: ProgramID<N>,
mapping_name: Identifier<N>,
) -> Result<FinalizeOperation<N>> {
self.storage.remove_mapping(program_id, mapping_name)
}
pub fn remove_program(&self, program_id: &ProgramID<N>) -> Result<()> {
self.storage.remove_program(program_id)
}
}
impl<N: Network, P: FinalizeStorage<N>> FinalizeStore<N, P> {
pub fn contains_program_confirmed(&self, program_id: &ProgramID<N>) -> Result<bool> {
self.storage.contains_program_confirmed(program_id)
}
pub fn contains_key_confirmed(
&self,
program_id: ProgramID<N>,
mapping_name: Identifier<N>,
key: &Plaintext<N>,
) -> Result<bool> {
self.storage.contains_key_confirmed(program_id, mapping_name, key)
}
}
impl<N: Network, P: FinalizeStorage<N>> FinalizeStore<N, P> {
pub fn get_mapping_names_confirmed(&self, program_id: &ProgramID<N>) -> Result<Option<IndexSet<Identifier<N>>>> {
self.storage.get_mapping_names_confirmed(program_id)
}
pub fn get_mapping_confirmed(
&self,
program_id: ProgramID<N>,
mapping_name: Identifier<N>,
) -> Result<Vec<(Plaintext<N>, Value<N>)>> {
self.storage.get_mapping_confirmed(program_id, mapping_name)
}
pub fn get_mapping_speculative(
&self,
program_id: ProgramID<N>,
mapping_name: Identifier<N>,
) -> Result<Vec<(Plaintext<N>, Value<N>)>> {
self.storage.get_mapping_speculative(program_id, mapping_name)
}
pub fn get_value_confirmed(
&self,
program_id: ProgramID<N>,
mapping_name: Identifier<N>,
key: &Plaintext<N>,
) -> Result<Option<Value<N>>> {
self.storage.get_value_confirmed(program_id, mapping_name, key)
}
pub fn get_value_speculative(
&self,
program_id: ProgramID<N>,
mapping_name: Identifier<N>,
key: &Plaintext<N>,
) -> Result<Option<Value<N>>> {
self.storage.get_value_speculative(program_id, mapping_name, key)
}
pub fn get_checksum_confirmed(&self) -> Result<Field<N>> {
self.storage.get_checksum_confirmed()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::helpers::memory::FinalizeMemory;
use console::{network::MainnetV0, program::Literal, types::U64};
type CurrentNetwork = MainnetV0;
fn check_initialize_insert_remove<N: Network>(
finalize_store: &FinalizeStore<N, FinalizeMemory<N>>,
program_id: ProgramID<N>,
mapping_name: Identifier<N>,
) {
let key = Plaintext::from_str("123456789field").unwrap();
let value = Value::from_str("987654321u128").unwrap();
assert!(!finalize_store.contains_program_confirmed(&program_id).unwrap());
assert!(!finalize_store.contains_mapping_confirmed(&program_id, &mapping_name).unwrap());
assert!(finalize_store.remove_mapping(program_id, mapping_name).is_err());
finalize_store.initialize_mapping(program_id, mapping_name).unwrap();
assert!(finalize_store.contains_program_confirmed(&program_id).unwrap());
assert!(finalize_store.contains_mapping_confirmed(&program_id, &mapping_name).unwrap());
assert!(!finalize_store.contains_key_confirmed(program_id, mapping_name, &key).unwrap());
assert!(finalize_store.get_value_speculative(program_id, mapping_name, &key).unwrap().is_none());
finalize_store.insert_key_value(program_id, mapping_name, key.clone(), value.clone()).unwrap();
assert!(finalize_store.contains_program_confirmed(&program_id).unwrap());
assert!(finalize_store.contains_mapping_confirmed(&program_id, &mapping_name).unwrap());
assert!(finalize_store.contains_key_confirmed(program_id, mapping_name, &key).unwrap());
assert_eq!(value, finalize_store.get_value_speculative(program_id, mapping_name, &key).unwrap().unwrap());
assert!(finalize_store.remove_key_value(program_id, mapping_name, &key).unwrap().is_some());
assert!(finalize_store.contains_program_confirmed(&program_id).unwrap());
assert!(finalize_store.contains_mapping_confirmed(&program_id, &mapping_name).unwrap());
assert!(!finalize_store.contains_key_confirmed(program_id, mapping_name, &key).unwrap());
assert!(finalize_store.get_value_speculative(program_id, mapping_name, &key).unwrap().is_none());
finalize_store.remove_mapping(program_id, mapping_name).unwrap();
assert!(finalize_store.contains_program_confirmed(&program_id).unwrap());
assert!(!finalize_store.contains_mapping_confirmed(&program_id, &mapping_name).unwrap());
assert!(!finalize_store.contains_key_confirmed(program_id, mapping_name, &key).unwrap());
assert!(finalize_store.get_value_speculative(program_id, mapping_name, &key).unwrap().is_none());
finalize_store.remove_program(&program_id).unwrap();
assert!(!finalize_store.contains_program_confirmed(&program_id).unwrap());
assert!(!finalize_store.contains_mapping_confirmed(&program_id, &mapping_name).unwrap());
assert!(!finalize_store.contains_key_confirmed(program_id, mapping_name, &key).unwrap());
assert!(finalize_store.get_value_speculative(program_id, mapping_name, &key).unwrap().is_none());
}
fn check_initialize_update_remove<N: Network>(
finalize_store: &FinalizeStore<N, FinalizeMemory<N>>,
program_id: ProgramID<N>,
mapping_name: Identifier<N>,
) {
let key = Plaintext::from_str("123456789field").unwrap();
let value = Value::from_str("987654321u128").unwrap();
assert!(!finalize_store.contains_program_confirmed(&program_id).unwrap());
assert!(!finalize_store.contains_mapping_confirmed(&program_id, &mapping_name).unwrap());
assert!(finalize_store.remove_mapping(program_id, mapping_name).is_err());
finalize_store.initialize_mapping(program_id, mapping_name).unwrap();
assert!(finalize_store.contains_program_confirmed(&program_id).unwrap());
assert!(finalize_store.contains_mapping_confirmed(&program_id, &mapping_name).unwrap());
assert!(!finalize_store.contains_key_confirmed(program_id, mapping_name, &key).unwrap());
assert!(finalize_store.get_value_speculative(program_id, mapping_name, &key).unwrap().is_none());
finalize_store.update_key_value(program_id, mapping_name, key.clone(), value.clone()).unwrap();
assert!(finalize_store.contains_program_confirmed(&program_id).unwrap());
assert!(finalize_store.contains_mapping_confirmed(&program_id, &mapping_name).unwrap());
assert!(finalize_store.contains_key_confirmed(program_id, mapping_name, &key).unwrap());
assert_eq!(value, finalize_store.get_value_speculative(program_id, mapping_name, &key).unwrap().unwrap());
assert!(finalize_store.insert_key_value(program_id, mapping_name, key.clone(), value.clone()).is_err());
assert!(finalize_store.contains_key_confirmed(program_id, mapping_name, &key).unwrap());
assert_eq!(value, finalize_store.get_value_speculative(program_id, mapping_name, &key).unwrap().unwrap());
finalize_store.update_key_value(program_id, mapping_name, key.clone(), value.clone()).unwrap();
assert!(finalize_store.contains_key_confirmed(program_id, mapping_name, &key).unwrap());
assert_eq!(value, finalize_store.get_value_speculative(program_id, mapping_name, &key).unwrap().unwrap());
{
let new_value = Value::from_str("123456789u128").unwrap();
assert!(finalize_store.insert_key_value(program_id, mapping_name, key.clone(), new_value.clone()).is_err());
assert!(finalize_store.contains_key_confirmed(program_id, mapping_name, &key).unwrap());
assert_eq!(value, finalize_store.get_value_speculative(program_id, mapping_name, &key).unwrap().unwrap());
finalize_store.update_key_value(program_id, mapping_name, key.clone(), new_value.clone()).unwrap();
assert!(finalize_store.contains_key_confirmed(program_id, mapping_name, &key).unwrap());
assert_eq!(
new_value,
finalize_store.get_value_speculative(program_id, mapping_name, &key).unwrap().unwrap()
);
finalize_store.update_key_value(program_id, mapping_name, key.clone(), value.clone()).unwrap();
assert!(finalize_store.contains_key_confirmed(program_id, mapping_name, &key).unwrap());
assert_eq!(value, finalize_store.get_value_speculative(program_id, mapping_name, &key).unwrap().unwrap());
}
assert!(finalize_store.remove_key_value(program_id, mapping_name, &key).unwrap().is_some());
assert!(finalize_store.contains_program_confirmed(&program_id).unwrap());
assert!(finalize_store.contains_mapping_confirmed(&program_id, &mapping_name).unwrap());
assert!(!finalize_store.contains_key_confirmed(program_id, mapping_name, &key).unwrap());
assert!(finalize_store.get_value_speculative(program_id, mapping_name, &key).unwrap().is_none());
finalize_store.remove_mapping(program_id, mapping_name).unwrap();
assert!(finalize_store.contains_program_confirmed(&program_id).unwrap());
assert!(!finalize_store.contains_mapping_confirmed(&program_id, &mapping_name).unwrap());
assert!(!finalize_store.contains_key_confirmed(program_id, mapping_name, &key).unwrap());
assert!(finalize_store.get_value_speculative(program_id, mapping_name, &key).unwrap().is_none());
finalize_store.remove_program(&program_id).unwrap();
assert!(!finalize_store.contains_program_confirmed(&program_id).unwrap());
assert!(!finalize_store.contains_mapping_confirmed(&program_id, &mapping_name).unwrap());
assert!(!finalize_store.contains_key_confirmed(program_id, mapping_name, &key).unwrap());
assert!(finalize_store.get_value_speculative(program_id, mapping_name, &key).unwrap().is_none());
}
#[test]
fn test_initialize_insert_remove() {
let program_id = ProgramID::<CurrentNetwork>::from_str("hello.aleo").unwrap();
let mapping_name = Identifier::from_str("account").unwrap();
let program_memory = FinalizeMemory::open(None).unwrap();
let finalize_store = FinalizeStore::from(program_memory).unwrap();
check_initialize_insert_remove(&finalize_store, program_id, mapping_name);
}
#[test]
fn test_initialize_update_remove() {
let program_id = ProgramID::<CurrentNetwork>::from_str("hello.aleo").unwrap();
let mapping_name = Identifier::from_str("account").unwrap();
let program_memory = FinalizeMemory::open(None).unwrap();
let finalize_store = FinalizeStore::from(program_memory).unwrap();
check_initialize_update_remove(&finalize_store, program_id, mapping_name);
}
#[test]
fn test_remove_key_value() {
let program_id = ProgramID::<CurrentNetwork>::from_str("hello.aleo").unwrap();
let mapping_name = Identifier::from_str("account").unwrap();
let program_memory = FinalizeMemory::open(None).unwrap();
let finalize_store = FinalizeStore::from(program_memory).unwrap();
assert!(!finalize_store.contains_program_confirmed(&program_id).unwrap());
assert!(!finalize_store.contains_mapping_confirmed(&program_id, &mapping_name).unwrap());
assert!(finalize_store.remove_mapping(program_id, mapping_name).is_err());
finalize_store.initialize_mapping(program_id, mapping_name).unwrap();
assert!(finalize_store.contains_program_confirmed(&program_id).unwrap());
assert!(finalize_store.contains_mapping_confirmed(&program_id, &mapping_name).unwrap());
for item in 0..1000 {
let key = Plaintext::from_str(&format!("{item}field")).unwrap();
assert!(!finalize_store.contains_key_confirmed(program_id, mapping_name, &key).unwrap());
assert!(finalize_store.get_value_speculative(program_id, mapping_name, &key).unwrap().is_none());
assert!(finalize_store.remove_key_value(program_id, mapping_name, &key).unwrap().is_none());
assert!(finalize_store.contains_program_confirmed(&program_id).unwrap());
assert!(finalize_store.contains_mapping_confirmed(&program_id, &mapping_name).unwrap());
assert!(!finalize_store.contains_key_confirmed(program_id, mapping_name, &key).unwrap());
assert!(finalize_store.get_value_speculative(program_id, mapping_name, &key).unwrap().is_none());
}
for item in 0..1000 {
let key = Plaintext::from_str(&format!("{item}field")).unwrap();
let value = Value::from_str(&format!("{item}u64")).unwrap();
assert!(!finalize_store.contains_key_confirmed(program_id, mapping_name, &key).unwrap());
assert!(finalize_store.get_value_speculative(program_id, mapping_name, &key).unwrap().is_none());
finalize_store.insert_key_value(program_id, mapping_name, key.clone(), value.clone()).unwrap();
assert!(finalize_store.contains_program_confirmed(&program_id).unwrap());
assert!(finalize_store.contains_mapping_confirmed(&program_id, &mapping_name).unwrap());
assert!(finalize_store.contains_key_confirmed(program_id, mapping_name, &key).unwrap());
assert_eq!(value, finalize_store.get_value_speculative(program_id, mapping_name, &key).unwrap().unwrap());
}
for item in 0..1000 {
let key = Plaintext::from_str(&format!("{item}field")).unwrap();
let value = Value::from_str(&format!("{item}u64")).unwrap();
assert!(finalize_store.contains_key_confirmed(program_id, mapping_name, &key).unwrap());
assert_eq!(value, finalize_store.get_value_speculative(program_id, mapping_name, &key).unwrap().unwrap());
assert!(finalize_store.remove_key_value(program_id, mapping_name, &key).unwrap().is_some());
assert!(finalize_store.contains_program_confirmed(&program_id).unwrap());
assert!(finalize_store.contains_mapping_confirmed(&program_id, &mapping_name).unwrap());
assert!(!finalize_store.contains_key_confirmed(program_id, mapping_name, &key).unwrap());
assert!(finalize_store.get_value_speculative(program_id, mapping_name, &key).unwrap().is_none());
}
}
#[test]
fn test_remove_mapping() {
let program_id = ProgramID::<CurrentNetwork>::from_str("hello.aleo").unwrap();
let mapping_name = Identifier::from_str("account").unwrap();
let program_memory = FinalizeMemory::open(None).unwrap();
let finalize_store = FinalizeStore::from(program_memory).unwrap();
assert!(!finalize_store.contains_program_confirmed(&program_id).unwrap());
assert!(!finalize_store.contains_mapping_confirmed(&program_id, &mapping_name).unwrap());
assert!(finalize_store.remove_mapping(program_id, mapping_name).is_err());
finalize_store.initialize_mapping(program_id, mapping_name).unwrap();
assert!(finalize_store.contains_program_confirmed(&program_id).unwrap());
assert!(finalize_store.contains_mapping_confirmed(&program_id, &mapping_name).unwrap());
for item in 0..1000 {
let key = Plaintext::from_str(&format!("{item}field")).unwrap();
let value = Value::from_str(&format!("{item}u64")).unwrap();
assert!(!finalize_store.contains_key_confirmed(program_id, mapping_name, &key).unwrap());
assert!(finalize_store.get_value_speculative(program_id, mapping_name, &key).unwrap().is_none());
finalize_store.insert_key_value(program_id, mapping_name, key.clone(), value.clone()).unwrap();
assert!(finalize_store.contains_program_confirmed(&program_id).unwrap());
assert!(finalize_store.contains_mapping_confirmed(&program_id, &mapping_name).unwrap());
assert!(finalize_store.contains_key_confirmed(program_id, mapping_name, &key).unwrap());
assert_eq!(value, finalize_store.get_value_speculative(program_id, mapping_name, &key).unwrap().unwrap());
}
finalize_store.remove_mapping(program_id, mapping_name).unwrap();
assert!(finalize_store.contains_program_confirmed(&program_id).unwrap());
assert!(!finalize_store.contains_mapping_confirmed(&program_id, &mapping_name).unwrap());
for item in 0..1000 {
let key = Plaintext::from_str(&format!("{item}field")).unwrap();
assert!(!finalize_store.contains_key_confirmed(program_id, mapping_name, &key).unwrap());
assert!(finalize_store.get_value_speculative(program_id, mapping_name, &key).unwrap().is_none());
}
}
#[test]
fn test_remove_program() {
let program_id = ProgramID::<CurrentNetwork>::from_str("hello.aleo").unwrap();
let mapping_name = Identifier::from_str("account").unwrap();
let program_memory = FinalizeMemory::open(None).unwrap();
let finalize_store = FinalizeStore::from(program_memory).unwrap();
assert!(!finalize_store.contains_program_confirmed(&program_id).unwrap());
assert!(!finalize_store.contains_mapping_confirmed(&program_id, &mapping_name).unwrap());
assert!(finalize_store.remove_mapping(program_id, mapping_name).is_err());
finalize_store.initialize_mapping(program_id, mapping_name).unwrap();
assert!(finalize_store.contains_program_confirmed(&program_id).unwrap());
assert!(finalize_store.contains_mapping_confirmed(&program_id, &mapping_name).unwrap());
for item in 0..1000 {
let key = Plaintext::from_str(&format!("{item}field")).unwrap();
let value = Value::from_str(&format!("{item}u64")).unwrap();
assert!(!finalize_store.contains_key_confirmed(program_id, mapping_name, &key).unwrap());
assert!(finalize_store.get_value_speculative(program_id, mapping_name, &key).unwrap().is_none());
finalize_store.insert_key_value(program_id, mapping_name, key.clone(), value.clone()).unwrap();
assert!(finalize_store.contains_program_confirmed(&program_id).unwrap());
assert!(finalize_store.contains_mapping_confirmed(&program_id, &mapping_name).unwrap());
assert!(finalize_store.contains_key_confirmed(program_id, mapping_name, &key).unwrap());
assert_eq!(value, finalize_store.get_value_speculative(program_id, mapping_name, &key).unwrap().unwrap());
}
finalize_store.remove_program(&program_id).unwrap();
assert!(!finalize_store.contains_program_confirmed(&program_id).unwrap());
assert!(!finalize_store.contains_mapping_confirmed(&program_id, &mapping_name).unwrap());
for item in 0..1000 {
let key = Plaintext::from_str(&format!("{item}field")).unwrap();
assert!(!finalize_store.contains_key_confirmed(program_id, mapping_name, &key).unwrap());
assert!(finalize_store.get_value_speculative(program_id, mapping_name, &key).unwrap().is_none());
}
}
#[test]
fn test_must_initialize_first() {
let program_id = ProgramID::<CurrentNetwork>::from_str("hello.aleo").unwrap();
let mapping_name = Identifier::from_str("account").unwrap();
let program_memory = FinalizeMemory::open(None).unwrap();
let finalize_store = FinalizeStore::from(program_memory).unwrap();
assert!(!finalize_store.contains_program_confirmed(&program_id).unwrap());
assert!(!finalize_store.contains_mapping_confirmed(&program_id, &mapping_name).unwrap());
assert!(finalize_store.remove_mapping(program_id, mapping_name).is_err());
{
let key = Plaintext::from_str("123456789field").unwrap();
let value = Value::from_str("987654321u128").unwrap();
assert!(finalize_store.insert_key_value(program_id, mapping_name, key.clone(), value).is_err());
assert!(!finalize_store.contains_program_confirmed(&program_id).unwrap());
assert!(!finalize_store.contains_mapping_confirmed(&program_id, &mapping_name).unwrap());
assert!(!finalize_store.contains_key_confirmed(program_id, mapping_name, &key).unwrap());
assert!(finalize_store.get_value_speculative(program_id, mapping_name, &key).unwrap().is_none());
assert!(finalize_store.remove_key_value(program_id, mapping_name, &key).is_err());
assert!(finalize_store.remove_mapping(program_id, mapping_name).is_err());
}
{
let key = Plaintext::from_str("987654321field").unwrap();
let value = Value::from_str("123456789u128").unwrap();
assert!(finalize_store.update_key_value(program_id, mapping_name, key.clone(), value).is_err());
assert!(!finalize_store.contains_program_confirmed(&program_id).unwrap());
assert!(!finalize_store.contains_mapping_confirmed(&program_id, &mapping_name).unwrap());
assert!(!finalize_store.contains_key_confirmed(program_id, mapping_name, &key).unwrap());
assert!(finalize_store.get_value_speculative(program_id, mapping_name, &key).unwrap().is_none());
assert!(finalize_store.remove_key_value(program_id, mapping_name, &key).is_err());
assert!(finalize_store.remove_mapping(program_id, mapping_name).is_err());
}
check_initialize_insert_remove(&finalize_store, program_id, mapping_name);
check_initialize_update_remove(&finalize_store, program_id, mapping_name);
}
#[test]
fn test_finalize_timings() {
let rng = &mut TestRng::default();
let num_items: u128 = std::env::var("NUM_ITEMS")
.unwrap_or_else(|_| "100000".to_string())
.parse()
.expect("Failed to parse NUM_ITEMS as u128");
let program_id = ProgramID::<CurrentNetwork>::from_str("hello.aleo").unwrap();
let mapping_name = Identifier::from_str("account").unwrap();
#[cfg(not(feature = "rocks"))]
let finalize_store = {
let program_memory = FinalizeMemory::open(None).unwrap();
FinalizeStore::from(program_memory).unwrap()
};
#[cfg(feature = "rocks")]
let finalize_store = {
let temp_dir = tempfile::tempdir().expect("Failed to open temporary directory").into_path();
let program_rocksdb = crate::helpers::rocksdb::FinalizeDB::open_testing(temp_dir, None).unwrap();
FinalizeStore::from(program_rocksdb).unwrap()
};
let timer = std::time::Instant::now();
finalize_store.initialize_mapping(program_id, mapping_name).unwrap();
println!("FinalizeStore::initialize_mapping - {} μs", timer.elapsed().as_micros());
let item: u64 = 100u64;
let key = Plaintext::from(Literal::Field(Field::from_u64(item)));
let value = Value::from(Literal::U64(U64::new(item)));
let timer = std::time::Instant::now();
finalize_store.insert_key_value(program_id, mapping_name, key.clone(), value).unwrap();
println!("FinalizeStore::insert_key_value - {} μs", timer.elapsed().as_micros());
let mut elapsed = 0u128;
finalize_store.start_atomic();
for i in 0..num_items {
if i != 0 && i % 10_000 == 0 {
if finalize_store.is_atomic_in_progress() {
finalize_store.finish_atomic().unwrap();
}
println!("FinalizeStore::insert_key_value - {} μs (average over {i} items)", elapsed / i);
finalize_store.start_atomic();
}
let item: u64 = rng.gen();
let key = Plaintext::from(Literal::Field(Field::from_u64(item)));
let value = Value::from(Literal::U64(U64::new(item)));
let timer = std::time::Instant::now();
finalize_store.insert_key_value(program_id, mapping_name, key, value).unwrap();
elapsed = elapsed.checked_add(timer.elapsed().as_micros()).unwrap();
}
if finalize_store.is_atomic_in_progress() {
finalize_store.finish_atomic().unwrap();
}
println!("FinalizeStore::insert_key_value - {} μs (average over {num_items} items)", elapsed / num_items);
let timer = std::time::Instant::now();
finalize_store.get_checksum_confirmed().unwrap();
println!("FinalizeStore::get_checksum_confirmed - {} μs", timer.elapsed().as_micros());
let timer = std::time::Instant::now();
assert!(finalize_store.contains_program_confirmed(&program_id).unwrap());
println!("FinalizeStore::contains_program_confirmed - {} μs", timer.elapsed().as_micros());
let timer = std::time::Instant::now();
assert!(finalize_store.contains_mapping_confirmed(&program_id, &mapping_name).unwrap());
println!("FinalizeStore::contains_mapping_confirmed - {} μs", timer.elapsed().as_micros());
let timer = std::time::Instant::now();
assert!(finalize_store.contains_key_confirmed(program_id, mapping_name, &key).unwrap());
println!("FinalizeStore::contains_key_confirmed - {} μs", timer.elapsed().as_micros());
let timer = std::time::Instant::now();
finalize_store.get_value_speculative(program_id, mapping_name, &key).unwrap().unwrap();
println!("FinalizeStore::get_value_speculative - {} μs", timer.elapsed().as_micros());
let timer = std::time::Instant::now();
assert!(finalize_store.remove_key_value(program_id, mapping_name, &key).unwrap().is_some());
println!("FinalizeStore::remove_key_value - {} μs", timer.elapsed().as_micros());
let timer = std::time::Instant::now();
finalize_store.remove_mapping(program_id, mapping_name).unwrap();
println!("FinalizeStore::remove_mapping - {} μs", timer.elapsed().as_micros());
let timer = std::time::Instant::now();
finalize_store.remove_program(&program_id).unwrap();
println!("FinalizeStore::remove_program - {} μs", timer.elapsed().as_micros());
}
}