snarkvm_ledger_store/transaction/
fee.rsuse crate::{
TransitionStorage,
TransitionStore,
atomic_batch_scope,
cow_to_cloned,
cow_to_copied,
helpers::{Map, MapRead},
};
use console::network::prelude::*;
use ledger_block::Fee;
use synthesizer_snark::Proof;
use aleo_std_storage::StorageMode;
use anyhow::Result;
use core::marker::PhantomData;
pub trait FeeStorage<N: Network>: Clone + Send + Sync {
type FeeMap: for<'a> Map<'a, N::TransactionID, (N::TransitionID, N::StateRoot, Option<Proof<N>>)>;
type ReverseFeeMap: for<'a> Map<'a, N::TransitionID, N::TransactionID>;
type TransitionStorage: TransitionStorage<N>;
fn open(transition_store: TransitionStore<N, Self::TransitionStorage>) -> Result<Self>;
fn fee_map(&self) -> &Self::FeeMap;
fn reverse_fee_map(&self) -> &Self::ReverseFeeMap;
fn transition_store(&self) -> &TransitionStore<N, Self::TransitionStorage>;
fn storage_mode(&self) -> &StorageMode {
self.transition_store().storage_mode()
}
fn start_atomic(&self) {
self.fee_map().start_atomic();
self.reverse_fee_map().start_atomic();
self.transition_store().start_atomic();
}
fn is_atomic_in_progress(&self) -> bool {
self.fee_map().is_atomic_in_progress()
|| self.reverse_fee_map().is_atomic_in_progress()
|| self.transition_store().is_atomic_in_progress()
}
fn atomic_checkpoint(&self) {
self.fee_map().atomic_checkpoint();
self.reverse_fee_map().atomic_checkpoint();
self.transition_store().atomic_checkpoint();
}
fn clear_latest_checkpoint(&self) {
self.fee_map().clear_latest_checkpoint();
self.reverse_fee_map().clear_latest_checkpoint();
self.transition_store().clear_latest_checkpoint();
}
fn atomic_rewind(&self) {
self.fee_map().atomic_rewind();
self.reverse_fee_map().atomic_rewind();
self.transition_store().atomic_rewind();
}
fn abort_atomic(&self) {
self.fee_map().abort_atomic();
self.reverse_fee_map().abort_atomic();
self.transition_store().abort_atomic();
}
fn finish_atomic(&self) -> Result<()> {
self.fee_map().finish_atomic()?;
self.reverse_fee_map().finish_atomic()?;
self.transition_store().finish_atomic()
}
fn insert(&self, transaction_id: N::TransactionID, fee: &Fee<N>) -> Result<()> {
atomic_batch_scope!(self, {
self.fee_map()
.insert(transaction_id, (*fee.transition_id(), fee.global_state_root(), fee.proof().cloned()))?;
self.reverse_fee_map().insert(*fee.transition_id(), transaction_id)?;
self.transition_store().insert(fee)?;
Ok(())
})
}
fn remove(&self, transaction_id: &N::TransactionID) -> Result<()> {
let (transition_id, _, _) = match self.fee_map().get_confirmed(transaction_id)? {
Some(fee_id) => cow_to_cloned!(fee_id),
None => bail!("Failed to locate the fee transition ID for transaction '{transaction_id}'"),
};
atomic_batch_scope!(self, {
self.fee_map().remove(transaction_id)?;
self.reverse_fee_map().remove(&transition_id)?;
self.transition_store().remove(&transition_id)?;
Ok(())
})
}
fn find_transaction_id_from_transition_id(
&self,
transition_id: &N::TransitionID,
) -> Result<Option<N::TransactionID>> {
match self.reverse_fee_map().get_confirmed(transition_id)? {
Some(transaction_id) => Ok(Some(cow_to_copied!(transaction_id))),
None => Ok(None),
}
}
fn get_fee(&self, transaction_id: &N::TransactionID) -> Result<Option<Fee<N>>> {
let (fee_transition_id, global_state_root, proof) = match self.fee_map().get_confirmed(transaction_id)? {
Some(fee) => cow_to_cloned!(fee),
None => return Ok(None),
};
match self.transition_store().get_transition(&fee_transition_id)? {
Some(transition) => Ok(Some(Fee::from_unchecked(transition, global_state_root, proof))),
None => bail!("Failed to locate the fee transition for transaction '{transaction_id}'"),
}
}
}
#[derive(Clone)]
pub struct FeeStore<N: Network, F: FeeStorage<N>> {
storage: F,
_phantom: PhantomData<N>,
}
impl<N: Network, F: FeeStorage<N>> FeeStore<N, F> {
pub fn open(transition_store: TransitionStore<N, F::TransitionStorage>) -> Result<Self> {
let storage = F::open(transition_store)?;
Ok(Self { storage, _phantom: PhantomData })
}
pub fn from(storage: F) -> Self {
Self { storage, _phantom: PhantomData }
}
pub fn insert(&self, transaction_id: N::TransactionID, fee: &Fee<N>) -> Result<()> {
self.storage.insert(transaction_id, fee)
}
pub fn remove(&self, transaction_id: &N::TransactionID) -> Result<()> {
self.storage.remove(transaction_id)
}
pub fn transition_store(&self) -> &TransitionStore<N, F::TransitionStorage> {
self.storage.transition_store()
}
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, F: FeeStorage<N>> FeeStore<N, F> {
pub fn get_fee(&self, transaction_id: &N::TransactionID) -> Result<Option<Fee<N>>> {
self.storage.get_fee(transaction_id)
}
}
impl<N: Network, F: FeeStorage<N>> FeeStore<N, F> {
pub fn find_transaction_id_from_transition_id(
&self,
transition_id: &N::TransitionID,
) -> Result<Option<N::TransactionID>> {
self.storage.find_transaction_id_from_transition_id(transition_id)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::helpers::memory::FeeMemory;
use ledger_block::Transaction;
#[test]
fn test_insert_get_remove() {
let rng = &mut TestRng::default();
let transaction_0 = ledger_test_helpers::sample_fee_private_transaction(rng);
let transaction_1 = ledger_test_helpers::sample_fee_public_transaction(rng);
let transactions = vec![transaction_0, transaction_1];
for transaction in transactions {
let (transaction_id, fee) = match transaction {
Transaction::Fee(id, fee) => (id, fee),
_ => unreachable!("Invalid transaction type - expected a fee transaction"),
};
let transition_store = TransitionStore::open(None).unwrap();
let fee_store = FeeMemory::open(transition_store).unwrap();
let candidate = fee_store.get_fee(&transaction_id).unwrap();
assert_eq!(None, candidate);
fee_store.insert(transaction_id, &fee).unwrap();
let candidate = fee_store.get_fee(&transaction_id).unwrap();
assert_eq!(Some(fee), candidate);
fee_store.remove(&transaction_id).unwrap();
let candidate = fee_store.get_fee(&transaction_id).unwrap();
assert_eq!(None, candidate);
}
}
#[test]
fn test_find_transaction_id() {
let rng = &mut TestRng::default();
let transaction_0 = ledger_test_helpers::sample_fee_private_transaction(rng);
let transaction_1 = ledger_test_helpers::sample_fee_public_transaction(rng);
let transactions = vec![transaction_0, transaction_1];
for transaction in transactions {
let (transaction_id, fee) = match transaction {
Transaction::Fee(id, fee) => (id, fee),
_ => unreachable!("Invalid transaction type - expected a fee transaction"),
};
let fee_transition_id = fee.id();
let transition_store = TransitionStore::open(None).unwrap();
let fee_store = FeeMemory::open(transition_store).unwrap();
let candidate = fee_store.get_fee(&transaction_id).unwrap();
assert_eq!(None, candidate);
let candidate = fee_store.find_transaction_id_from_transition_id(fee_transition_id).unwrap();
assert_eq!(None, candidate);
fee_store.insert(transaction_id, &fee).unwrap();
let candidate = fee_store.find_transaction_id_from_transition_id(fee_transition_id).unwrap();
assert_eq!(Some(transaction_id), candidate);
fee_store.remove(&transaction_id).unwrap();
let candidate = fee_store.find_transaction_id_from_transition_id(fee_transition_id).unwrap();
assert_eq!(None, candidate);
}
}
}