Skip to main content

snarkvm_ledger_store/transaction/
mod.rs

1// Copyright (c) 2019-2025 Provable Inc.
2// This file is part of the snarkVM library.
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7
8// http://www.apache.org/licenses/LICENSE-2.0
9
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16mod deployment;
17pub use deployment::*;
18
19mod execution;
20pub use execution::*;
21
22mod fee;
23pub use fee::*;
24
25use crate::{
26    TransitionStorage,
27    TransitionStore,
28    atomic_batch_scope,
29    helpers::{Map, MapRead},
30};
31use console::{
32    network::prelude::*,
33    program::{Identifier, ProgramID},
34};
35use snarkvm_ledger_block::{Deployment, Execution, Transaction};
36use snarkvm_synthesizer_program::Program;
37use snarkvm_synthesizer_snark::{Certificate, VerifyingKey};
38
39use aleo_std_storage::StorageMode;
40use anyhow::Result;
41use serde::{Deserialize, Serialize};
42use std::borrow::Cow;
43
44#[derive(Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
45pub enum TransactionType {
46    /// A transaction that is a deployment.
47    Deploy,
48    /// A transaction that is an execution.
49    Execute,
50    /// A transaction that is a fee.
51    Fee,
52}
53
54/// A trait for transaction storage.
55pub trait TransactionStorage<N: Network>: Clone + Send + Sync {
56    /// The mapping of `transaction ID` to `transaction type`.
57    type IDMap: for<'a> Map<'a, N::TransactionID, TransactionType>;
58    /// The deployment storage.
59    type DeploymentStorage: DeploymentStorage<N, FeeStorage = Self::FeeStorage>;
60    /// The execution storage.
61    type ExecutionStorage: ExecutionStorage<N, FeeStorage = Self::FeeStorage>;
62    /// The fee storage.
63    type FeeStorage: FeeStorage<N, TransitionStorage = Self::TransitionStorage>;
64    /// The transition storage.
65    type TransitionStorage: TransitionStorage<N>;
66
67    /// Initializes the transaction storage.
68    fn open(transition_store: TransitionStore<N, Self::TransitionStorage>) -> Result<Self>;
69
70    /// Returns the ID map.
71    fn id_map(&self) -> &Self::IDMap;
72    /// Returns the deployment store.
73    fn deployment_store(&self) -> &DeploymentStore<N, Self::DeploymentStorage>;
74    /// Returns the execution store.
75    fn execution_store(&self) -> &ExecutionStore<N, Self::ExecutionStorage>;
76    /// Returns the fee store.
77    fn fee_store(&self) -> &FeeStore<N, Self::FeeStorage>;
78    /// Returns the transition store.
79    fn transition_store(&self) -> &TransitionStore<N, Self::TransitionStorage> {
80        debug_assert!(self.deployment_store().storage_mode() == self.execution_store().storage_mode());
81        debug_assert!(self.execution_store().storage_mode() == self.fee_store().storage_mode());
82        self.fee_store().transition_store()
83    }
84
85    /// Returns the storage mode.
86    fn storage_mode(&self) -> &StorageMode {
87        self.transition_store().storage_mode()
88    }
89
90    /// Starts an atomic batch write operation.
91    fn start_atomic(&self) {
92        self.id_map().start_atomic();
93        self.deployment_store().start_atomic();
94        self.execution_store().start_atomic();
95        self.fee_store().start_atomic();
96    }
97
98    /// Checks if an atomic batch is in progress.
99    fn is_atomic_in_progress(&self) -> bool {
100        self.id_map().is_atomic_in_progress()
101            || self.deployment_store().is_atomic_in_progress()
102            || self.execution_store().is_atomic_in_progress()
103            || self.fee_store().is_atomic_in_progress()
104    }
105
106    /// Checkpoints the atomic batch.
107    fn atomic_checkpoint(&self) {
108        self.id_map().atomic_checkpoint();
109        self.deployment_store().atomic_checkpoint();
110        self.execution_store().atomic_checkpoint();
111        self.fee_store().atomic_checkpoint();
112    }
113
114    /// Clears the latest atomic batch checkpoint.
115    fn clear_latest_checkpoint(&self) {
116        self.id_map().clear_latest_checkpoint();
117        self.deployment_store().clear_latest_checkpoint();
118        self.execution_store().clear_latest_checkpoint();
119        self.fee_store().clear_latest_checkpoint();
120    }
121
122    /// Rewinds the atomic batch to the previous checkpoint.
123    fn atomic_rewind(&self) {
124        self.id_map().atomic_rewind();
125        self.deployment_store().atomic_rewind();
126        self.execution_store().atomic_rewind();
127        self.fee_store().atomic_rewind();
128    }
129
130    /// Aborts an atomic batch write operation.
131    fn abort_atomic(&self) {
132        self.id_map().abort_atomic();
133        self.deployment_store().abort_atomic();
134        self.execution_store().abort_atomic();
135        self.fee_store().abort_atomic();
136    }
137
138    /// Finishes an atomic batch write operation.
139    fn finish_atomic(&self) -> Result<()> {
140        self.id_map().finish_atomic()?;
141        self.deployment_store().finish_atomic()?;
142        self.execution_store().finish_atomic()?;
143        self.fee_store().finish_atomic()
144    }
145
146    /// Stores the given `transaction` into storage.
147    fn insert(&self, transaction: &Transaction<N>) -> Result<()> {
148        atomic_batch_scope!(self, {
149            match transaction {
150                Transaction::Deploy(..) => {
151                    // Store the transaction type.
152                    self.id_map().insert(transaction.id(), TransactionType::Deploy)?;
153                    // Store the deployment transaction.
154                    self.deployment_store().insert(transaction)?;
155                }
156                Transaction::Execute(..) => {
157                    // Store the transaction type.
158                    self.id_map().insert(transaction.id(), TransactionType::Execute)?;
159                    // Store the execution transaction.
160                    self.execution_store().insert(transaction)?;
161                }
162                Transaction::Fee(_, fee) => {
163                    // Store the transaction type.
164                    self.id_map().insert(transaction.id(), TransactionType::Fee)?;
165                    // Store the fee transaction.
166                    self.fee_store().insert(transaction.id(), fee)?;
167                }
168            }
169            Ok(())
170        })
171    }
172
173    /// Removes the transaction for the given `transaction ID`.
174    fn remove(&self, transaction_id: &N::TransactionID) -> Result<()> {
175        // Retrieve the transaction type.
176        let Some(transaction_type) = self.id_map().get_confirmed(transaction_id)?.map(|x| *x) else {
177            bail!("Failed to get the type for transaction '{transaction_id}'");
178        };
179
180        atomic_batch_scope!(self, {
181            // Remove the transaction type.
182            self.id_map().remove(transaction_id)?;
183            // Remove the transaction.
184            match transaction_type {
185                // Remove the deployment transaction.
186                TransactionType::Deploy => self.deployment_store().remove(transaction_id)?,
187                // Remove the execution transaction.
188                TransactionType::Execute => self.execution_store().remove(transaction_id)?,
189                // Remove the fee transaction.
190                TransactionType::Fee => self.fee_store().remove(transaction_id)?,
191            }
192            Ok(())
193        })
194    }
195
196    /// Returns the latest transaction ID that contains the given `program ID`.
197    fn find_latest_transaction_id_from_program_id(
198        &self,
199        program_id: &ProgramID<N>,
200    ) -> Result<Option<N::TransactionID>> {
201        self.deployment_store().find_latest_transaction_id_from_program_id(program_id)
202    }
203
204    /// Returns the transaction ID that contains the given `program ID` and `edition`.
205    fn find_transaction_id_from_program_id_and_edition(
206        &self,
207        program_id: &ProgramID<N>,
208        edition: u16,
209    ) -> Result<Option<N::TransactionID>> {
210        self.deployment_store().find_transaction_id_from_program_id_and_edition(program_id, edition)
211    }
212
213    /// Returns the transaction ID that contains the given `transition ID`.
214    fn find_transaction_id_from_transition_id(
215        &self,
216        transition_id: &N::TransitionID,
217    ) -> Result<Option<N::TransactionID>> {
218        self.execution_store().find_transaction_id_from_transition_id(transition_id)
219    }
220
221    /// Returns the transaction for the given `transaction ID`.
222    fn get_transaction(&self, transaction_id: &N::TransactionID) -> Result<Option<Transaction<N>>> {
223        // Retrieve the transaction type.
224        let Some(transaction_type) = self.id_map().get_confirmed(transaction_id)?.map(|x| *x) else {
225            return Ok(None);
226        };
227        // Retrieve the transaction.
228        match transaction_type {
229            // Return the deployment transaction.
230            TransactionType::Deploy => self.deployment_store().get_transaction(transaction_id),
231            // Return the execution transaction.
232            TransactionType::Execute => self.execution_store().get_transaction(transaction_id),
233            // Return the fee transaction.
234            TransactionType::Fee => match self.fee_store().get_fee(transaction_id)? {
235                Some(fee) => Ok(Some(Transaction::Fee(*transaction_id, fee))),
236                None => bail!("Failed to get fee for transaction '{transaction_id}'"),
237            },
238        }
239    }
240}
241
242/// The transaction store.
243#[derive(Clone)]
244pub struct TransactionStore<N: Network, T: TransactionStorage<N>> {
245    /// The map of `transaction ID` to `transaction type`.
246    transaction_ids: T::IDMap,
247    /// The transaction storage.
248    storage: T,
249}
250
251impl<N: Network, T: TransactionStorage<N>> TransactionStore<N, T> {
252    /// Initializes the transaction store.
253    pub fn open(transition_store: TransitionStore<N, T::TransitionStorage>) -> Result<Self> {
254        // Initialize the transaction storage.
255        let storage = T::open(transition_store)?;
256        // Return the transaction store.
257        Ok(Self { transaction_ids: storage.id_map().clone(), storage })
258    }
259
260    /// Initializes a transaction store from storage.
261    pub fn from(storage: T) -> Self {
262        Self { transaction_ids: storage.id_map().clone(), storage }
263    }
264
265    /// Stores the given `transaction` into storage.
266    pub fn insert(&self, transaction: &Transaction<N>) -> Result<()> {
267        self.storage.insert(transaction)
268    }
269
270    /// Removes the transaction for the given `transaction ID`.
271    pub fn remove(&self, transaction_id: &N::TransactionID) -> Result<()> {
272        self.storage.remove(transaction_id)
273    }
274
275    /// Returns the deployment store.
276    pub fn deployment_store(&self) -> &DeploymentStore<N, T::DeploymentStorage> {
277        self.storage.deployment_store()
278    }
279
280    /// Returns the transition store.
281    pub fn transition_store(&self) -> &TransitionStore<N, T::TransitionStorage> {
282        self.storage.transition_store()
283    }
284
285    /// Starts an atomic batch write operation.
286    pub fn start_atomic(&self) {
287        self.storage.start_atomic();
288    }
289
290    /// Checks if an atomic batch is in progress.
291    pub fn is_atomic_in_progress(&self) -> bool {
292        self.storage.is_atomic_in_progress()
293    }
294
295    /// Checkpoints the atomic batch.
296    pub fn atomic_checkpoint(&self) {
297        self.storage.atomic_checkpoint();
298    }
299
300    /// Clears the latest atomic batch checkpoint.
301    pub fn clear_latest_checkpoint(&self) {
302        self.storage.clear_latest_checkpoint();
303    }
304
305    /// Rewinds the atomic batch to the previous checkpoint.
306    pub fn atomic_rewind(&self) {
307        self.storage.atomic_rewind();
308    }
309
310    /// Aborts an atomic batch write operation.
311    pub fn abort_atomic(&self) {
312        self.storage.abort_atomic();
313    }
314
315    /// Finishes an atomic batch write operation.
316    pub fn finish_atomic(&self) -> Result<()> {
317        self.storage.finish_atomic()
318    }
319
320    /// Returns the storage mode.
321    pub fn storage_mode(&self) -> &StorageMode {
322        self.storage.storage_mode()
323    }
324}
325
326impl<N: Network, T: TransactionStorage<N>> TransactionStore<N, T> {
327    /// Returns the transaction for the given `transaction ID`.
328    pub fn get_transaction(&self, transaction_id: &N::TransactionID) -> Result<Option<Transaction<N>>> {
329        self.storage.get_transaction(transaction_id)
330    }
331
332    /// Returns the deployment for the given `transaction ID`.
333    pub fn get_deployment(&self, transaction_id: &N::TransactionID) -> Result<Option<Deployment<N>>> {
334        // Retrieve the transaction type.
335        let Some(transaction_type) = self.transaction_ids.get_confirmed(transaction_id)?.map(|x| *x) else {
336            bail!("Failed to get the type for transaction '{transaction_id}'");
337        };
338        // Retrieve the deployment.
339        match transaction_type {
340            // Return the deployment.
341            TransactionType::Deploy => self.storage.deployment_store().get_deployment(transaction_id),
342            // Throw an error.
343            TransactionType::Execute => bail!("Tried to get a deployment for execution transaction '{transaction_id}'"),
344            // Throw an error.
345            TransactionType::Fee => bail!("Tried to get a deployment for fee transaction '{transaction_id}'"),
346        }
347    }
348
349    /// Returns the execution for the given `transaction ID`.
350    pub fn get_execution(&self, transaction_id: &N::TransactionID) -> Result<Option<Execution<N>>> {
351        // Retrieve the transaction type.
352        let Some(transaction_type) = self.transaction_ids.get_confirmed(transaction_id)?.map(|x| *x) else {
353            bail!("Failed to get the type for transaction '{transaction_id}'");
354        };
355        // Retrieve the execution.
356        match transaction_type {
357            // Throw an error.
358            TransactionType::Deploy => bail!("Tried to get an execution for deployment transaction '{transaction_id}'"),
359            // Return the execution.
360            TransactionType::Execute => self.storage.execution_store().get_execution(transaction_id),
361            // Throw an error.
362            TransactionType::Fee => bail!("Tried to get an execution for fee transaction '{transaction_id}'"),
363        }
364    }
365
366    /// Returns the latest edition for the given `program ID`.
367    pub fn get_latest_edition_for_program(&self, program_id: &ProgramID<N>) -> Result<Option<u16>> {
368        self.storage.deployment_store().get_latest_edition_for_program(program_id)
369    }
370
371    /// Returns the edition for the given `transaction ID`.
372    pub fn get_edition(&self, transaction_id: &N::TransactionID) -> Result<Option<u16>> {
373        // Retrieve the transaction type.
374        let Some(transaction_type) = self.transaction_ids.get_confirmed(transaction_id)?.map(|x| *x) else {
375            bail!("Failed to get the type for transaction '{transaction_id}'");
376        };
377        // Retrieve the edition.
378        match transaction_type {
379            TransactionType::Deploy => self.storage.deployment_store().get_edition_for_transaction(transaction_id),
380            // Return 'None'.
381            TransactionType::Execute => Ok(None),
382            // Return 'None'.
383            TransactionType::Fee => Ok(None),
384        }
385    }
386
387    /// Returns the program ID for the given `transaction ID`.
388    pub fn get_program_id(&self, transaction_id: &N::TransactionID) -> Result<Option<ProgramID<N>>> {
389        self.storage.deployment_store().get_program_id(transaction_id)
390    }
391
392    /// Returns the latest program for the given `program ID`.
393    pub fn get_latest_program(&self, program_id: &ProgramID<N>) -> Result<Option<Program<N>>> {
394        self.storage.deployment_store().get_latest_program(program_id)
395    }
396
397    /// Returns the program for the given `program ID` and `edition`.
398    pub fn get_program_for_edition(&self, program_id: &ProgramID<N>, edition: u16) -> Result<Option<Program<N>>> {
399        self.storage.deployment_store().get_program_for_edition(program_id, edition)
400    }
401
402    /// Returns the latest verifying key for the given `(program ID, function name)`.
403    pub fn get_latest_verifying_key(
404        &self,
405        program_id: &ProgramID<N>,
406        function_name: &Identifier<N>,
407    ) -> Result<Option<VerifyingKey<N>>> {
408        self.storage.deployment_store().get_latest_verifying_key(program_id, function_name)
409    }
410
411    /// Returns the verifying key for the given `(program ID, function name, edition)`.
412    pub fn get_verifying_key_with_edition(
413        &self,
414        program_id: &ProgramID<N>,
415        function_name: &Identifier<N>,
416        edition: u16,
417    ) -> Result<Option<VerifyingKey<N>>> {
418        self.storage.deployment_store().get_verifying_key_with_edition(program_id, function_name, edition)
419    }
420
421    /// Returns the latest certificate for the given `(program ID, function name)`.
422    pub fn get_latest_certificate(
423        &self,
424        program_id: &ProgramID<N>,
425        function_name: &Identifier<N>,
426    ) -> Result<Option<Certificate<N>>> {
427        self.storage.deployment_store().get_latest_certificate(program_id, function_name)
428    }
429
430    /// Returns the certificate for the given `(program ID, function name, edition)`.
431    pub fn get_certificate_with_edition(
432        &self,
433        program_id: &ProgramID<N>,
434        function_name: &Identifier<N>,
435        edition: u16,
436    ) -> Result<Option<Certificate<N>>> {
437        self.storage.deployment_store().get_certificate_with_edition(program_id, function_name, edition)
438    }
439}
440
441impl<N: Network, T: TransactionStorage<N>> TransactionStore<N, T> {
442    /// Returns the latest transaction ID that contains the given `program ID`.
443    pub fn find_latest_transaction_id_from_program_id(
444        &self,
445        program_id: &ProgramID<N>,
446    ) -> Result<Option<N::TransactionID>> {
447        self.storage.deployment_store().find_latest_transaction_id_from_program_id(program_id)
448    }
449
450    /// Returns the transaction ID that contains the given `program ID` and `edition`.
451    pub fn find_transaction_id_from_program_id_and_edition(
452        &self,
453        program_id: &ProgramID<N>,
454        edition: u16,
455    ) -> Result<Option<N::TransactionID>> {
456        self.storage.deployment_store().find_transaction_id_from_program_id_and_edition(program_id, edition)
457    }
458
459    /// Returns the transaction ID that contains the given `transition ID`.
460    pub fn find_transaction_id_from_transition_id(
461        &self,
462        transition_id: &N::TransitionID,
463    ) -> Result<Option<N::TransactionID>> {
464        self.storage.find_transaction_id_from_transition_id(transition_id)
465    }
466}
467
468impl<N: Network, T: TransactionStorage<N>> TransactionStore<N, T> {
469    /// Returns `true` if the given transaction ID exists.
470    pub fn contains_transaction_id(&self, transaction_id: &N::TransactionID) -> Result<bool> {
471        self.transaction_ids.contains_key_confirmed(transaction_id)
472    }
473
474    /// Returns `true` if the given program ID exists.
475    pub fn contains_program_id(&self, program_id: &ProgramID<N>) -> Result<bool> {
476        self.storage.deployment_store().contains_program_id(program_id)
477    }
478
479    /// Returns `true` if the given program ID and edition exist.
480    pub fn contains_program_id_and_edition(&self, program_id: &ProgramID<N>, edition: u16) -> Result<bool> {
481        self.storage.deployment_store().contains_program_id_and_edition(program_id, edition)
482    }
483}
484
485type ProgramIDEdition<N> = (ProgramID<N>, u16);
486type ProgramTriplet<N> = (ProgramID<N>, Identifier<N>, u16);
487
488impl<N: Network, T: TransactionStorage<N>> TransactionStore<N, T> {
489    /// Returns an iterator over the transaction IDs, for all transactions.
490    pub fn transaction_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, N::TransactionID>> {
491        self.transaction_ids.keys_confirmed()
492    }
493
494    /// Returns an iterator over the deployment transaction IDs, for all deployments.
495    pub fn deployment_transaction_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, N::TransactionID>> {
496        self.storage.deployment_store().deployment_transaction_ids()
497    }
498
499    /// Returns an iterator over the execution transaction IDs, for all executions.
500    pub fn execution_transaction_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, N::TransactionID>> {
501        self.storage.execution_store().execution_transaction_ids()
502    }
503
504    /// Returns an iterator over the program IDs, for all deployments.
505    /// Note: If a program upgraded, this method will return duplicates of the program ID.
506    pub fn program_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, ProgramID<N>>> {
507        self.storage.deployment_store().program_ids()
508    }
509
510    /// Returns an iterator over the program IDs and latest editions.
511    pub fn program_ids_and_latest_editions(&self) -> impl '_ + Iterator<Item = (Cow<'_, ProgramID<N>>, Cow<'_, u16>)> {
512        self.storage.deployment_store().program_ids_and_latest_editions()
513    }
514
515    /// Returns an iterator over the programs, for all deployments.
516    /// If a program has been upgraded, all instances of the program will be returned.
517    pub fn programs(&self) -> impl '_ + Iterator<Item = Cow<'_, Program<N>>> {
518        self.storage.deployment_store().programs()
519    }
520
521    /// Returns an iterator over the programs and editions, for all deployments.
522    pub fn programs_with_editions(
523        &self,
524    ) -> impl '_ + Iterator<Item = (Cow<'_, ProgramIDEdition<N>>, Cow<'_, Program<N>>)> {
525        self.storage.deployment_store().programs_with_editions()
526    }
527
528    /// Returns an iterator over the `((program ID, function name, edition), verifying key)`, for all deployments.
529    pub fn verifying_keys(&self) -> impl '_ + Iterator<Item = (Cow<'_, ProgramTriplet<N>>, Cow<'_, VerifyingKey<N>>)> {
530        self.storage.deployment_store().verifying_keys()
531    }
532
533    /// Returns an iterator over the `((program ID, function name, edition), certificate)`, for all deployments.
534    pub fn certificates(&self) -> impl '_ + Iterator<Item = (Cow<'_, ProgramTriplet<N>>, Cow<'_, Certificate<N>>)> {
535        self.storage.deployment_store().certificates()
536    }
537}
538
539#[cfg(test)]
540mod tests {
541    use super::*;
542    use crate::helpers::memory::{TransactionMemory, TransitionMemory};
543
544    #[test]
545    fn test_insert_get_remove() {
546        let rng = &mut TestRng::default();
547
548        // Initialize a new transition store.
549        let transition_store = TransitionStore::<_, TransitionMemory<_>>::open(StorageMode::new_test(None)).unwrap();
550        // Initialize a new transaction store.
551        let transaction_store = TransactionStore::<_, TransactionMemory<_>>::open(transition_store).unwrap();
552
553        // Sample the transactions.
554        for transaction in [
555            snarkvm_ledger_test_helpers::sample_deployment_transaction(1, 0, true, rng),
556            snarkvm_ledger_test_helpers::sample_deployment_transaction(1, 1, false, rng),
557            snarkvm_ledger_test_helpers::sample_deployment_transaction(2, 0, true, rng),
558            snarkvm_ledger_test_helpers::sample_deployment_transaction(2, 1, false, rng),
559            snarkvm_ledger_test_helpers::sample_deployment_transaction(2, 2, true, rng),
560            snarkvm_ledger_test_helpers::sample_execution_transaction_with_fee(true, rng, 0),
561            snarkvm_ledger_test_helpers::sample_execution_transaction_with_fee(false, rng, 0),
562            snarkvm_ledger_test_helpers::sample_fee_private_transaction(rng),
563            snarkvm_ledger_test_helpers::sample_fee_public_transaction(rng),
564        ] {
565            let transaction_id = transaction.id();
566
567            // Ensure the transaction does not exist.
568            let candidate = transaction_store.get_transaction(&transaction_id).unwrap();
569            assert_eq!(None, candidate);
570
571            // Insert the transaction.
572            transaction_store.insert(&transaction).unwrap();
573
574            // Retrieve the transaction.
575            let candidate = transaction_store.get_transaction(&transaction_id).unwrap();
576            assert_eq!(Some(transaction.clone()), candidate);
577
578            // Remove the transaction.
579            transaction_store.remove(&transaction_id).unwrap();
580
581            // Ensure the transaction does not exist.
582            let candidate = transaction_store.get_transaction(&transaction_id).unwrap();
583            assert_eq!(None, candidate);
584
585            // Insert the transaction again.
586            transaction_store.insert(&transaction).unwrap();
587        }
588    }
589
590    #[test]
591    fn test_find_transaction_id() {
592        let rng = &mut TestRng::default();
593
594        // Initialize a new transition store.
595        let transition_store = TransitionStore::<_, TransitionMemory<_>>::open(StorageMode::new_test(None)).unwrap();
596        // Initialize a new transaction store.
597        let transaction_store = TransactionStore::<_, TransactionMemory<_>>::open(transition_store).unwrap();
598
599        // Sample the transactions.
600        for transaction in [
601            snarkvm_ledger_test_helpers::sample_deployment_transaction(1, 0, true, rng),
602            snarkvm_ledger_test_helpers::sample_deployment_transaction(1, 1, false, rng),
603            snarkvm_ledger_test_helpers::sample_deployment_transaction(2, 0, true, rng),
604            snarkvm_ledger_test_helpers::sample_deployment_transaction(2, 1, false, rng),
605            snarkvm_ledger_test_helpers::sample_deployment_transaction(2, 2, true, rng),
606            snarkvm_ledger_test_helpers::sample_execution_transaction_with_fee(true, rng, 0),
607            snarkvm_ledger_test_helpers::sample_execution_transaction_with_fee(true, rng, 1),
608            Transaction::from_fee(snarkvm_ledger_test_helpers::sample_fee_private(
609                snarkvm_console::types::Field::rand(rng),
610                rng,
611            ))
612            .unwrap(),
613            Transaction::from_fee(snarkvm_ledger_test_helpers::sample_fee_public(
614                snarkvm_console::types::Field::rand(rng),
615                rng,
616            ))
617            .unwrap(),
618        ] {
619            let transaction_id = transaction.id();
620            let transition_ids = transaction.transition_ids();
621
622            // Ensure the execution transaction does not exist.
623            let candidate = transaction_store.get_transaction(&transaction_id).unwrap();
624            assert_eq!(None, candidate);
625
626            for transition_id in transition_ids {
627                // Ensure the transition ID is not found.
628                let candidate = transaction_store.find_transaction_id_from_transition_id(transition_id).unwrap();
629                assert_eq!(None, candidate);
630
631                // Insert the transaction.
632                transaction_store.insert(&transaction).unwrap();
633
634                // Find the transaction ID.
635                let candidate = transaction_store.find_transaction_id_from_transition_id(transition_id).unwrap();
636                assert_eq!(Some(transaction_id), candidate);
637
638                // Remove the transaction.
639                transaction_store.remove(&transaction_id).unwrap();
640
641                // Ensure the transaction ID is not found.
642                let candidate = transaction_store.find_transaction_id_from_transition_id(transition_id).unwrap();
643                assert_eq!(None, candidate);
644            }
645
646            // Insert the transaction.
647            transaction_store.insert(&transaction).unwrap();
648
649            // If the transaction was a deployment, find it through the other getters.
650            if let Some(deployment) = transaction.deployment() {
651                // Get the program ID.
652                let program_id = deployment.program().id();
653                // Get the edition.
654                let edition = deployment.edition();
655                // Get and check the latest transaction ID for the program ID.
656                let candidate = transaction_store.find_latest_transaction_id_from_program_id(program_id).unwrap();
657                assert_eq!(Some(transaction_id), candidate);
658                // Get the check the transaction ID for the program ID and edition.
659                let candidate =
660                    transaction_store.find_transaction_id_from_program_id_and_edition(program_id, edition).unwrap();
661                assert_eq!(Some(transaction_id), candidate);
662            }
663        }
664    }
665}