snarkvm_ledger_block/transactions/confirmed/
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 bytes;
17mod serialize;
18mod string;
19
20use crate::{Transaction, rejected::Rejected};
21use console::{network::prelude::*, program::FINALIZE_ID_DEPTH, types::Field};
22use synthesizer_program::FinalizeOperation;
23
24pub type NumFinalizeSize = u16;
25
26/// The confirmed transaction.
27#[derive(Clone, PartialEq, Eq)]
28pub enum ConfirmedTransaction<N: Network> {
29    /// The accepted deploy transaction is composed of `(index, deploy_transaction, finalize_operations)`.
30    AcceptedDeploy(u32, Transaction<N>, Vec<FinalizeOperation<N>>),
31    /// The accepted execute transaction is composed of `(index, execute_transaction, finalize_operations)`.
32    AcceptedExecute(u32, Transaction<N>, Vec<FinalizeOperation<N>>),
33    /// The rejected deploy transaction is composed of `(index, fee_transaction, rejected_deployment, finalize_operations)`.
34    RejectedDeploy(u32, Transaction<N>, Rejected<N>, Vec<FinalizeOperation<N>>),
35    /// The rejected execute transaction is composed of `(index, fee_transaction, rejected_execution, finalize_operations)`.
36    RejectedExecute(u32, Transaction<N>, Rejected<N>, Vec<FinalizeOperation<N>>),
37}
38
39impl<N: Network> ConfirmedTransaction<N> {
40    /// Returns a new instance of an accepted deploy transaction.
41    pub fn accepted_deploy(
42        index: u32,
43        transaction: Transaction<N>,
44        finalize_operations: Vec<FinalizeOperation<N>>,
45    ) -> Result<Self> {
46        // Retrieve the program and fee from the deployment transaction, and ensure the transaction is a deploy transaction.
47        let (program, fee) = match &transaction {
48            Transaction::Deploy(_, _, _, deployment, fee) => (deployment.program(), fee),
49            Transaction::Execute(..) | Transaction::Fee(..) => {
50                bail!("Transaction '{}' is not a deploy transaction", transaction.id())
51            }
52        };
53
54        // Count the number of `InitializeMapping` and `UpdateKeyValue` finalize operations.
55        let (num_initialize_mappings, num_update_key_values) =
56            finalize_operations.iter().try_fold((0, 0), |(init, update), operation| match operation {
57                FinalizeOperation::InitializeMapping(..) => Ok((init + 1, update)),
58                FinalizeOperation::UpdateKeyValue(..) => Ok((init, update + 1)),
59                op => {
60                    bail!("Transaction '{}' (deploy) contains an invalid finalize operation ({op})", transaction.id())
61                }
62            })?;
63
64        // Perform safety checks on the finalize operations.
65        {
66            // Ensure the number of finalize operations matches the number of 'InitializeMapping' and 'UpdateKeyValue' finalize operations.
67            if num_initialize_mappings + num_update_key_values != finalize_operations.len() {
68                bail!(
69                    "Transaction '{}' (deploy) must contain '{}' operations",
70                    transaction.id(),
71                    finalize_operations.len()
72                );
73            }
74            // Ensure the number of program mappings matches the number of 'InitializeMapping' finalize operations.
75            if num_initialize_mappings != program.mappings().len() {
76                bail!(
77                    "Transaction '{}' (deploy) must contain '{}' 'InitializeMapping' operations (found '{num_initialize_mappings}')",
78                    transaction.id(),
79                    program.mappings().len(),
80                )
81            }
82            // Ensure the number of finalize operations matches the number of 'UpdateKeyValue' finalize operations.
83            if num_update_key_values != fee.num_finalize_operations() {
84                bail!(
85                    "Transaction '{}' (deploy) must contain {} 'UpdateKeyValue' operations (found '{num_update_key_values}')",
86                    transaction.id(),
87                    fee.num_finalize_operations()
88                );
89            }
90        }
91
92        // Return the accepted deploy transaction.
93        Ok(Self::AcceptedDeploy(index, transaction, finalize_operations))
94    }
95
96    /// Returns a new instance of an accepted execute transaction.
97    pub fn accepted_execute(
98        index: u32,
99        transaction: Transaction<N>,
100        finalize_operations: Vec<FinalizeOperation<N>>,
101    ) -> Result<Self> {
102        // Ensure the finalize operations contain the correct types.
103        for operation in finalize_operations.iter() {
104            // Ensure the finalize operation is an insert or update key-value operation.
105            match operation {
106                FinalizeOperation::InsertKeyValue(..)
107                | FinalizeOperation::UpdateKeyValue(..)
108                | FinalizeOperation::RemoveKeyValue(..) => (),
109                FinalizeOperation::InitializeMapping(..)
110                | FinalizeOperation::ReplaceMapping(..)
111                | FinalizeOperation::RemoveMapping(..) => {
112                    bail!("Transaction '{}' (execute) contains an invalid finalize operation type", transaction.id())
113                }
114            }
115        }
116        // Ensure the transaction is an execute transaction.
117        match transaction.is_execute() {
118            true => Ok(Self::AcceptedExecute(index, transaction, finalize_operations)),
119            false => bail!("Transaction '{}' is not an execute transaction", transaction.id()),
120        }
121    }
122
123    /// Returns a new instance of a rejected deploy transaction.
124    pub fn rejected_deploy(
125        index: u32,
126        transaction: Transaction<N>,
127        rejected: Rejected<N>,
128        finalize_operations: Vec<FinalizeOperation<N>>,
129    ) -> Result<Self> {
130        // Ensure the rejected object is a deployment.
131        ensure!(rejected.is_deployment(), "Rejected deployment is not a deployment");
132        // Ensure the finalize operations contain the correct types.
133        for operation in finalize_operations.iter() {
134            // Ensure the finalize operation is an insert or update key-value operation.
135            match operation {
136                FinalizeOperation::InsertKeyValue(..)
137                | FinalizeOperation::UpdateKeyValue(..)
138                | FinalizeOperation::RemoveKeyValue(..) => (),
139                FinalizeOperation::InitializeMapping(..)
140                | FinalizeOperation::ReplaceMapping(..)
141                | FinalizeOperation::RemoveMapping(..) => {
142                    bail!("Transaction '{}' (fee) contains an invalid finalize operation type", transaction.id())
143                }
144            }
145        }
146        // Ensure the transaction is a fee transaction.
147        match transaction.is_fee() {
148            true => Ok(Self::RejectedDeploy(index, transaction, rejected, finalize_operations)),
149            false => bail!("Transaction '{}' is not a fee transaction", transaction.id()),
150        }
151    }
152
153    /// Returns a new instance of a rejected execute transaction.
154    ///
155    /// Arguments:
156    /// - `index`: The index of the tranaction within the block.
157    /// - `transaction`: The associated fee transaction.
158    /// - `rejected`: The rejected execute transaction.
159    /// - `finalize_operations`: The finalize operations for the fee transaction.
160    pub fn rejected_execute(
161        index: u32,
162        transaction: Transaction<N>,
163        rejected: Rejected<N>,
164        finalize_operations: Vec<FinalizeOperation<N>>,
165    ) -> Result<Self> {
166        // Ensure the rejected object is an execution.
167        ensure!(rejected.is_execution(), "Rejected execution is not an execution");
168        // Ensure the finalize operations contain the correct types.
169        for operation in finalize_operations.iter() {
170            // Ensure the finalize operation is an insert or update key-value operation.
171            match operation {
172                FinalizeOperation::InsertKeyValue(..)
173                | FinalizeOperation::UpdateKeyValue(..)
174                | FinalizeOperation::RemoveKeyValue(..) => (),
175                FinalizeOperation::InitializeMapping(..)
176                | FinalizeOperation::ReplaceMapping(..)
177                | FinalizeOperation::RemoveMapping(..) => {
178                    bail!("Transaction '{}' (fee) contains an invalid finalize operation type", transaction.id())
179                }
180            }
181        }
182        // Ensure the transaction is a fee transaction.
183        match transaction.is_fee() {
184            true => Ok(Self::RejectedExecute(index, transaction, rejected, finalize_operations)),
185            false => bail!("Transaction '{}' is not a fee transaction", transaction.id()),
186        }
187    }
188}
189
190impl<N: Network> ConfirmedTransaction<N> {
191    /// Returns 'true' if the confirmed transaction is accepted.
192    pub const fn is_accepted(&self) -> bool {
193        match self {
194            Self::AcceptedDeploy(..) | Self::AcceptedExecute(..) => true,
195            Self::RejectedDeploy(..) | Self::RejectedExecute(..) => false,
196        }
197    }
198
199    /// Returns 'true' if the confirmed transaction is rejected.
200    pub const fn is_rejected(&self) -> bool {
201        !self.is_accepted()
202    }
203
204    /// Returns `true` if the confirmed transaction represents the given unconfirmed transaction ID.
205    pub fn contains_unconfirmed_transaction_id(&self, unconfirmed_transaction_id: &N::TransactionID) -> bool {
206        self.to_unconfirmed_transaction_id().map_or(false, |id| &id == unconfirmed_transaction_id)
207    }
208}
209
210impl<N: Network> ConfirmedTransaction<N> {
211    /// Returns the confirmed transaction index.
212    pub const fn index(&self) -> u32 {
213        match self {
214            Self::AcceptedDeploy(index, ..) => *index,
215            Self::AcceptedExecute(index, ..) => *index,
216            Self::RejectedDeploy(index, ..) => *index,
217            Self::RejectedExecute(index, ..) => *index,
218        }
219    }
220
221    /// Returns the human-readable variant of the confirmed transaction.
222    pub const fn variant(&self) -> &str {
223        match self {
224            Self::AcceptedDeploy(..) => "accepted deploy",
225            Self::AcceptedExecute(..) => "accepted execute",
226            Self::RejectedDeploy(..) => "rejected deploy",
227            Self::RejectedExecute(..) => "rejected execute",
228        }
229    }
230
231    /// Returns the underlying transaction.
232    ///
233    /// For an accepted transaction, it is the original/unconfirmed transaction issued by the client.
234    /// For a rejected transaction, it is the fee transaction, not the original transaction.
235    pub const fn transaction(&self) -> &Transaction<N> {
236        match self {
237            Self::AcceptedDeploy(_, transaction, _) => transaction,
238            Self::AcceptedExecute(_, transaction, _) => transaction,
239            Self::RejectedDeploy(_, transaction, _, _) => transaction,
240            Self::RejectedExecute(_, transaction, _, _) => transaction,
241        }
242    }
243
244    /// Returns the transaction.
245    pub fn into_transaction(self) -> Transaction<N> {
246        match self {
247            Self::AcceptedDeploy(_, transaction, _) => transaction,
248            Self::AcceptedExecute(_, transaction, _) => transaction,
249            Self::RejectedDeploy(_, transaction, _, _) => transaction,
250            Self::RejectedExecute(_, transaction, _, _) => transaction,
251        }
252    }
253
254    /// Returns the number of finalize operations.
255    pub fn num_finalize(&self) -> usize {
256        match self {
257            Self::AcceptedDeploy(_, _, finalize) => finalize.len(),
258            Self::AcceptedExecute(_, _, finalize) => finalize.len(),
259            Self::RejectedDeploy(_, _, _, finalize) => finalize.len(),
260            Self::RejectedExecute(_, _, _, finalize) => finalize.len(),
261        }
262    }
263
264    /// Returns the finalize operations for the confirmed transaction.
265    pub const fn finalize_operations(&self) -> &Vec<FinalizeOperation<N>> {
266        match self {
267            Self::AcceptedDeploy(_, _, finalize) => finalize,
268            Self::AcceptedExecute(_, _, finalize) => finalize,
269            Self::RejectedDeploy(_, _, _, finalize) => finalize,
270            Self::RejectedExecute(_, _, _, finalize) => finalize,
271        }
272    }
273
274    /// Returns the finalize ID, by computing the root of a (small) Merkle tree comprised of
275    /// the ordered finalize operations for the transaction.
276    pub fn to_finalize_id(&self) -> Result<Field<N>> {
277        // Prepare the leaves.
278        let leaves = self.finalize_operations().iter().map(ToBits::to_bits_le).collect::<Vec<_>>();
279        // Compute the finalize ID.
280        // Note: This call will ensure the number of finalize operations is within the size of the Merkle tree.
281        Ok(*N::merkle_tree_bhp::<FINALIZE_ID_DEPTH>(&leaves)?.root())
282    }
283
284    /// Returns the rejected ID, if the confirmed transaction is rejected.
285    pub fn to_rejected_id(&self) -> Result<Option<Field<N>>> {
286        match self {
287            ConfirmedTransaction::AcceptedDeploy(..) | ConfirmedTransaction::AcceptedExecute(..) => Ok(None),
288            ConfirmedTransaction::RejectedDeploy(_, _, rejected, _) => Ok(Some(rejected.to_id()?)),
289            ConfirmedTransaction::RejectedExecute(_, _, rejected, _) => Ok(Some(rejected.to_id()?)),
290        }
291    }
292
293    /// Returns the rejected object, if the confirmed transaction is rejected.
294    pub fn to_rejected(&self) -> Option<&Rejected<N>> {
295        match self {
296            ConfirmedTransaction::AcceptedDeploy(..) | ConfirmedTransaction::AcceptedExecute(..) => None,
297            ConfirmedTransaction::RejectedDeploy(_, _, rejected, _) => Some(rejected),
298            ConfirmedTransaction::RejectedExecute(_, _, rejected, _) => Some(rejected),
299        }
300    }
301
302    /// Returns the unconfirmed transaction ID, which is defined as the transaction ID prior to confirmation.
303    /// When a transaction is rejected, its fee transition is used to construct the confirmed transaction ID,
304    /// changing the original transaction ID.
305    pub fn to_unconfirmed_transaction_id(&self) -> Result<N::TransactionID> {
306        match self {
307            Self::AcceptedDeploy(_, transaction, _) => Ok(transaction.id()),
308            Self::AcceptedExecute(_, transaction, _) => Ok(transaction.id()),
309            Self::RejectedDeploy(_, fee_transaction, rejected, _)
310            | Self::RejectedExecute(_, fee_transaction, rejected, _) => {
311                Ok(rejected.to_unconfirmed_id(&fee_transaction.fee_transition())?.into())
312            }
313        }
314    }
315
316    /// Returns the unconfirmed transaction, which is defined as the transaction prior to confirmation.
317    /// When a transaction is rejected, its fee transition is used to construct the confirmed transaction,
318    /// changing the original transaction.
319    pub fn to_unconfirmed_transaction(&self) -> Result<Transaction<N>> {
320        match self {
321            Self::AcceptedDeploy(_, transaction, _) => Ok(transaction.clone()),
322            Self::AcceptedExecute(_, transaction, _) => Ok(transaction.clone()),
323            Self::RejectedDeploy(_, fee_transaction, rejected, _) => Transaction::from_deployment(
324                rejected
325                    .program_owner()
326                    .copied()
327                    .ok_or_else(|| anyhow!("Missing program owner for rejected transaction"))?,
328                rejected.deployment().cloned().ok_or_else(|| anyhow!("Missing deployment for rejected transaction"))?,
329                fee_transaction.fee_transition().ok_or_else(|| anyhow!("Missing fee for rejected deployment"))?,
330            ),
331            Self::RejectedExecute(_, fee_transaction, rejected, _) => Transaction::from_execution(
332                rejected.execution().cloned().ok_or_else(|| anyhow!("Missing execution for rejected transaction"))?,
333                fee_transaction.fee_transition(),
334            ),
335        }
336    }
337}
338
339impl<N: Network> Deref for ConfirmedTransaction<N> {
340    type Target = Transaction<N>;
341
342    /// Returns a reference to the valid transaction.
343    fn deref(&self) -> &Self::Target {
344        self.transaction()
345    }
346}
347
348#[cfg(test)]
349pub mod test_helpers {
350    use super::*;
351    use console::network::MainnetV0;
352
353    type CurrentNetwork = MainnetV0;
354
355    /// Samples an accepted deploy transaction at the given index.
356    pub(crate) fn sample_accepted_deploy(
357        index: u32,
358        is_fee_private: bool,
359        rng: &mut TestRng,
360    ) -> ConfirmedTransaction<CurrentNetwork> {
361        // Sample a deploy transaction.
362        let tx = crate::transaction::test_helpers::sample_deployment_transaction(is_fee_private, rng);
363
364        // Construct the finalize operations based on if the fee is public or private.
365        let finalize_operations = match is_fee_private {
366            true => vec![FinalizeOperation::InitializeMapping(Uniform::rand(rng))],
367            false => vec![
368                FinalizeOperation::InitializeMapping(Uniform::rand(rng)),
369                FinalizeOperation::UpdateKeyValue(Uniform::rand(rng), Uniform::rand(rng), Uniform::rand(rng)),
370            ],
371        };
372
373        // Return the confirmed transaction.
374        ConfirmedTransaction::accepted_deploy(index, tx, finalize_operations).unwrap()
375    }
376
377    /// Samples an accepted execute transaction at the given index.
378    pub(crate) fn sample_accepted_execute(
379        index: u32,
380        is_fee_private: bool,
381        rng: &mut TestRng,
382    ) -> ConfirmedTransaction<CurrentNetwork> {
383        // Sample an execute transaction.
384        let tx = crate::transaction::test_helpers::sample_execution_transaction_with_fee(is_fee_private, rng);
385        // Return the confirmed transaction.
386        ConfirmedTransaction::accepted_execute(index, tx, vec![]).unwrap()
387    }
388
389    /// Samples a rejected deploy transaction at the given index.
390    pub(crate) fn sample_rejected_deploy(
391        index: u32,
392        is_fee_private: bool,
393        rng: &mut TestRng,
394    ) -> ConfirmedTransaction<CurrentNetwork> {
395        // Sample a fee transaction.
396        let fee_transaction = match is_fee_private {
397            true => crate::transaction::test_helpers::sample_private_fee_transaction(rng),
398            false => crate::transaction::test_helpers::sample_fee_public_transaction(rng),
399        };
400
401        // Extract the rejected deployment.
402        let rejected = crate::rejected::test_helpers::sample_rejected_deployment(is_fee_private, rng);
403
404        // Return the confirmed transaction.
405        ConfirmedTransaction::rejected_deploy(index, fee_transaction, rejected, vec![]).unwrap()
406    }
407
408    /// Samples a rejected execute transaction at the given index.
409    pub(crate) fn sample_rejected_execute(
410        index: u32,
411        is_fee_private: bool,
412        rng: &mut TestRng,
413    ) -> ConfirmedTransaction<CurrentNetwork> {
414        // Sample a fee transaction.
415        let fee_transaction = match is_fee_private {
416            true => crate::transaction::test_helpers::sample_private_fee_transaction(rng),
417            false => crate::transaction::test_helpers::sample_fee_public_transaction(rng),
418        };
419
420        // Extract the rejected execution.
421        let rejected = crate::rejected::test_helpers::sample_rejected_execution(is_fee_private, rng);
422
423        // Return the confirmed transaction.
424        ConfirmedTransaction::rejected_execute(index, fee_transaction, rejected, vec![]).unwrap()
425    }
426
427    /// Sample a list of randomly confirmed transactions.
428    pub(crate) fn sample_confirmed_transactions() -> Vec<ConfirmedTransaction<CurrentNetwork>> {
429        let rng = &mut TestRng::default();
430
431        vec![
432            sample_accepted_deploy(0, true, rng),
433            sample_accepted_deploy(0, false, rng),
434            sample_accepted_execute(1, true, rng),
435            sample_accepted_execute(1, false, rng),
436            sample_rejected_deploy(2, true, rng),
437            sample_rejected_deploy(2, false, rng),
438            sample_rejected_execute(3, true, rng),
439            sample_rejected_execute(3, false, rng),
440            sample_accepted_deploy(Uniform::rand(rng), true, rng),
441            sample_accepted_deploy(Uniform::rand(rng), false, rng),
442            sample_accepted_execute(Uniform::rand(rng), true, rng),
443            sample_accepted_execute(Uniform::rand(rng), false, rng),
444            sample_rejected_deploy(Uniform::rand(rng), true, rng),
445            sample_rejected_deploy(Uniform::rand(rng), false, rng),
446            sample_rejected_execute(Uniform::rand(rng), true, rng),
447            sample_rejected_execute(Uniform::rand(rng), false, rng),
448        ]
449    }
450}
451
452#[cfg(test)]
453mod test {
454    use super::*;
455    use crate::transactions::confirmed::test_helpers;
456
457    type CurrentNetwork = console::network::MainnetV0;
458
459    #[test]
460    fn test_accepted_execute() {
461        let rng = &mut TestRng::default();
462
463        let index = Uniform::rand(rng);
464        let tx = crate::transaction::test_helpers::sample_execution_transaction_with_fee(true, rng);
465
466        // Create an `AcceptedExecution` with valid `FinalizeOperation`s.
467        let finalize_operations = vec![
468            FinalizeOperation::InsertKeyValue(Uniform::rand(rng), Uniform::rand(rng), Uniform::rand(rng)),
469            FinalizeOperation::UpdateKeyValue(Uniform::rand(rng), Uniform::rand(rng), Uniform::rand(rng)),
470            FinalizeOperation::RemoveKeyValue(Uniform::rand(rng), Uniform::rand(rng)),
471        ];
472        let confirmed = ConfirmedTransaction::accepted_execute(index, tx.clone(), finalize_operations.clone()).unwrap();
473
474        assert_eq!(confirmed.index(), index);
475        assert_eq!(confirmed.transaction(), &tx);
476        assert_eq!(confirmed.num_finalize(), finalize_operations.len());
477        assert_eq!(confirmed.finalize_operations(), &finalize_operations);
478
479        // Attempt to create an `AcceptedExecution` with invalid `FinalizeOperation`s.
480        let finalize_operations = vec![FinalizeOperation::InitializeMapping(Uniform::rand(rng))];
481        let confirmed = ConfirmedTransaction::accepted_execute(index, tx.clone(), finalize_operations);
482        assert!(confirmed.is_err());
483
484        let finalize_operations = vec![FinalizeOperation::RemoveMapping(Uniform::rand(rng))];
485        let confirmed = ConfirmedTransaction::accepted_execute(index, tx, finalize_operations);
486        assert!(confirmed.is_err());
487    }
488
489    #[test]
490    fn test_contains_unconfirmed_transaction_id() {
491        let rng = &mut TestRng::default();
492
493        // A helper function to check that the unconfirmed transaction ID is correct.
494        let check_contains_unconfirmed_transaction_id = |confirmed: ConfirmedTransaction<CurrentNetwork>| {
495            let rng = &mut TestRng::default();
496            let unconfirmed_transaction_id = confirmed.to_unconfirmed_transaction_id().unwrap();
497            assert!(confirmed.contains_unconfirmed_transaction_id(&unconfirmed_transaction_id));
498            assert!(!confirmed.contains_unconfirmed_transaction_id(&<CurrentNetwork as Network>::TransactionID::from(
499                Field::rand(rng)
500            )));
501        };
502
503        // Ensure that the unconfirmed transaction ID of an accepted deployment is equivalent to its confirmed transaction ID.
504        let accepted_deploy = test_helpers::sample_accepted_deploy(Uniform::rand(rng), true, rng);
505        check_contains_unconfirmed_transaction_id(accepted_deploy);
506        let accepted_deploy = test_helpers::sample_accepted_deploy(Uniform::rand(rng), false, rng);
507        check_contains_unconfirmed_transaction_id(accepted_deploy);
508
509        // Ensure that the unconfirmed transaction ID of an accepted execute is equivalent to its confirmed transaction ID.
510        let accepted_execution = test_helpers::sample_accepted_execute(Uniform::rand(rng), true, rng);
511        check_contains_unconfirmed_transaction_id(accepted_execution);
512        let accepted_execution = test_helpers::sample_accepted_execute(Uniform::rand(rng), false, rng);
513        check_contains_unconfirmed_transaction_id(accepted_execution);
514
515        // Ensure that the unconfirmed transaction ID of a rejected deployment is not equivalent to its confirmed transaction ID.
516        let rejected_deploy = test_helpers::sample_rejected_deploy(Uniform::rand(rng), true, rng);
517        check_contains_unconfirmed_transaction_id(rejected_deploy);
518        let rejected_deploy = test_helpers::sample_rejected_deploy(Uniform::rand(rng), false, rng);
519        check_contains_unconfirmed_transaction_id(rejected_deploy);
520
521        // Ensure that the unconfirmed transaction ID of a rejected execute is not equivalent to its confirmed transaction ID.
522        let rejected_execution = test_helpers::sample_rejected_execute(Uniform::rand(rng), true, rng);
523        check_contains_unconfirmed_transaction_id(rejected_execution);
524        let rejected_execution = test_helpers::sample_rejected_execute(Uniform::rand(rng), false, rng);
525        check_contains_unconfirmed_transaction_id(rejected_execution);
526    }
527
528    #[test]
529    fn test_unconfirmed_transaction_ids() {
530        let rng = &mut TestRng::default();
531
532        // Ensure that the unconfirmed transaction ID of an accepted deployment is equivalent to its confirmed transaction ID.
533        let accepted_deploy = test_helpers::sample_accepted_deploy(Uniform::rand(rng), true, rng);
534        assert_eq!(accepted_deploy.to_unconfirmed_transaction_id().unwrap(), accepted_deploy.id());
535        let accepted_deploy = test_helpers::sample_accepted_deploy(Uniform::rand(rng), false, rng);
536        assert_eq!(accepted_deploy.to_unconfirmed_transaction_id().unwrap(), accepted_deploy.id());
537
538        // Ensure that the unconfirmed transaction ID of an accepted execute is equivalent to its confirmed transaction ID.
539        let accepted_execution = test_helpers::sample_accepted_execute(Uniform::rand(rng), true, rng);
540        assert_eq!(accepted_execution.to_unconfirmed_transaction_id().unwrap(), accepted_execution.id());
541        let accepted_execution = test_helpers::sample_accepted_execute(Uniform::rand(rng), false, rng);
542        assert_eq!(accepted_execution.to_unconfirmed_transaction_id().unwrap(), accepted_execution.id());
543
544        // Ensure that the unconfirmed transaction ID of a rejected deployment is not equivalent to its confirmed transaction ID.
545        let rejected_deploy = test_helpers::sample_rejected_deploy(Uniform::rand(rng), true, rng);
546        assert_ne!(rejected_deploy.to_unconfirmed_transaction_id().unwrap(), rejected_deploy.id());
547        let rejected_deploy = test_helpers::sample_rejected_deploy(Uniform::rand(rng), false, rng);
548        assert_ne!(rejected_deploy.to_unconfirmed_transaction_id().unwrap(), rejected_deploy.id());
549
550        // Ensure that the unconfirmed transaction ID of a rejected execute is not equivalent to its confirmed transaction ID.
551        let rejected_execution = test_helpers::sample_rejected_execute(Uniform::rand(rng), true, rng);
552        assert_ne!(rejected_execution.to_unconfirmed_transaction_id().unwrap(), rejected_execution.id());
553        let rejected_execution = test_helpers::sample_rejected_execute(Uniform::rand(rng), false, rng);
554        assert_ne!(rejected_execution.to_unconfirmed_transaction_id().unwrap(), rejected_execution.id());
555    }
556
557    #[test]
558    fn test_unconfirmed_transactions() {
559        let rng = &mut TestRng::default();
560
561        // Ensure that the unconfirmed transaction of an accepted deployment is equivalent to its confirmed transaction.
562        let accepted_deploy = test_helpers::sample_accepted_deploy(Uniform::rand(rng), true, rng);
563        assert_eq!(&accepted_deploy.to_unconfirmed_transaction().unwrap(), accepted_deploy.transaction());
564        let accepted_deploy = test_helpers::sample_accepted_deploy(Uniform::rand(rng), false, rng);
565        assert_eq!(&accepted_deploy.to_unconfirmed_transaction().unwrap(), accepted_deploy.transaction());
566
567        // Ensure that the unconfirmed transaction of an accepted execute is equivalent to its confirmed transaction.
568        let accepted_execution = test_helpers::sample_accepted_execute(Uniform::rand(rng), true, rng);
569        assert_eq!(&accepted_execution.to_unconfirmed_transaction().unwrap(), accepted_execution.transaction());
570        let accepted_execution = test_helpers::sample_accepted_execute(Uniform::rand(rng), false, rng);
571        assert_eq!(&accepted_execution.to_unconfirmed_transaction().unwrap(), accepted_execution.transaction());
572
573        // Ensure that the unconfirmed transaction of a rejected deployment is not equivalent to its confirmed transaction.
574        let deployment_transaction = crate::transaction::test_helpers::sample_deployment_transaction(true, rng);
575        let rejected = Rejected::new_deployment(
576            *deployment_transaction.owner().unwrap(),
577            deployment_transaction.deployment().unwrap().clone(),
578        );
579        let fee = Transaction::from_fee(deployment_transaction.fee_transition().unwrap()).unwrap();
580        let rejected_deploy = ConfirmedTransaction::rejected_deploy(Uniform::rand(rng), fee, rejected, vec![]).unwrap();
581        assert_eq!(rejected_deploy.to_unconfirmed_transaction_id().unwrap(), deployment_transaction.id());
582        assert_eq!(rejected_deploy.to_unconfirmed_transaction().unwrap(), deployment_transaction);
583        let deployment_transaction = crate::transaction::test_helpers::sample_deployment_transaction(false, rng);
584        let rejected = Rejected::new_deployment(
585            *deployment_transaction.owner().unwrap(),
586            deployment_transaction.deployment().unwrap().clone(),
587        );
588        let fee = Transaction::from_fee(deployment_transaction.fee_transition().unwrap()).unwrap();
589        let rejected_deploy = ConfirmedTransaction::rejected_deploy(Uniform::rand(rng), fee, rejected, vec![]).unwrap();
590        assert_eq!(rejected_deploy.to_unconfirmed_transaction_id().unwrap(), deployment_transaction.id());
591        assert_eq!(rejected_deploy.to_unconfirmed_transaction().unwrap(), deployment_transaction);
592
593        // Ensure that the unconfirmed transaction of a rejected execute is not equivalent to its confirmed transaction.
594        let execution_transaction = crate::transaction::test_helpers::sample_execution_transaction_with_fee(true, rng);
595        let rejected = Rejected::new_execution(execution_transaction.execution().unwrap().clone());
596        let fee = Transaction::from_fee(execution_transaction.fee_transition().unwrap()).unwrap();
597        let rejected_execute =
598            ConfirmedTransaction::rejected_execute(Uniform::rand(rng), fee, rejected, vec![]).unwrap();
599        assert_eq!(rejected_execute.to_unconfirmed_transaction_id().unwrap(), execution_transaction.id());
600        assert_eq!(rejected_execute.to_unconfirmed_transaction().unwrap(), execution_transaction);
601        let execution_transaction = crate::transaction::test_helpers::sample_execution_transaction_with_fee(false, rng);
602        let rejected = Rejected::new_execution(execution_transaction.execution().unwrap().clone());
603        let fee = Transaction::from_fee(execution_transaction.fee_transition().unwrap()).unwrap();
604        let rejected_execute =
605            ConfirmedTransaction::rejected_execute(Uniform::rand(rng), fee, rejected, vec![]).unwrap();
606        assert_eq!(rejected_execute.to_unconfirmed_transaction_id().unwrap(), execution_transaction.id());
607        assert_eq!(rejected_execute.to_unconfirmed_transaction().unwrap(), execution_transaction);
608    }
609}