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