mod.rs
  1  // Copyright (c) 2019-2025 Alpha-Delta Network Inc.
  2  // This file is part of the alphavm 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  
 16  mod bytes;
 17  mod serialize;
 18  mod string;
 19  
 20  use crate::{Transaction, rejected::Rejected};
 21  use alphavm_synthesizer_program::FinalizeOperation;
 22  use console::{network::prelude::*, program::FINALIZE_ID_DEPTH, types::Field};
 23  
 24  pub type NumFinalizeSize = u16;
 25  
 26  /// The confirmed transaction.
 27  #[derive(Clone, PartialEq, Eq)]
 28  pub 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  
 43  impl<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  
205  impl<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  
225  impl<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  
354  impl<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)]
364  pub 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)]
480  mod 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  }