snarkvm_ledger_store/transaction/
fee.rs1use crate::{
17 TransitionStorage,
18 TransitionStore,
19 atomic_batch_scope,
20 cow_to_cloned,
21 cow_to_copied,
22 helpers::{Map, MapRead},
23};
24use console::network::prelude::*;
25use ledger_block::Fee;
26use synthesizer_snark::Proof;
27
28use aleo_std_storage::StorageMode;
29use anyhow::Result;
30use core::marker::PhantomData;
31
32pub trait FeeStorage<N: Network>: Clone + Send + Sync {
34 type FeeMap: for<'a> Map<'a, N::TransactionID, (N::TransitionID, N::StateRoot, Option<Proof<N>>)>;
36 type ReverseFeeMap: for<'a> Map<'a, N::TransitionID, N::TransactionID>;
38
39 type TransitionStorage: TransitionStorage<N>;
41
42 fn open(transition_store: TransitionStore<N, Self::TransitionStorage>) -> Result<Self>;
44
45 fn fee_map(&self) -> &Self::FeeMap;
47 fn reverse_fee_map(&self) -> &Self::ReverseFeeMap;
49 fn transition_store(&self) -> &TransitionStore<N, Self::TransitionStorage>;
51
52 fn storage_mode(&self) -> &StorageMode {
54 self.transition_store().storage_mode()
55 }
56
57 fn start_atomic(&self) {
59 self.fee_map().start_atomic();
60 self.reverse_fee_map().start_atomic();
61 self.transition_store().start_atomic();
62 }
63
64 fn is_atomic_in_progress(&self) -> bool {
66 self.fee_map().is_atomic_in_progress()
67 || self.reverse_fee_map().is_atomic_in_progress()
68 || self.transition_store().is_atomic_in_progress()
69 }
70
71 fn atomic_checkpoint(&self) {
73 self.fee_map().atomic_checkpoint();
74 self.reverse_fee_map().atomic_checkpoint();
75 self.transition_store().atomic_checkpoint();
76 }
77
78 fn clear_latest_checkpoint(&self) {
80 self.fee_map().clear_latest_checkpoint();
81 self.reverse_fee_map().clear_latest_checkpoint();
82 self.transition_store().clear_latest_checkpoint();
83 }
84
85 fn atomic_rewind(&self) {
87 self.fee_map().atomic_rewind();
88 self.reverse_fee_map().atomic_rewind();
89 self.transition_store().atomic_rewind();
90 }
91
92 fn abort_atomic(&self) {
94 self.fee_map().abort_atomic();
95 self.reverse_fee_map().abort_atomic();
96 self.transition_store().abort_atomic();
97 }
98
99 fn finish_atomic(&self) -> Result<()> {
101 self.fee_map().finish_atomic()?;
102 self.reverse_fee_map().finish_atomic()?;
103 self.transition_store().finish_atomic()
104 }
105
106 fn insert(&self, transaction_id: N::TransactionID, fee: &Fee<N>) -> Result<()> {
108 atomic_batch_scope!(self, {
109 self.fee_map()
111 .insert(transaction_id, (*fee.transition_id(), fee.global_state_root(), fee.proof().cloned()))?;
112 self.reverse_fee_map().insert(*fee.transition_id(), transaction_id)?;
113
114 self.transition_store().insert(fee)?;
116
117 Ok(())
118 })
119 }
120
121 fn remove(&self, transaction_id: &N::TransactionID) -> Result<()> {
123 let (transition_id, _, _) = match self.fee_map().get_confirmed(transaction_id)? {
125 Some(fee_id) => cow_to_cloned!(fee_id),
126 None => bail!("Failed to locate the fee transition ID for transaction '{transaction_id}'"),
127 };
128
129 atomic_batch_scope!(self, {
130 self.fee_map().remove(transaction_id)?;
132 self.reverse_fee_map().remove(&transition_id)?;
133
134 self.transition_store().remove(&transition_id)?;
136
137 Ok(())
138 })
139 }
140
141 fn find_transaction_id_from_transition_id(
143 &self,
144 transition_id: &N::TransitionID,
145 ) -> Result<Option<N::TransactionID>> {
146 match self.reverse_fee_map().get_confirmed(transition_id)? {
147 Some(transaction_id) => Ok(Some(cow_to_copied!(transaction_id))),
148 None => Ok(None),
149 }
150 }
151
152 fn get_fee(&self, transaction_id: &N::TransactionID) -> Result<Option<Fee<N>>> {
154 let (fee_transition_id, global_state_root, proof) = match self.fee_map().get_confirmed(transaction_id)? {
156 Some(fee) => cow_to_cloned!(fee),
157 None => return Ok(None),
158 };
159 match self.transition_store().get_transition(&fee_transition_id)? {
161 Some(transition) => Ok(Some(Fee::from_unchecked(transition, global_state_root, proof))),
162 None => bail!("Failed to locate the fee transition for transaction '{transaction_id}'"),
163 }
164 }
165}
166
167#[derive(Clone)]
169pub struct FeeStore<N: Network, F: FeeStorage<N>> {
170 storage: F,
172 _phantom: PhantomData<N>,
174}
175
176impl<N: Network, F: FeeStorage<N>> FeeStore<N, F> {
177 pub fn open(transition_store: TransitionStore<N, F::TransitionStorage>) -> Result<Self> {
179 let storage = F::open(transition_store)?;
181 Ok(Self { storage, _phantom: PhantomData })
183 }
184
185 pub fn from(storage: F) -> Self {
187 Self { storage, _phantom: PhantomData }
188 }
189
190 pub fn insert(&self, transaction_id: N::TransactionID, fee: &Fee<N>) -> Result<()> {
192 self.storage.insert(transaction_id, fee)
193 }
194
195 pub fn remove(&self, transaction_id: &N::TransactionID) -> Result<()> {
197 self.storage.remove(transaction_id)
198 }
199
200 pub fn transition_store(&self) -> &TransitionStore<N, F::TransitionStorage> {
202 self.storage.transition_store()
203 }
204
205 pub fn start_atomic(&self) {
207 self.storage.start_atomic();
208 }
209
210 pub fn is_atomic_in_progress(&self) -> bool {
212 self.storage.is_atomic_in_progress()
213 }
214
215 pub fn atomic_checkpoint(&self) {
217 self.storage.atomic_checkpoint();
218 }
219
220 pub fn clear_latest_checkpoint(&self) {
222 self.storage.clear_latest_checkpoint();
223 }
224
225 pub fn atomic_rewind(&self) {
227 self.storage.atomic_rewind();
228 }
229
230 pub fn abort_atomic(&self) {
232 self.storage.abort_atomic();
233 }
234
235 pub fn finish_atomic(&self) -> Result<()> {
237 self.storage.finish_atomic()
238 }
239
240 pub fn storage_mode(&self) -> &StorageMode {
242 self.storage.storage_mode()
243 }
244}
245
246impl<N: Network, F: FeeStorage<N>> FeeStore<N, F> {
247 pub fn get_fee(&self, transaction_id: &N::TransactionID) -> Result<Option<Fee<N>>> {
249 self.storage.get_fee(transaction_id)
250 }
251}
252
253impl<N: Network, F: FeeStorage<N>> FeeStore<N, F> {
254 pub fn find_transaction_id_from_transition_id(
256 &self,
257 transition_id: &N::TransitionID,
258 ) -> Result<Option<N::TransactionID>> {
259 self.storage.find_transaction_id_from_transition_id(transition_id)
260 }
261}
262
263#[cfg(test)]
264mod tests {
265 use super::*;
266 use crate::helpers::memory::FeeMemory;
267 use ledger_block::Transaction;
268
269 #[test]
270 fn test_insert_get_remove() {
271 let rng = &mut TestRng::default();
272
273 let transaction_0 = ledger_test_helpers::sample_fee_private_transaction(rng);
275 let transaction_1 = ledger_test_helpers::sample_fee_public_transaction(rng);
276 let transactions = vec![transaction_0, transaction_1];
277
278 for transaction in transactions {
279 let (transaction_id, fee) = match transaction {
280 Transaction::Fee(id, fee) => (id, fee),
281 _ => unreachable!("Invalid transaction type - expected a fee transaction"),
282 };
283
284 let transition_store = TransitionStore::open(StorageMode::Test(None)).unwrap();
286 let fee_store = FeeMemory::open(transition_store).unwrap();
288
289 let candidate = fee_store.get_fee(&transaction_id).unwrap();
291 assert_eq!(None, candidate);
292
293 fee_store.insert(transaction_id, &fee).unwrap();
295
296 let candidate = fee_store.get_fee(&transaction_id).unwrap();
298 assert_eq!(Some(fee), candidate);
299
300 fee_store.remove(&transaction_id).unwrap();
302
303 let candidate = fee_store.get_fee(&transaction_id).unwrap();
305 assert_eq!(None, candidate);
306 }
307 }
308
309 #[test]
310 fn test_find_transaction_id() {
311 let rng = &mut TestRng::default();
312
313 let transaction_0 = ledger_test_helpers::sample_fee_private_transaction(rng);
315 let transaction_1 = ledger_test_helpers::sample_fee_public_transaction(rng);
316 let transactions = vec![transaction_0, transaction_1];
317
318 for transaction in transactions {
319 let (transaction_id, fee) = match transaction {
320 Transaction::Fee(id, fee) => (id, fee),
321 _ => unreachable!("Invalid transaction type - expected a fee transaction"),
322 };
323 let fee_transition_id = fee.id();
324
325 let transition_store = TransitionStore::open(StorageMode::Test(None)).unwrap();
327 let fee_store = FeeMemory::open(transition_store).unwrap();
329
330 let candidate = fee_store.get_fee(&transaction_id).unwrap();
332 assert_eq!(None, candidate);
333
334 let candidate = fee_store.find_transaction_id_from_transition_id(fee_transition_id).unwrap();
336 assert_eq!(None, candidate);
337
338 fee_store.insert(transaction_id, &fee).unwrap();
340
341 let candidate = fee_store.find_transaction_id_from_transition_id(fee_transition_id).unwrap();
343 assert_eq!(Some(transaction_id), candidate);
344
345 fee_store.remove(&transaction_id).unwrap();
347
348 let candidate = fee_store.find_transaction_id_from_transition_id(fee_transition_id).unwrap();
350 assert_eq!(None, candidate);
351 }
352 }
353}