snarkvm_ledger_store/transaction/
execution.rs1use crate::{
17 FeeStorage,
18 FeeStore,
19 TransitionStore,
20 atomic_batch_scope,
21 cow_to_cloned,
22 cow_to_copied,
23 helpers::{Map, MapRead},
24};
25use console::network::prelude::*;
26use ledger_block::{Execution, Transaction, Transition};
27use synthesizer_snark::Proof;
28
29use aleo_std_storage::StorageMode;
30use anyhow::Result;
31use core::marker::PhantomData;
32use std::borrow::Cow;
33
34pub trait ExecutionStorage<N: Network>: Clone + Send + Sync {
36 type IDMap: for<'a> Map<'a, N::TransactionID, (Vec<N::TransitionID>, bool)>;
38 type ReverseIDMap: for<'a> Map<'a, N::TransitionID, N::TransactionID>;
40 type InclusionMap: for<'a> Map<'a, N::TransactionID, (N::StateRoot, Option<Proof<N>>)>;
42 type FeeStorage: FeeStorage<N>;
44
45 fn open(fee_store: FeeStore<N, Self::FeeStorage>) -> Result<Self>;
47
48 fn id_map(&self) -> &Self::IDMap;
50 fn reverse_id_map(&self) -> &Self::ReverseIDMap;
52 fn inclusion_map(&self) -> &Self::InclusionMap;
54 fn fee_store(&self) -> &FeeStore<N, Self::FeeStorage>;
56 fn transition_store(&self) -> &TransitionStore<N, <Self::FeeStorage as FeeStorage<N>>::TransitionStorage> {
58 self.fee_store().transition_store()
59 }
60
61 fn storage_mode(&self) -> &StorageMode {
63 self.transition_store().storage_mode()
64 }
65
66 fn start_atomic(&self) {
68 self.id_map().start_atomic();
69 self.reverse_id_map().start_atomic();
70 self.inclusion_map().start_atomic();
71 self.fee_store().start_atomic();
72 }
73
74 fn is_atomic_in_progress(&self) -> bool {
76 self.id_map().is_atomic_in_progress()
77 || self.reverse_id_map().is_atomic_in_progress()
78 || self.inclusion_map().is_atomic_in_progress()
79 || self.fee_store().is_atomic_in_progress()
80 }
81
82 fn atomic_checkpoint(&self) {
84 self.id_map().atomic_checkpoint();
85 self.reverse_id_map().atomic_checkpoint();
86 self.inclusion_map().atomic_checkpoint();
87 self.fee_store().atomic_checkpoint();
88 }
89
90 fn clear_latest_checkpoint(&self) {
92 self.id_map().clear_latest_checkpoint();
93 self.reverse_id_map().clear_latest_checkpoint();
94 self.inclusion_map().clear_latest_checkpoint();
95 self.fee_store().clear_latest_checkpoint();
96 }
97
98 fn atomic_rewind(&self) {
100 self.id_map().atomic_rewind();
101 self.reverse_id_map().atomic_rewind();
102 self.inclusion_map().atomic_rewind();
103 self.fee_store().atomic_rewind();
104 }
105
106 fn abort_atomic(&self) {
108 self.id_map().abort_atomic();
109 self.reverse_id_map().abort_atomic();
110 self.inclusion_map().abort_atomic();
111 self.fee_store().abort_atomic();
112 }
113
114 fn finish_atomic(&self) -> Result<()> {
116 self.id_map().finish_atomic()?;
117 self.reverse_id_map().finish_atomic()?;
118 self.inclusion_map().finish_atomic()?;
119 self.fee_store().finish_atomic()
120 }
121
122 fn insert(&self, transaction: &Transaction<N>) -> Result<()> {
124 let (transaction_id, execution, fee) = match transaction {
126 Transaction::Deploy(..) => bail!("Attempted to insert a deploy transaction into execution storage."),
127 Transaction::Execute(transaction_id, _, execution, fee) => (transaction_id, execution, fee),
128 Transaction::Fee(..) => bail!("Attempted to insert a fee transaction into execution storage."),
129 };
130
131 let transitions = execution.transitions();
133 let transition_ids = execution.transitions().map(Transition::id).copied().collect();
135 let global_state_root = execution.global_state_root();
137 let proof = execution.proof().cloned();
139
140 atomic_batch_scope!(self, {
141 self.id_map().insert(*transaction_id, (transition_ids, fee.is_some()))?;
143
144 for transition in transitions {
146 self.reverse_id_map().insert(*transition.id(), *transaction_id)?;
148 self.transition_store().insert(transition)?;
150 }
151
152 self.inclusion_map().insert(*transaction_id, (global_state_root, proof))?;
154
155 if let Some(fee) = fee {
157 self.fee_store().insert(*transaction_id, fee)?;
159 }
160
161 Ok(())
162 })
163 }
164
165 fn remove(&self, transaction_id: &N::TransactionID) -> Result<()> {
167 let (transition_ids, has_fee) = match self.id_map().get_confirmed(transaction_id)? {
169 Some(ids) => cow_to_cloned!(ids),
170 None => bail!("Failed to get the transition IDs for the transaction '{transaction_id}'"),
171 };
172
173 atomic_batch_scope!(self, {
174 self.id_map().remove(transaction_id)?;
176
177 for transition_id in transition_ids {
179 self.reverse_id_map().remove(&transition_id)?;
181 self.transition_store().remove(&transition_id)?;
183 }
184
185 self.inclusion_map().remove(transaction_id)?;
187
188 if has_fee {
190 self.fee_store().remove(transaction_id)?;
192 }
193
194 Ok(())
195 })
196 }
197
198 fn find_transaction_id_from_transition_id(
200 &self,
201 transition_id: &N::TransitionID,
202 ) -> Result<Option<N::TransactionID>> {
203 if let Some(transaction_id) = self.fee_store().find_transaction_id_from_transition_id(transition_id)? {
205 return Ok(Some(transaction_id));
206 }
207 match self.reverse_id_map().get_confirmed(transition_id)? {
209 Some(transaction_id) => Ok(Some(cow_to_copied!(transaction_id))),
210 None => Ok(None),
211 }
212 }
213
214 fn get_execution(&self, transaction_id: &N::TransactionID) -> Result<Option<Execution<N>>> {
216 let (transition_ids, _) = match self.id_map().get_confirmed(transaction_id)? {
218 Some(ids) => cow_to_cloned!(ids),
219 None => return Ok(None),
220 };
221
222 let (global_state_root, proof) = match self.inclusion_map().get_confirmed(transaction_id)? {
224 Some(inclusion) => cow_to_cloned!(inclusion),
225 None => bail!("Failed to get the proof for the transaction '{transaction_id}'"),
226 };
227
228 let mut transitions = Vec::new();
230
231 for transition_id in &transition_ids {
233 match self.transition_store().get_transition(transition_id)? {
234 Some(transition) => transitions.push(transition),
235 None => bail!("Failed to get transition '{transition_id}' for transaction '{transaction_id}'"),
236 };
237 }
238
239 Ok(Some(Execution::from(transitions.into_iter(), global_state_root, proof)?))
241 }
242
243 fn get_transaction(&self, transaction_id: &N::TransactionID) -> Result<Option<Transaction<N>>> {
245 let (transition_ids, has_fee) = match self.id_map().get_confirmed(transaction_id)? {
247 Some(ids) => cow_to_cloned!(ids),
248 None => return Ok(None),
249 };
250
251 let (global_state_root, proof) = match self.inclusion_map().get_confirmed(transaction_id)? {
253 Some(inclusion) => cow_to_cloned!(inclusion),
254 None => bail!("Failed to get the proof for the transaction '{transaction_id}'"),
255 };
256
257 let mut transitions = Vec::new();
259
260 for transition_id in &transition_ids {
262 match self.transition_store().get_transition(transition_id)? {
263 Some(transition) => transitions.push(transition),
264 None => bail!("Failed to get transition '{transition_id}' for transaction '{transaction_id}'"),
265 };
266 }
267
268 let execution = Execution::from(transitions.into_iter(), global_state_root, proof)?;
270
271 let transaction = match has_fee {
273 true => match self.fee_store().get_fee(transaction_id)? {
275 Some(fee) => Transaction::from_execution(execution, Some(fee))?,
277 None => bail!("Failed to get the fee for transaction '{transaction_id}'"),
278 },
279 false => Transaction::from_execution(execution, None)?,
280 };
281
282 match *transaction_id == transaction.id() {
284 true => Ok(Some(transaction)),
285 false => bail!("Mismatching transaction ID for transaction '{transaction_id}'"),
286 }
287 }
288}
289
290#[derive(Clone)]
292pub struct ExecutionStore<N: Network, E: ExecutionStorage<N>> {
293 storage: E,
295 _phantom: PhantomData<N>,
297}
298
299impl<N: Network, E: ExecutionStorage<N>> ExecutionStore<N, E> {
300 pub fn open(fee_store: FeeStore<N, E::FeeStorage>) -> Result<Self> {
302 let storage = E::open(fee_store)?;
304 Ok(Self { storage, _phantom: PhantomData })
306 }
307
308 pub fn from(storage: E) -> Self {
310 Self { storage, _phantom: PhantomData }
311 }
312
313 pub fn insert(&self, transaction: &Transaction<N>) -> Result<()> {
315 self.storage.insert(transaction)
316 }
317
318 pub fn remove(&self, transaction_id: &N::TransactionID) -> Result<()> {
320 self.storage.remove(transaction_id)
321 }
322
323 pub fn start_atomic(&self) {
325 self.storage.start_atomic();
326 }
327
328 pub fn is_atomic_in_progress(&self) -> bool {
330 self.storage.is_atomic_in_progress()
331 }
332
333 pub fn atomic_checkpoint(&self) {
335 self.storage.atomic_checkpoint();
336 }
337
338 pub fn clear_latest_checkpoint(&self) {
340 self.storage.clear_latest_checkpoint();
341 }
342
343 pub fn atomic_rewind(&self) {
345 self.storage.atomic_rewind();
346 }
347
348 pub fn abort_atomic(&self) {
350 self.storage.abort_atomic();
351 }
352
353 pub fn finish_atomic(&self) -> Result<()> {
355 self.storage.finish_atomic()
356 }
357
358 pub fn storage_mode(&self) -> &StorageMode {
360 self.storage.storage_mode()
361 }
362}
363
364impl<N: Network, E: ExecutionStorage<N>> ExecutionStore<N, E> {
365 pub fn get_transaction(&self, transaction_id: &N::TransactionID) -> Result<Option<Transaction<N>>> {
367 self.storage.get_transaction(transaction_id)
368 }
369
370 pub fn get_execution(&self, transaction_id: &N::TransactionID) -> Result<Option<Execution<N>>> {
372 self.storage.get_execution(transaction_id)
373 }
374}
375
376impl<N: Network, E: ExecutionStorage<N>> ExecutionStore<N, E> {
377 pub fn find_transaction_id_from_transition_id(
379 &self,
380 transition_id: &N::TransitionID,
381 ) -> Result<Option<N::TransactionID>> {
382 self.storage.find_transaction_id_from_transition_id(transition_id)
383 }
384}
385
386impl<N: Network, E: ExecutionStorage<N>> ExecutionStore<N, E> {
387 pub fn execution_transaction_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, N::TransactionID>> {
389 self.storage.id_map().keys_confirmed()
390 }
391}
392
393#[cfg(test)]
394mod tests {
395 use super::*;
396 use crate::{TransitionStore, helpers::memory::ExecutionMemory};
397
398 type CurrentNetwork = console::network::MainnetV0;
399
400 fn insert_get_remove(transaction: Transaction<CurrentNetwork>) -> Result<()> {
401 let transaction_id = transaction.id();
402
403 let transition_store = TransitionStore::open(StorageMode::Test(None))?;
405 let fee_store = FeeStore::open(transition_store).unwrap();
407 let execution_store = ExecutionMemory::open(fee_store)?;
409
410 let candidate = execution_store.get_transaction(&transaction_id)?;
412 assert_eq!(None, candidate);
413
414 execution_store.insert(&transaction)?;
416
417 let candidate = execution_store.get_transaction(&transaction_id)?;
419 assert_eq!(Some(transaction), candidate);
420
421 execution_store.remove(&transaction_id)?;
423
424 let candidate = execution_store.get_transaction(&transaction_id)?;
426 assert_eq!(None, candidate);
427
428 Ok(())
429 }
430
431 fn find_transaction_id(transaction: Transaction<CurrentNetwork>) -> Result<()> {
432 let transaction_id = transaction.id();
433
434 if matches!(transaction, Transaction::Deploy(..)) {
436 bail!("Invalid transaction type");
437 }
438
439 let transition_store = TransitionStore::open(StorageMode::Test(None))?;
441 let fee_store = FeeStore::open(transition_store).unwrap();
443 let execution_store = ExecutionMemory::open(fee_store)?;
445
446 let candidate = execution_store.get_transaction(&transaction_id)?;
448 assert_eq!(None, candidate);
449
450 for transition_id in transaction.transition_ids() {
451 let candidate = execution_store.find_transaction_id_from_transition_id(transition_id).unwrap();
453 assert_eq!(None, candidate);
454
455 execution_store.insert(&transaction)?;
457
458 let candidate = execution_store.find_transaction_id_from_transition_id(transition_id).unwrap();
460 assert_eq!(Some(transaction_id), candidate);
461
462 execution_store.remove(&transaction_id)?;
464
465 let candidate = execution_store.find_transaction_id_from_transition_id(transition_id).unwrap();
467 assert_eq!(None, candidate);
468 }
469
470 Ok(())
471 }
472
473 #[test]
474 fn test_insert_get_remove() {
475 let rng = &mut TestRng::default();
476
477 let transaction = ledger_test_helpers::sample_execution_transaction_with_fee(true, rng);
479 insert_get_remove(transaction).unwrap();
480
481 let transaction = ledger_test_helpers::sample_execution_transaction_with_fee(false, rng);
483 insert_get_remove(transaction).unwrap();
484 }
485
486 #[test]
487 fn test_find_transaction_id() {
488 let rng = &mut TestRng::default();
489
490 let transaction = ledger_test_helpers::sample_execution_transaction_with_fee(true, rng);
492 find_transaction_id(transaction).unwrap();
493
494 let transaction = ledger_test_helpers::sample_execution_transaction_with_fee(false, rng);
496 find_transaction_id(transaction).unwrap();
497 }
498}