Skip to main content

snarkvm_ledger_block/transactions/confirmed/
mod.rs

1// Copyright (c) 2019-2026 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 transaction 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        has_translation_keys: bool,
376        is_fee_private: bool,
377        rng: &mut TestRng,
378    ) -> ConfirmedTransaction<CurrentNetwork> {
379        // Sample a deploy transaction.
380        let tx = crate::transaction::test_helpers::sample_deployment_transaction(
381            version,
382            edition,
383            has_translation_keys,
384            is_fee_private,
385            rng,
386        );
387
388        // Construct the finalize operations based on if the fee is public or private.
389        let finalize_operations = match is_fee_private {
390            true => vec![FinalizeOperation::InitializeMapping(Uniform::rand(rng))],
391            false => vec![
392                FinalizeOperation::InitializeMapping(Uniform::rand(rng)),
393                FinalizeOperation::UpdateKeyValue(Uniform::rand(rng), Uniform::rand(rng), Uniform::rand(rng)),
394            ],
395        };
396
397        // Return the confirmed transaction.
398        ConfirmedTransaction::accepted_deploy(index, tx, finalize_operations).unwrap()
399    }
400
401    /// Samples an accepted execute transaction at the given index.
402    pub(crate) fn sample_accepted_execute(
403        index: u32,
404        is_fee_private: bool,
405        rng: &mut TestRng,
406    ) -> ConfirmedTransaction<CurrentNetwork> {
407        // Sample an execute transaction.
408        let tx = crate::transaction::test_helpers::sample_execution_transaction_with_fee(is_fee_private, rng, 0);
409        // Return the confirmed transaction.
410        ConfirmedTransaction::accepted_execute(index, tx, vec![]).unwrap()
411    }
412
413    /// Samples a rejected deploy transaction at the given index.
414    pub(crate) fn sample_rejected_deploy(
415        index: u32,
416        version: u8,
417        edition: u16,
418        has_translation_keys: bool,
419        is_fee_private: bool,
420        rng: &mut TestRng,
421    ) -> ConfirmedTransaction<CurrentNetwork> {
422        // Sample a fee transaction.
423        let fee_transaction = match is_fee_private {
424            true => crate::transaction::test_helpers::sample_private_fee_transaction(rng),
425            false => crate::transaction::test_helpers::sample_fee_public_transaction(rng),
426        };
427
428        // Extract the rejected deployment.
429        let rejected = crate::rejected::test_helpers::sample_rejected_deployment(
430            version,
431            edition,
432            has_translation_keys,
433            is_fee_private,
434            rng,
435        );
436
437        // Return the confirmed transaction.
438        ConfirmedTransaction::rejected_deploy(index, fee_transaction, rejected, vec![]).unwrap()
439    }
440
441    /// Samples a rejected execute transaction at the given index.
442    pub(crate) fn sample_rejected_execute(
443        index: u32,
444        is_fee_private: bool,
445        rng: &mut TestRng,
446    ) -> ConfirmedTransaction<CurrentNetwork> {
447        // Sample a fee transaction.
448        let fee_transaction = match is_fee_private {
449            true => crate::transaction::test_helpers::sample_private_fee_transaction(rng),
450            false => crate::transaction::test_helpers::sample_fee_public_transaction(rng),
451        };
452
453        // Extract the rejected execution.
454        let rejected = crate::rejected::test_helpers::sample_rejected_execution(is_fee_private, rng);
455
456        // Return the confirmed transaction.
457        ConfirmedTransaction::rejected_execute(index, fee_transaction, rejected, vec![]).unwrap()
458    }
459
460    /// Sample a list of randomly confirmed transactions.
461    pub(crate) fn sample_confirmed_transactions() -> Vec<ConfirmedTransaction<CurrentNetwork>> {
462        let rng = &mut TestRng::default();
463
464        vec![
465            sample_accepted_deploy(0, 1, Uniform::rand(rng), false, true, rng),
466            sample_accepted_deploy(0, 1, Uniform::rand(rng), false, true, rng),
467            sample_accepted_deploy(0, 2, Uniform::rand(rng), false, true, rng),
468            sample_accepted_deploy(0, 2, Uniform::rand(rng), true, true, rng),
469            sample_accepted_execute(1, true, rng),
470            sample_accepted_execute(1, false, rng),
471            sample_rejected_deploy(2, 1, Uniform::rand(rng), false, true, rng),
472            sample_rejected_deploy(2, 1, Uniform::rand(rng), false, true, rng),
473            sample_rejected_deploy(2, 2, Uniform::rand(rng), false, true, rng),
474            sample_rejected_deploy(2, 2, Uniform::rand(rng), true, true, rng),
475            sample_rejected_execute(3, true, rng),
476            sample_rejected_execute(3, true, rng),
477            sample_rejected_execute(3, false, rng),
478            sample_rejected_execute(3, false, rng),
479            sample_accepted_execute(Uniform::rand(rng), true, rng),
480            sample_accepted_execute(Uniform::rand(rng), false, rng),
481            sample_rejected_deploy(Uniform::rand(rng), 1, Uniform::rand(rng), false, Uniform::rand(rng), rng),
482            sample_rejected_deploy(Uniform::rand(rng), 1, Uniform::rand(rng), false, Uniform::rand(rng), rng),
483            sample_rejected_deploy(Uniform::rand(rng), 1, Uniform::rand(rng), false, Uniform::rand(rng), rng),
484            sample_rejected_deploy(Uniform::rand(rng), 1, Uniform::rand(rng), false, Uniform::rand(rng), rng),
485            sample_rejected_deploy(Uniform::rand(rng), 2, Uniform::rand(rng), false, Uniform::rand(rng), rng),
486            sample_rejected_deploy(Uniform::rand(rng), 2, Uniform::rand(rng), false, Uniform::rand(rng), rng),
487            sample_rejected_deploy(Uniform::rand(rng), 2, Uniform::rand(rng), false, Uniform::rand(rng), rng),
488            sample_rejected_deploy(Uniform::rand(rng), 2, Uniform::rand(rng), false, Uniform::rand(rng), rng),
489            sample_rejected_execute(Uniform::rand(rng), true, rng),
490            sample_rejected_execute(Uniform::rand(rng), true, rng),
491            sample_rejected_execute(Uniform::rand(rng), false, rng),
492            sample_rejected_execute(Uniform::rand(rng), false, rng),
493        ]
494    }
495}
496
497#[cfg(test)]
498mod test {
499    use super::*;
500    use crate::transactions::confirmed::test_helpers;
501
502    type CurrentNetwork = console::network::MainnetV0;
503
504    #[test]
505    fn test_accepted_execute() {
506        let rng = &mut TestRng::default();
507
508        let index = Uniform::rand(rng);
509        let tx = crate::transaction::test_helpers::sample_execution_transaction_with_fee(true, rng, 0);
510
511        // Create an `AcceptedExecution` with valid `FinalizeOperation`s.
512        let finalize_operations = vec![
513            FinalizeOperation::InsertKeyValue(Uniform::rand(rng), Uniform::rand(rng), Uniform::rand(rng)),
514            FinalizeOperation::UpdateKeyValue(Uniform::rand(rng), Uniform::rand(rng), Uniform::rand(rng)),
515            FinalizeOperation::RemoveKeyValue(Uniform::rand(rng), Uniform::rand(rng)),
516        ];
517        let confirmed = ConfirmedTransaction::accepted_execute(index, tx.clone(), finalize_operations.clone()).unwrap();
518
519        assert_eq!(confirmed.index(), index);
520        assert_eq!(confirmed.transaction(), &tx);
521        assert_eq!(confirmed.num_finalize(), finalize_operations.len());
522        assert_eq!(confirmed.finalize_operations(), &finalize_operations);
523
524        // Attempt to create an `AcceptedExecution` with invalid `FinalizeOperation`s.
525        let finalize_operations = vec![FinalizeOperation::InitializeMapping(Uniform::rand(rng))];
526        let confirmed = ConfirmedTransaction::accepted_execute(index, tx.clone(), finalize_operations);
527        assert!(confirmed.is_err());
528
529        let finalize_operations = vec![FinalizeOperation::RemoveMapping(Uniform::rand(rng))];
530        let confirmed = ConfirmedTransaction::accepted_execute(index, tx, finalize_operations);
531        assert!(confirmed.is_err());
532    }
533
534    #[test]
535    fn test_contains_unconfirmed_transaction_id() {
536        let rng = &mut TestRng::default();
537
538        // A helper function to check that the unconfirmed transaction ID is correct.
539        let check_contains_unconfirmed_transaction_id = |confirmed: ConfirmedTransaction<CurrentNetwork>| {
540            let rng = &mut TestRng::default();
541            let unconfirmed_transaction_id = confirmed.to_unconfirmed_transaction_id().unwrap();
542            assert!(confirmed.contains_unconfirmed_transaction_id(&unconfirmed_transaction_id));
543            assert!(!confirmed.contains_unconfirmed_transaction_id(&<CurrentNetwork as Network>::TransactionID::from(
544                Field::rand(rng)
545            )));
546        };
547
548        // Ensure that the unconfirmed transaction ID of an accepted deployment is equivalent to its confirmed transaction ID.
549        let accepted_deploy =
550            test_helpers::sample_accepted_deploy(Uniform::rand(rng), 1, Uniform::rand(rng), false, true, rng);
551        check_contains_unconfirmed_transaction_id(accepted_deploy);
552        let accepted_deploy =
553            test_helpers::sample_accepted_deploy(Uniform::rand(rng), 1, Uniform::rand(rng), false, false, rng);
554        check_contains_unconfirmed_transaction_id(accepted_deploy);
555        let accepted_deploy =
556            test_helpers::sample_accepted_deploy(Uniform::rand(rng), 2, Uniform::rand(rng), false, true, rng);
557        check_contains_unconfirmed_transaction_id(accepted_deploy);
558        let accepted_deploy =
559            test_helpers::sample_accepted_deploy(Uniform::rand(rng), 2, Uniform::rand(rng), false, false, rng);
560        check_contains_unconfirmed_transaction_id(accepted_deploy);
561
562        // Ensure that the unconfirmed transaction ID of an accepted execute is equivalent to its confirmed transaction ID.
563        let accepted_execution = test_helpers::sample_accepted_execute(Uniform::rand(rng), true, rng);
564        check_contains_unconfirmed_transaction_id(accepted_execution);
565        let accepted_execution = test_helpers::sample_accepted_execute(Uniform::rand(rng), false, rng);
566        check_contains_unconfirmed_transaction_id(accepted_execution);
567
568        // Ensure that the unconfirmed transaction ID of a rejected deployment is not equivalent to its confirmed transaction ID.
569        let rejected_deploy =
570            test_helpers::sample_rejected_deploy(Uniform::rand(rng), 1, Uniform::rand(rng), false, true, rng);
571        check_contains_unconfirmed_transaction_id(rejected_deploy);
572        let rejected_deploy =
573            test_helpers::sample_rejected_deploy(Uniform::rand(rng), 1, Uniform::rand(rng), false, false, rng);
574        check_contains_unconfirmed_transaction_id(rejected_deploy);
575        let rejected_deploy =
576            test_helpers::sample_rejected_deploy(Uniform::rand(rng), 2, Uniform::rand(rng), false, true, rng);
577        check_contains_unconfirmed_transaction_id(rejected_deploy);
578        let rejected_deploy =
579            test_helpers::sample_rejected_deploy(Uniform::rand(rng), 2, Uniform::rand(rng), false, false, rng);
580        check_contains_unconfirmed_transaction_id(rejected_deploy);
581
582        // Ensure that the unconfirmed transaction ID of a rejected execute is not equivalent to its confirmed transaction ID.
583        let rejected_execution = test_helpers::sample_rejected_execute(Uniform::rand(rng), true, rng);
584        check_contains_unconfirmed_transaction_id(rejected_execution);
585        let rejected_execution = test_helpers::sample_rejected_execute(Uniform::rand(rng), false, rng);
586        check_contains_unconfirmed_transaction_id(rejected_execution);
587    }
588
589    #[test]
590    fn test_unconfirmed_transaction_ids() {
591        let rng = &mut TestRng::default();
592
593        // Ensure that the unconfirmed transaction ID of an accepted deployment is equivalent to its confirmed transaction ID.
594        let accepted_deploy =
595            test_helpers::sample_accepted_deploy(Uniform::rand(rng), 1, Uniform::rand(rng), false, true, rng);
596        assert_eq!(accepted_deploy.to_unconfirmed_transaction_id().unwrap(), accepted_deploy.id());
597        let accepted_deploy =
598            test_helpers::sample_accepted_deploy(Uniform::rand(rng), 1, Uniform::rand(rng), false, false, rng);
599        assert_eq!(accepted_deploy.to_unconfirmed_transaction_id().unwrap(), accepted_deploy.id());
600        let accepted_deploy =
601            test_helpers::sample_accepted_deploy(Uniform::rand(rng), 2, Uniform::rand(rng), false, true, rng);
602        assert_eq!(accepted_deploy.to_unconfirmed_transaction_id().unwrap(), accepted_deploy.id());
603        let accepted_deploy =
604            test_helpers::sample_accepted_deploy(Uniform::rand(rng), 2, Uniform::rand(rng), false, false, rng);
605        assert_eq!(accepted_deploy.to_unconfirmed_transaction_id().unwrap(), accepted_deploy.id());
606
607        // Ensure that the unconfirmed transaction ID of an accepted execute is equivalent to its confirmed transaction ID.
608        let accepted_execution = test_helpers::sample_accepted_execute(Uniform::rand(rng), true, rng);
609        assert_eq!(accepted_execution.to_unconfirmed_transaction_id().unwrap(), accepted_execution.id());
610        let accepted_execution = test_helpers::sample_accepted_execute(Uniform::rand(rng), false, rng);
611        assert_eq!(accepted_execution.to_unconfirmed_transaction_id().unwrap(), accepted_execution.id());
612
613        // Ensure that the unconfirmed transaction ID of a rejected deployment is not equivalent to its confirmed transaction ID.
614        let rejected_deploy =
615            test_helpers::sample_rejected_deploy(Uniform::rand(rng), 1, Uniform::rand(rng), false, true, rng);
616        assert_ne!(rejected_deploy.to_unconfirmed_transaction_id().unwrap(), rejected_deploy.id());
617        let rejected_deploy =
618            test_helpers::sample_rejected_deploy(Uniform::rand(rng), 1, Uniform::rand(rng), false, false, rng);
619        assert_ne!(rejected_deploy.to_unconfirmed_transaction_id().unwrap(), rejected_deploy.id());
620        let rejected_deploy =
621            test_helpers::sample_rejected_deploy(Uniform::rand(rng), 2, Uniform::rand(rng), false, true, rng);
622        assert_ne!(rejected_deploy.to_unconfirmed_transaction_id().unwrap(), rejected_deploy.id());
623        let rejected_deploy =
624            test_helpers::sample_rejected_deploy(Uniform::rand(rng), 2, Uniform::rand(rng), false, false, rng);
625        assert_ne!(rejected_deploy.to_unconfirmed_transaction_id().unwrap(), rejected_deploy.id());
626
627        // Ensure that the unconfirmed transaction ID of a rejected execute is not equivalent to its confirmed transaction ID.
628        let rejected_execution = test_helpers::sample_rejected_execute(Uniform::rand(rng), true, rng);
629        assert_ne!(rejected_execution.to_unconfirmed_transaction_id().unwrap(), rejected_execution.id());
630        let rejected_execution = test_helpers::sample_rejected_execute(Uniform::rand(rng), false, rng);
631        assert_ne!(rejected_execution.to_unconfirmed_transaction_id().unwrap(), rejected_execution.id());
632    }
633
634    #[test]
635    fn test_unconfirmed_transactions() {
636        let rng = &mut TestRng::default();
637
638        // Ensure that the unconfirmed transaction of an accepted deployment is equivalent to its confirmed transaction.
639        let accepted_deploy =
640            test_helpers::sample_accepted_deploy(Uniform::rand(rng), 1, Uniform::rand(rng), false, true, rng);
641        assert_eq!(&accepted_deploy.to_unconfirmed_transaction().unwrap(), accepted_deploy.transaction());
642        let accepted_deploy =
643            test_helpers::sample_accepted_deploy(Uniform::rand(rng), 1, Uniform::rand(rng), false, false, rng);
644        assert_eq!(&accepted_deploy.to_unconfirmed_transaction().unwrap(), accepted_deploy.transaction());
645        let accepted_deploy =
646            test_helpers::sample_accepted_deploy(Uniform::rand(rng), 2, Uniform::rand(rng), false, true, rng);
647        assert_eq!(&accepted_deploy.to_unconfirmed_transaction().unwrap(), accepted_deploy.transaction());
648        let accepted_deploy =
649            test_helpers::sample_accepted_deploy(Uniform::rand(rng), 2, Uniform::rand(rng), false, false, rng);
650        assert_eq!(&accepted_deploy.to_unconfirmed_transaction().unwrap(), accepted_deploy.transaction());
651
652        // Ensure that the unconfirmed transaction of an accepted execute is equivalent to its confirmed transaction.
653        let accepted_execution = test_helpers::sample_accepted_execute(Uniform::rand(rng), true, rng);
654        assert_eq!(&accepted_execution.to_unconfirmed_transaction().unwrap(), accepted_execution.transaction());
655        let accepted_execution = test_helpers::sample_accepted_execute(Uniform::rand(rng), false, rng);
656        assert_eq!(&accepted_execution.to_unconfirmed_transaction().unwrap(), accepted_execution.transaction());
657
658        // Ensure that the unconfirmed transaction of a rejected deployment is not equivalent to its confirmed transaction.
659        let deployment_transaction =
660            crate::transaction::test_helpers::sample_deployment_transaction(1, Uniform::rand(rng), false, true, rng);
661        let rejected = Rejected::new_deployment(
662            *deployment_transaction.owner().unwrap(),
663            deployment_transaction.deployment().unwrap().clone(),
664        );
665        let fee = Transaction::from_fee(deployment_transaction.fee_transition().unwrap()).unwrap();
666        let rejected_deploy = ConfirmedTransaction::rejected_deploy(Uniform::rand(rng), fee, rejected, vec![]).unwrap();
667        assert_eq!(rejected_deploy.to_unconfirmed_transaction_id().unwrap(), deployment_transaction.id());
668        assert_eq!(rejected_deploy.to_unconfirmed_transaction().unwrap(), deployment_transaction);
669
670        let deployment_transaction =
671            crate::transaction::test_helpers::sample_deployment_transaction(1, Uniform::rand(rng), false, false, rng);
672        let rejected = Rejected::new_deployment(
673            *deployment_transaction.owner().unwrap(),
674            deployment_transaction.deployment().unwrap().clone(),
675        );
676        let fee = Transaction::from_fee(deployment_transaction.fee_transition().unwrap()).unwrap();
677        let rejected_deploy = ConfirmedTransaction::rejected_deploy(Uniform::rand(rng), fee, rejected, vec![]).unwrap();
678        assert_eq!(rejected_deploy.to_unconfirmed_transaction_id().unwrap(), deployment_transaction.id());
679        assert_eq!(rejected_deploy.to_unconfirmed_transaction().unwrap(), deployment_transaction);
680
681        let deployment_transaction =
682            crate::transaction::test_helpers::sample_deployment_transaction(2, Uniform::rand(rng), false, true, rng);
683        let rejected = Rejected::new_deployment(
684            *deployment_transaction.owner().unwrap(),
685            deployment_transaction.deployment().unwrap().clone(),
686        );
687        let fee = Transaction::from_fee(deployment_transaction.fee_transition().unwrap()).unwrap();
688        let rejected_deploy = ConfirmedTransaction::rejected_deploy(Uniform::rand(rng), fee, rejected, vec![]).unwrap();
689        assert_eq!(rejected_deploy.to_unconfirmed_transaction_id().unwrap(), deployment_transaction.id());
690        assert_eq!(rejected_deploy.to_unconfirmed_transaction().unwrap(), deployment_transaction);
691
692        let deployment_transaction =
693            crate::transaction::test_helpers::sample_deployment_transaction(2, Uniform::rand(rng), false, false, rng);
694        let rejected = Rejected::new_deployment(
695            *deployment_transaction.owner().unwrap(),
696            deployment_transaction.deployment().unwrap().clone(),
697        );
698        let fee = Transaction::from_fee(deployment_transaction.fee_transition().unwrap()).unwrap();
699        let rejected_deploy = ConfirmedTransaction::rejected_deploy(Uniform::rand(rng), fee, rejected, vec![]).unwrap();
700        assert_eq!(rejected_deploy.to_unconfirmed_transaction_id().unwrap(), deployment_transaction.id());
701        assert_eq!(rejected_deploy.to_unconfirmed_transaction().unwrap(), deployment_transaction);
702
703        let deployment_transaction =
704            crate::transaction::test_helpers::sample_deployment_transaction(2, Uniform::rand(rng), true, true, rng);
705        let rejected = Rejected::new_deployment(
706            *deployment_transaction.owner().unwrap(),
707            deployment_transaction.deployment().unwrap().clone(),
708        );
709        let fee = Transaction::from_fee(deployment_transaction.fee_transition().unwrap()).unwrap();
710        let rejected_deploy = ConfirmedTransaction::rejected_deploy(Uniform::rand(rng), fee, rejected, vec![]).unwrap();
711        assert_eq!(rejected_deploy.to_unconfirmed_transaction_id().unwrap(), deployment_transaction.id());
712        assert_eq!(rejected_deploy.to_unconfirmed_transaction().unwrap(), deployment_transaction);
713
714        let deployment_transaction =
715            crate::transaction::test_helpers::sample_deployment_transaction(2, Uniform::rand(rng), true, false, rng);
716        let rejected = Rejected::new_deployment(
717            *deployment_transaction.owner().unwrap(),
718            deployment_transaction.deployment().unwrap().clone(),
719        );
720        let fee = Transaction::from_fee(deployment_transaction.fee_transition().unwrap()).unwrap();
721        let rejected_deploy = ConfirmedTransaction::rejected_deploy(Uniform::rand(rng), fee, rejected, vec![]).unwrap();
722        assert_eq!(rejected_deploy.to_unconfirmed_transaction_id().unwrap(), deployment_transaction.id());
723        assert_eq!(rejected_deploy.to_unconfirmed_transaction().unwrap(), deployment_transaction);
724
725        // Ensure that the unconfirmed transaction of a rejected execute is not equivalent to its confirmed transaction.
726        let execution_transaction =
727            crate::transaction::test_helpers::sample_execution_transaction_with_fee(true, rng, 0);
728        let rejected = Rejected::new_execution(execution_transaction.execution().unwrap().clone());
729        let fee = Transaction::from_fee(execution_transaction.fee_transition().unwrap()).unwrap();
730        let rejected_execute =
731            ConfirmedTransaction::rejected_execute(Uniform::rand(rng), fee, rejected, vec![]).unwrap();
732        assert_eq!(rejected_execute.to_unconfirmed_transaction_id().unwrap(), execution_transaction.id());
733        assert_eq!(rejected_execute.to_unconfirmed_transaction().unwrap(), execution_transaction);
734
735        let execution_transaction =
736            crate::transaction::test_helpers::sample_execution_transaction_with_fee(false, rng, 0);
737        let rejected = Rejected::new_execution(execution_transaction.execution().unwrap().clone());
738        let fee = Transaction::from_fee(execution_transaction.fee_transition().unwrap()).unwrap();
739        let rejected_execute =
740            ConfirmedTransaction::rejected_execute(Uniform::rand(rng), fee, rejected, vec![]).unwrap();
741        assert_eq!(rejected_execute.to_unconfirmed_transaction_id().unwrap(), execution_transaction.id());
742        assert_eq!(rejected_execute.to_unconfirmed_transaction().unwrap(), execution_transaction);
743    }
744}