1mod 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 Deploy,
48 Execute,
50 Fee,
52}
53
54pub trait TransactionStorage<N: Network>: Clone + Send + Sync {
56 type IDMap: for<'a> Map<'a, N::TransactionID, TransactionType>;
58 type DeploymentStorage: DeploymentStorage<N, FeeStorage = Self::FeeStorage>;
60 type ExecutionStorage: ExecutionStorage<N, FeeStorage = Self::FeeStorage>;
62 type FeeStorage: FeeStorage<N, TransitionStorage = Self::TransitionStorage>;
64 type TransitionStorage: TransitionStorage<N>;
66
67 fn open(transition_store: TransitionStore<N, Self::TransitionStorage>) -> Result<Self>;
69
70 fn id_map(&self) -> &Self::IDMap;
72 fn deployment_store(&self) -> &DeploymentStore<N, Self::DeploymentStorage>;
74 fn execution_store(&self) -> &ExecutionStore<N, Self::ExecutionStorage>;
76 fn fee_store(&self) -> &FeeStore<N, Self::FeeStorage>;
78 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 fn storage_mode(&self) -> &StorageMode {
87 self.transition_store().storage_mode()
88 }
89
90 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 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 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 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 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 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 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 fn insert(&self, transaction: &Transaction<N>) -> Result<()> {
148 atomic_batch_scope!(self, {
149 match transaction {
150 Transaction::Deploy(..) => {
151 self.id_map().insert(transaction.id(), TransactionType::Deploy)?;
153 self.deployment_store().insert(transaction)?;
155 }
156 Transaction::Execute(..) => {
157 self.id_map().insert(transaction.id(), TransactionType::Execute)?;
159 self.execution_store().insert(transaction)?;
161 }
162 Transaction::Fee(_, fee) => {
163 self.id_map().insert(transaction.id(), TransactionType::Fee)?;
165 self.fee_store().insert(transaction.id(), fee)?;
167 }
168 }
169 Ok(())
170 })
171 }
172
173 fn remove(&self, transaction_id: &N::TransactionID) -> Result<()> {
175 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 self.id_map().remove(transaction_id)?;
183 match transaction_type {
185 TransactionType::Deploy => self.deployment_store().remove(transaction_id)?,
187 TransactionType::Execute => self.execution_store().remove(transaction_id)?,
189 TransactionType::Fee => self.fee_store().remove(transaction_id)?,
191 }
192 Ok(())
193 })
194 }
195
196 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 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 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 fn get_transaction(&self, transaction_id: &N::TransactionID) -> Result<Option<Transaction<N>>> {
223 let Some(transaction_type) = self.id_map().get_confirmed(transaction_id)?.map(|x| *x) else {
225 return Ok(None);
226 };
227 match transaction_type {
229 TransactionType::Deploy => self.deployment_store().get_transaction(transaction_id),
231 TransactionType::Execute => self.execution_store().get_transaction(transaction_id),
233 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#[derive(Clone)]
244pub struct TransactionStore<N: Network, T: TransactionStorage<N>> {
245 transaction_ids: T::IDMap,
247 storage: T,
249}
250
251impl<N: Network, T: TransactionStorage<N>> TransactionStore<N, T> {
252 pub fn open(transition_store: TransitionStore<N, T::TransitionStorage>) -> Result<Self> {
254 let storage = T::open(transition_store)?;
256 Ok(Self { transaction_ids: storage.id_map().clone(), storage })
258 }
259
260 pub fn from(storage: T) -> Self {
262 Self { transaction_ids: storage.id_map().clone(), storage }
263 }
264
265 pub fn insert(&self, transaction: &Transaction<N>) -> Result<()> {
267 self.storage.insert(transaction)
268 }
269
270 pub fn remove(&self, transaction_id: &N::TransactionID) -> Result<()> {
272 self.storage.remove(transaction_id)
273 }
274
275 pub fn deployment_store(&self) -> &DeploymentStore<N, T::DeploymentStorage> {
277 self.storage.deployment_store()
278 }
279
280 pub fn transition_store(&self) -> &TransitionStore<N, T::TransitionStorage> {
282 self.storage.transition_store()
283 }
284
285 pub fn start_atomic(&self) {
287 self.storage.start_atomic();
288 }
289
290 pub fn is_atomic_in_progress(&self) -> bool {
292 self.storage.is_atomic_in_progress()
293 }
294
295 pub fn atomic_checkpoint(&self) {
297 self.storage.atomic_checkpoint();
298 }
299
300 pub fn clear_latest_checkpoint(&self) {
302 self.storage.clear_latest_checkpoint();
303 }
304
305 pub fn atomic_rewind(&self) {
307 self.storage.atomic_rewind();
308 }
309
310 pub fn abort_atomic(&self) {
312 self.storage.abort_atomic();
313 }
314
315 pub fn finish_atomic(&self) -> Result<()> {
317 self.storage.finish_atomic()
318 }
319
320 pub fn storage_mode(&self) -> &StorageMode {
322 self.storage.storage_mode()
323 }
324}
325
326impl<N: Network, T: TransactionStorage<N>> TransactionStore<N, T> {
327 pub fn get_transaction(&self, transaction_id: &N::TransactionID) -> Result<Option<Transaction<N>>> {
329 self.storage.get_transaction(transaction_id)
330 }
331
332 pub fn get_deployment(&self, transaction_id: &N::TransactionID) -> Result<Option<Deployment<N>>> {
334 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 match transaction_type {
340 TransactionType::Deploy => self.storage.deployment_store().get_deployment(transaction_id),
342 TransactionType::Execute => bail!("Tried to get a deployment for execution transaction '{transaction_id}'"),
344 TransactionType::Fee => bail!("Tried to get a deployment for fee transaction '{transaction_id}'"),
346 }
347 }
348
349 pub fn get_execution(&self, transaction_id: &N::TransactionID) -> Result<Option<Execution<N>>> {
351 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 match transaction_type {
357 TransactionType::Deploy => bail!("Tried to get an execution for deployment transaction '{transaction_id}'"),
359 TransactionType::Execute => self.storage.execution_store().get_execution(transaction_id),
361 TransactionType::Fee => bail!("Tried to get an execution for fee transaction '{transaction_id}'"),
363 }
364 }
365
366 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 pub fn get_edition(&self, transaction_id: &N::TransactionID) -> Result<Option<u16>> {
373 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 match transaction_type {
379 TransactionType::Deploy => self.storage.deployment_store().get_edition_for_transaction(transaction_id),
380 TransactionType::Execute => Ok(None),
382 TransactionType::Fee => Ok(None),
384 }
385 }
386
387 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 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 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 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 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 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 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 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 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 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 pub fn contains_transaction_id(&self, transaction_id: &N::TransactionID) -> Result<bool> {
471 self.transaction_ids.contains_key_confirmed(transaction_id)
472 }
473
474 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 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 pub fn transaction_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, N::TransactionID>> {
491 self.transaction_ids.keys_confirmed()
492 }
493
494 pub fn deployment_transaction_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, N::TransactionID>> {
496 self.storage.deployment_store().deployment_transaction_ids()
497 }
498
499 pub fn execution_transaction_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, N::TransactionID>> {
501 self.storage.execution_store().execution_transaction_ids()
502 }
503
504 pub fn program_ids(&self) -> impl '_ + Iterator<Item = Cow<'_, ProgramID<N>>> {
507 self.storage.deployment_store().program_ids()
508 }
509
510 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 pub fn programs(&self) -> impl '_ + Iterator<Item = Cow<'_, Program<N>>> {
518 self.storage.deployment_store().programs()
519 }
520
521 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 pub fn verifying_keys(&self) -> impl '_ + Iterator<Item = (Cow<'_, ProgramTriplet<N>>, Cow<'_, VerifyingKey<N>>)> {
530 self.storage.deployment_store().verifying_keys()
531 }
532
533 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 let transition_store = TransitionStore::<_, TransitionMemory<_>>::open(StorageMode::new_test(None)).unwrap();
550 let transaction_store = TransactionStore::<_, TransactionMemory<_>>::open(transition_store).unwrap();
552
553 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 let candidate = transaction_store.get_transaction(&transaction_id).unwrap();
569 assert_eq!(None, candidate);
570
571 transaction_store.insert(&transaction).unwrap();
573
574 let candidate = transaction_store.get_transaction(&transaction_id).unwrap();
576 assert_eq!(Some(transaction.clone()), candidate);
577
578 transaction_store.remove(&transaction_id).unwrap();
580
581 let candidate = transaction_store.get_transaction(&transaction_id).unwrap();
583 assert_eq!(None, candidate);
584
585 transaction_store.insert(&transaction).unwrap();
587 }
588 }
589
590 #[test]
591 fn test_find_transaction_id() {
592 let rng = &mut TestRng::default();
593
594 let transition_store = TransitionStore::<_, TransitionMemory<_>>::open(StorageMode::new_test(None)).unwrap();
596 let transaction_store = TransactionStore::<_, TransactionMemory<_>>::open(transition_store).unwrap();
598
599 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 let candidate = transaction_store.get_transaction(&transaction_id).unwrap();
624 assert_eq!(None, candidate);
625
626 for transition_id in transition_ids {
627 let candidate = transaction_store.find_transaction_id_from_transition_id(transition_id).unwrap();
629 assert_eq!(None, candidate);
630
631 transaction_store.insert(&transaction).unwrap();
633
634 let candidate = transaction_store.find_transaction_id_from_transition_id(transition_id).unwrap();
636 assert_eq!(Some(transaction_id), candidate);
637
638 transaction_store.remove(&transaction_id).unwrap();
640
641 let candidate = transaction_store.find_transaction_id_from_transition_id(transition_id).unwrap();
643 assert_eq!(None, candidate);
644 }
645
646 transaction_store.insert(&transaction).unwrap();
648
649 if let Some(deployment) = transaction.deployment() {
651 let program_id = deployment.program().id();
653 let edition = deployment.edition();
655 let candidate = transaction_store.find_latest_transaction_id_from_program_id(program_id).unwrap();
657 assert_eq!(Some(transaction_id), candidate);
658 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}