From 4559a6e907817bea82ab4a4f8238a6f8b67e5b2f Mon Sep 17 00:00:00 2001 From: Howard Wu <9260812+howardwu@users.noreply.github.com> Date: Wed, 25 Oct 2023 19:36:06 -0700 Subject: [PATCH 1/4] Adds replay prevention on rejected executions --- ledger/store/src/block/mod.rs | 73 +++++++++++++++---- ledger/store/src/helpers/memory/block.rs | 15 +++- ledger/store/src/helpers/rocksdb/block.rs | 13 +++- .../store/src/helpers/rocksdb/internal/id.rs | 4 + .../program/src/resources/credits.aleo | 8 +- synthesizer/src/vm/verify.rs | 19 ++++- 6 files changed, 106 insertions(+), 26 deletions(-) diff --git a/ledger/store/src/block/mod.rs b/ledger/store/src/block/mod.rs index 335b934526..603b44d4be 100644 --- a/ledger/store/src/block/mod.rs +++ b/ledger/store/src/block/mod.rs @@ -62,21 +62,22 @@ pub enum ConfirmedTxType { } /// Separates the confirmed transaction into a tuple. +#[allow(clippy::type_complexity)] fn to_confirmed_tuple( confirmed: ConfirmedTransaction, -) -> Result<(ConfirmedTxType, Transaction, Vec)> { +) -> Result<(ConfirmedTxType, Transaction, Vec, Option>)> { match confirmed { ConfirmedTransaction::AcceptedDeploy(index, tx, finalize) => { // Retrieve the number of finalize operations. let num_finalize = NumFinalizeSize::try_from(finalize.len())?; // Return the confirmed tuple. - Ok((ConfirmedTxType::AcceptedDeploy(index), tx, (num_finalize, finalize).to_bytes_le()?)) + Ok((ConfirmedTxType::AcceptedDeploy(index), tx, (num_finalize, finalize).to_bytes_le()?, None)) } ConfirmedTransaction::AcceptedExecute(index, tx, finalize) => { // Retrieve the number of finalize operations. let num_finalize = NumFinalizeSize::try_from(finalize.len())?; // Return the confirmed tuple. - Ok((ConfirmedTxType::AcceptedExecute(index), tx, (num_finalize, finalize).to_bytes_le()?)) + Ok((ConfirmedTxType::AcceptedExecute(index), tx, (num_finalize, finalize).to_bytes_le()?, None)) } ConfirmedTransaction::RejectedDeploy(index, tx, rejected, finalize) => { // Retrieve the number of finalize operations. @@ -92,7 +93,7 @@ fn to_confirmed_tuple( finalize.write_le(&mut blob)?; // Return the confirmed tuple. - Ok((ConfirmedTxType::RejectedDeploy(index), tx, blob)) + Ok((ConfirmedTxType::RejectedDeploy(index), tx, blob, Some(rejected))) } ConfirmedTransaction::RejectedExecute(index, tx, rejected, finalize) => { // Retrieve the number of finalize operations. @@ -108,7 +109,7 @@ fn to_confirmed_tuple( finalize.write_le(&mut blob)?; // Return the confirmed tuple. - Ok((ConfirmedTxType::RejectedExecute(index), tx, blob)) + Ok((ConfirmedTxType::RejectedExecute(index), tx, blob, Some(rejected))) } } } @@ -195,7 +196,11 @@ pub trait BlockStorage: 'static + Clone + Send + Sync { /// The mapping of rejected or aborted `transaction ID` to `block hash`. type RejectedOrAbortedTransactionIDMap: for<'a> Map<'a, N::TransactionID, N::BlockHash>; /// The mapping of `transaction ID` to `(block hash, confirmed tx type, confirmed blob)`. + /// TODO (howardwu): For mainnet - With recent DB changes, to prevent breaking compatibility, + /// include rejected (d or e) ID into `ConfirmedTxType`, and change from `Vec` to `Vec`. type ConfirmedTransactionsMap: for<'a> Map<'a, N::TransactionID, (N::BlockHash, ConfirmedTxType, Vec)>; + /// The rejected deployment or execution map. + type RejectedDeploymentOrExecutionMap: for<'a> Map<'a, Field, Rejected>; /// The transaction storage. type TransactionStorage: TransactionStorage; /// The transition storage. @@ -232,6 +237,8 @@ pub trait BlockStorage: 'static + Clone + Send + Sync { fn rejected_or_aborted_transaction_id_map(&self) -> &Self::RejectedOrAbortedTransactionIDMap; /// Returns the confirmed transactions map. fn confirmed_transactions_map(&self) -> &Self::ConfirmedTransactionsMap; + /// Returns the rejected deployment or execution map. + fn rejected_deployment_or_execution_map(&self) -> &Self::RejectedDeploymentOrExecutionMap; /// Returns the transaction store. fn transaction_store(&self) -> &TransactionStore; @@ -261,6 +268,7 @@ pub trait BlockStorage: 'static + Clone + Send + Sync { self.aborted_transaction_ids_map().start_atomic(); self.rejected_or_aborted_transaction_id_map().start_atomic(); self.confirmed_transactions_map().start_atomic(); + self.rejected_deployment_or_execution_map().start_atomic(); self.transaction_store().start_atomic(); } @@ -280,6 +288,7 @@ pub trait BlockStorage: 'static + Clone + Send + Sync { || self.aborted_transaction_ids_map().is_atomic_in_progress() || self.rejected_or_aborted_transaction_id_map().is_atomic_in_progress() || self.confirmed_transactions_map().is_atomic_in_progress() + || self.rejected_deployment_or_execution_map().is_atomic_in_progress() || self.transaction_store().is_atomic_in_progress() } @@ -299,6 +308,7 @@ pub trait BlockStorage: 'static + Clone + Send + Sync { self.aborted_transaction_ids_map().atomic_checkpoint(); self.rejected_or_aborted_transaction_id_map().atomic_checkpoint(); self.confirmed_transactions_map().atomic_checkpoint(); + self.rejected_deployment_or_execution_map().atomic_checkpoint(); self.transaction_store().atomic_checkpoint(); } @@ -318,6 +328,7 @@ pub trait BlockStorage: 'static + Clone + Send + Sync { self.aborted_transaction_ids_map().clear_latest_checkpoint(); self.rejected_or_aborted_transaction_id_map().clear_latest_checkpoint(); self.confirmed_transactions_map().clear_latest_checkpoint(); + self.rejected_deployment_or_execution_map().clear_latest_checkpoint(); self.transaction_store().clear_latest_checkpoint(); } @@ -337,6 +348,7 @@ pub trait BlockStorage: 'static + Clone + Send + Sync { self.aborted_transaction_ids_map().atomic_rewind(); self.rejected_or_aborted_transaction_id_map().atomic_rewind(); self.confirmed_transactions_map().atomic_rewind(); + self.rejected_deployment_or_execution_map().atomic_rewind(); self.transaction_store().atomic_rewind(); } @@ -356,6 +368,7 @@ pub trait BlockStorage: 'static + Clone + Send + Sync { self.aborted_transaction_ids_map().abort_atomic(); self.rejected_or_aborted_transaction_id_map().abort_atomic(); self.confirmed_transactions_map().abort_atomic(); + self.rejected_deployment_or_execution_map().abort_atomic(); self.transaction_store().abort_atomic(); } @@ -375,6 +388,7 @@ pub trait BlockStorage: 'static + Clone + Send + Sync { self.aborted_transaction_ids_map().finish_atomic()?; self.rejected_or_aborted_transaction_id_map().finish_atomic()?; self.confirmed_transactions_map().finish_atomic()?; + self.rejected_deployment_or_execution_map().finish_atomic()?; self.transaction_store().finish_atomic() } @@ -454,9 +468,13 @@ pub trait BlockStorage: 'static + Clone + Send + Sync { } // Store the confirmed transactions. - for (confirmed_type, transaction, blob) in confirmed { + for (confirmed_type, transaction, blob, rejected) in confirmed { // Store the block hash and confirmed transaction data. self.confirmed_transactions_map().insert(transaction.id(), (block.hash(), confirmed_type, blob))?; + // Store the rejected deployment or execution. + if let Some(rejected) = rejected { + self.rejected_deployment_or_execution_map().insert(rejected.to_id()?, rejected)?; + } // Store the transaction. self.transaction_store().insert(&transaction)?; } @@ -496,12 +514,12 @@ pub trait BlockStorage: 'static + Clone + Send + Sync { None => Vec::new(), }; - // Retrieve the rejected transaction IDs. - let rejected_transaction_ids = match self.get_block_transactions(block_hash)? { + // Retrieve the rejected transaction IDs, and deployment or execution. + let rejected_transaction_ids_and_deployment_or_execution = match self.get_block_transactions(block_hash)? { Some(transactions) => transactions .iter() .filter(|tx| tx.is_rejected()) - .map(|tx| tx.to_unconfirmed_transaction_id()) + .map(|tx| Ok((tx.to_unconfirmed_transaction_id()?, tx.to_rejected_id()?))) .collect::>>()?, None => Vec::new(), }; @@ -560,9 +578,14 @@ pub trait BlockStorage: 'static + Clone + Send + Sync { self.rejected_or_aborted_transaction_id_map().remove(&aborted_transaction_id)?; } - // Remove the rejected transaction IDs. - for rejected_transaction_id in rejected_transaction_ids { + // Remove the rejected state. + for (rejected_transaction_id, rejected_id) in rejected_transaction_ids_and_deployment_or_execution { + // Remove the rejected transaction ID. self.rejected_or_aborted_transaction_id_map().remove(&rejected_transaction_id)?; + // Remove the rejected deployment or execution. + if let Some(rejected_id) = rejected_id { + self.rejected_deployment_or_execution_map().remove(&rejected_id)?; + } } // Remove the block transactions. @@ -577,6 +600,22 @@ pub trait BlockStorage: 'static + Clone + Send + Sync { }) } + /// Returns `true` if the given transaction ID exists. + fn contains_transaction_id(&self, transaction_id: &N::TransactionID) -> Result { + Ok(self.transaction_store().contains_transaction_id(transaction_id)? + || self.contains_rejected_or_aborted_transaction_id(transaction_id)?) + } + + /// Returns `true` if the given rejected transaction ID or aborted transaction ID exists. + fn contains_rejected_or_aborted_transaction_id(&self, transaction_id: &N::TransactionID) -> Result { + self.rejected_or_aborted_transaction_id_map().contains_key_confirmed(transaction_id) + } + + /// Returns `true` if the given rejected deployment or execution ID. + fn contains_rejected_deployment_or_execution_id(&self, rejected_id: &Field) -> Result { + self.rejected_deployment_or_execution_map().contains_key_confirmed(rejected_id) + } + /// Returns the block height that contains the given `state root`. fn find_block_height_from_state_root(&self, state_root: N::StateRoot) -> Result> { match self.reverse_state_root_map().get_confirmed(&state_root)? { @@ -1240,13 +1279,17 @@ impl> BlockStore { /// Returns `true` if the given transaction ID exists. pub fn contains_transaction_id(&self, transaction_id: &N::TransactionID) -> Result { - Ok(self.transaction_store().contains_transaction_id(transaction_id)? - || self.contains_rejected_or_aborted_transaction_id(transaction_id)?) + self.storage.contains_transaction_id(transaction_id) } - /// Returns `true` if the given rejected or aborted transaction ID exists. + /// Returns `true` if the given rejected transaction ID or aborted transaction ID exists. pub fn contains_rejected_or_aborted_transaction_id(&self, transaction_id: &N::TransactionID) -> Result { - self.storage.rejected_or_aborted_transaction_id_map().contains_key_confirmed(transaction_id) + self.storage.contains_rejected_or_aborted_transaction_id(transaction_id) + } + + /// Returns `true` if the given rejected deployment or execution ID. + pub fn contains_rejected_deployment_or_execution_id(&self, rejected_id: &Field) -> Result { + self.storage.contains_rejected_deployment_or_execution_id(rejected_id) } /// Returns `true` if the given certificate ID exists. diff --git a/ledger/store/src/helpers/memory/block.rs b/ledger/store/src/helpers/memory/block.rs index b64af3a484..c09fa09f2e 100644 --- a/ledger/store/src/helpers/memory/block.rs +++ b/ledger/store/src/helpers/memory/block.rs @@ -21,7 +21,7 @@ use crate::{ }; use console::{prelude::*, types::Field}; use ledger_authority::Authority; -use ledger_block::{Header, Ratifications}; +use ledger_block::{Header, Ratifications, Rejected}; use ledger_coinbase::{CoinbaseSolution, PuzzleCommitment}; /// An in-memory block storage. @@ -51,10 +51,12 @@ pub struct BlockMemory { transactions_map: MemoryMap>, /// The aborted transaction IDs map. aborted_transaction_ids_map: MemoryMap>, - /// The rejected or aborted transaction ID map. + /// The rejected transaction ID or aborted transaction ID map. rejected_or_aborted_transaction_id_map: MemoryMap, /// The confirmed transactions map. confirmed_transactions_map: MemoryMap)>, + /// The rejected deployment or execution map. + rejected_deployment_or_execution_map: MemoryMap, Rejected>, /// The transaction store. transaction_store: TransactionStore>, } @@ -75,6 +77,7 @@ impl BlockStorage for BlockMemory { type AbortedTransactionIDsMap = MemoryMap>; type RejectedOrAbortedTransactionIDMap = MemoryMap; type ConfirmedTransactionsMap = MemoryMap)>; + type RejectedDeploymentOrExecutionMap = MemoryMap, Rejected>; type TransactionStorage = TransactionMemory; type TransitionStorage = TransitionMemory; @@ -100,6 +103,7 @@ impl BlockStorage for BlockMemory { aborted_transaction_ids_map: MemoryMap::default(), rejected_or_aborted_transaction_id_map: MemoryMap::default(), confirmed_transactions_map: MemoryMap::default(), + rejected_deployment_or_execution_map: MemoryMap::default(), transaction_store, }) } @@ -164,7 +168,7 @@ impl BlockStorage for BlockMemory { &self.aborted_transaction_ids_map } - /// Returns the rejected or aborted transaction ID map. + /// Returns the rejected transaction ID or aborted transaction ID map. fn rejected_or_aborted_transaction_id_map(&self) -> &Self::RejectedOrAbortedTransactionIDMap { &self.rejected_or_aborted_transaction_id_map } @@ -174,6 +178,11 @@ impl BlockStorage for BlockMemory { &self.confirmed_transactions_map } + /// Returns the rejected deployment or execution map. + fn rejected_deployment_or_execution_map(&self) -> &Self::RejectedDeploymentOrExecutionMap { + &self.rejected_deployment_or_execution_map + } + /// Returns the transaction store. fn transaction_store(&self) -> &TransactionStore { &self.transaction_store diff --git a/ledger/store/src/helpers/rocksdb/block.rs b/ledger/store/src/helpers/rocksdb/block.rs index dbf9975755..121ae2a994 100644 --- a/ledger/store/src/helpers/rocksdb/block.rs +++ b/ledger/store/src/helpers/rocksdb/block.rs @@ -27,7 +27,7 @@ use crate::{ }; use console::{prelude::*, types::Field}; use ledger_authority::Authority; -use ledger_block::{Header, Ratifications}; +use ledger_block::{Header, Ratifications, Rejected}; use ledger_coinbase::{CoinbaseSolution, PuzzleCommitment}; /// A RocksDB block storage. @@ -61,6 +61,8 @@ pub struct BlockDB { rejected_or_aborted_transaction_id_map: DataMap, /// The confirmed transactions map. confirmed_transactions_map: DataMap)>, + /// The rejected deployment or execution map. + rejected_deployment_or_execution_map: DataMap, Rejected>, /// The transaction store. transaction_store: TransactionStore>, } @@ -81,6 +83,7 @@ impl BlockStorage for BlockDB { type AbortedTransactionIDsMap = DataMap>; type RejectedOrAbortedTransactionIDMap = DataMap; type ConfirmedTransactionsMap = DataMap)>; + type RejectedDeploymentOrExecutionMap = DataMap, Rejected>; type TransactionStorage = TransactionDB; type TransitionStorage = TransitionDB; @@ -106,6 +109,7 @@ impl BlockStorage for BlockDB { aborted_transaction_ids_map: internal::RocksDB::open_map(N::ID, dev, MapID::Block(BlockMap::AbortedTransactionIDs))?, rejected_or_aborted_transaction_id_map: internal::RocksDB::open_map(N::ID, dev, MapID::Block(BlockMap::RejectedOrAbortedTransactionID))?, confirmed_transactions_map: internal::RocksDB::open_map(N::ID, dev, MapID::Block(BlockMap::ConfirmedTransactions))?, + rejected_deployment_or_execution_map: internal::RocksDB::open_map(N::ID, dev, MapID::Block(BlockMap::RejectedDeploymentOrExecution))?, transaction_store, }) } @@ -170,7 +174,7 @@ impl BlockStorage for BlockDB { &self.aborted_transaction_ids_map } - /// Returns the rejected or aborted transaction ID map. + /// Returns the rejected transaction ID or aborted transaction ID map. fn rejected_or_aborted_transaction_id_map(&self) -> &Self::RejectedOrAbortedTransactionIDMap { &self.rejected_or_aborted_transaction_id_map } @@ -180,6 +184,11 @@ impl BlockStorage for BlockDB { &self.confirmed_transactions_map } + /// Returns the rejected deployment or execution map. + fn rejected_deployment_or_execution_map(&self) -> &Self::RejectedDeploymentOrExecutionMap { + &self.rejected_deployment_or_execution_map + } + /// Returns the transaction store. fn transaction_store(&self) -> &TransactionStore { &self.transaction_store diff --git a/ledger/store/src/helpers/rocksdb/internal/id.rs b/ledger/store/src/helpers/rocksdb/internal/id.rs index c8562c6cc7..3091895661 100644 --- a/ledger/store/src/helpers/rocksdb/internal/id.rs +++ b/ledger/store/src/helpers/rocksdb/internal/id.rs @@ -70,6 +70,7 @@ pub enum BlockMap { AbortedTransactionIDs = DataID::BlockAbortedTransactionIDsMap as u16, RejectedOrAbortedTransactionID = DataID::BlockRejectedOrAbortedTransactionIDMap as u16, ConfirmedTransactions = DataID::BlockConfirmedTransactionsMap as u16, + RejectedDeploymentOrExecution = DataID::BlockRejectedDeploymentOrExecutionMap as u16, } /// The RocksDB map prefix for committee-related entries. @@ -269,6 +270,9 @@ enum DataID { ProgramIDMap, KeyValueMap, + // TODO (howardwu): For mainnet - Reorder this up above. + BlockRejectedDeploymentOrExecutionMap, + // Testing #[cfg(test)] Test, diff --git a/synthesizer/program/src/resources/credits.aleo b/synthesizer/program/src/resources/credits.aleo index 8e8c279dc1..534bb4ce86 100644 --- a/synthesizer/program/src/resources/credits.aleo +++ b/synthesizer/program/src/resources/credits.aleo @@ -789,11 +789,11 @@ function fee_private: input r1 as u64.public; // Input the priority fee amount. input r2 as u64.public; - // Input the deployment or execution root. + // Input the deployment or execution ID. input r3 as field.public; // Ensure the amount is nonzero. assert.neq r1 0u64; - // Ensure the deployment or execution root is nonzero. + // Ensure the deployment or execution ID is nonzero. assert.neq r3 0field; // Add the fee and priority fee amounts. add r1 r2 into r4; @@ -815,11 +815,11 @@ function fee_public: input r0 as u64.public; // Input the priority fee amount. input r1 as u64.public; - // Input the deployment or execution root. + // Input the deployment or execution ID. input r2 as field.public; // Ensure the amount is nonzero. assert.neq r0 0u64; - // Ensure the deployment or execution root is nonzero. + // Ensure the deployment or execution ID is nonzero. assert.neq r2 0field; // Add the fee and priority fee amounts. add r0 r1 into r3; diff --git a/synthesizer/src/vm/verify.rs b/synthesizer/src/vm/verify.rs index d94be9addc..5d2ac228b6 100644 --- a/synthesizer/src/vm/verify.rs +++ b/synthesizer/src/vm/verify.rs @@ -44,6 +44,13 @@ impl> VM { bail!("Transaction '{}' already exists in the ledger", transaction.id()) } + // Ensure the rejected ID is unique. + if let Some(rejected_id) = rejected_id { + if self.block_store().contains_rejected_deployment_or_execution_id(&rejected_id)? { + bail!("Transaction '{}' already exists in the ledger", rejected_id) + } + } + // Compute the Merkle root of the transaction. match transaction.to_root() { // Ensure the transaction ID is correct. @@ -95,7 +102,7 @@ impl> VM { Transaction::Deploy(id, owner, deployment, _) => { // Compute the deployment ID. let Ok(deployment_id) = deployment.to_deployment_id() else { - bail!("Failed to compute the Merkle root for deployment transaction '{id}'") + bail!("Failed to compute the Merkle root for a deployment transaction '{id}'") }; // Verify the signature corresponds to the transaction ID. ensure!(owner.verify(deployment_id), "Invalid owner signature for deployment transaction '{id}'"); @@ -110,7 +117,15 @@ impl> VM { // Verify the deployment. self.check_deployment_internal(deployment)?; } - Transaction::Execute(_, execution, _) => { + Transaction::Execute(id, execution, _) => { + // Compute the execution ID. + let Ok(execution_id) = execution.to_execution_id() else { + bail!("Failed to compute the Merkle root for an execution transaction '{id}'") + }; + // Ensure the execution was not previously rejected (replay attack prevention). + if self.block_store().contains_rejected_deployment_or_execution_id(&execution_id)? { + bail!("Transaction '{id}' contains a previously rejected execution") + } // Verify the execution. self.check_execution_internal(execution)?; } From 26a93fb68e406a2ec28b5ca3b175d487ab1df420 Mon Sep 17 00:00:00 2001 From: Howard Wu <9260812+howardwu@users.noreply.github.com> Date: Wed, 25 Oct 2023 19:46:54 -0700 Subject: [PATCH 2/4] Minor nit --- ledger/block/src/transaction/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ledger/block/src/transaction/mod.rs b/ledger/block/src/transaction/mod.rs index 10fa27de2e..fe1ace4c76 100644 --- a/ledger/block/src/transaction/mod.rs +++ b/ledger/block/src/transaction/mod.rs @@ -82,19 +82,19 @@ impl Transaction { impl Transaction { /// Returns `true` if the transaction is a deploy transaction. #[inline] - pub fn is_deploy(&self) -> bool { + pub const fn is_deploy(&self) -> bool { matches!(self, Self::Deploy(..)) } /// Returns `true` if the transaction is an execute transaction. #[inline] - pub fn is_execute(&self) -> bool { + pub const fn is_execute(&self) -> bool { matches!(self, Self::Execute(..)) } /// Returns `true` if the transaction is a fee transaction. #[inline] - pub fn is_fee(&self) -> bool { + pub const fn is_fee(&self) -> bool { matches!(self, Self::Fee(..)) } } From 1d621e8e2cc05549224467a38e67d60400e3c484 Mon Sep 17 00:00:00 2001 From: Howard Wu <9260812+howardwu@users.noreply.github.com> Date: Wed, 25 Oct 2023 19:48:48 -0700 Subject: [PATCH 3/4] nit --- ledger/store/src/block/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ledger/store/src/block/mod.rs b/ledger/store/src/block/mod.rs index 603b44d4be..eeee7c684d 100644 --- a/ledger/store/src/block/mod.rs +++ b/ledger/store/src/block/mod.rs @@ -514,8 +514,8 @@ pub trait BlockStorage: 'static + Clone + Send + Sync { None => Vec::new(), }; - // Retrieve the rejected transaction IDs, and deployment or execution. - let rejected_transaction_ids_and_deployment_or_execution = match self.get_block_transactions(block_hash)? { + // Retrieve the rejected transaction IDs, and the deployment or execution ID. + let rejected_transaction_ids_and_deployment_or_execution_id = match self.get_block_transactions(block_hash)? { Some(transactions) => transactions .iter() .filter(|tx| tx.is_rejected()) @@ -579,7 +579,7 @@ pub trait BlockStorage: 'static + Clone + Send + Sync { } // Remove the rejected state. - for (rejected_transaction_id, rejected_id) in rejected_transaction_ids_and_deployment_or_execution { + for (rejected_transaction_id, rejected_id) in rejected_transaction_ids_and_deployment_or_execution_id { // Remove the rejected transaction ID. self.rejected_or_aborted_transaction_id_map().remove(&rejected_transaction_id)?; // Remove the rejected deployment or execution. From 301573b0eef2764d4a90944193dedff6d408180a Mon Sep 17 00:00:00 2001 From: Howard Wu <9260812+howardwu@users.noreply.github.com> Date: Wed, 25 Oct 2023 20:12:04 -0700 Subject: [PATCH 4/4] Remove unnecessary check --- synthesizer/src/vm/verify.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/synthesizer/src/vm/verify.rs b/synthesizer/src/vm/verify.rs index 5d2ac228b6..99c921dec7 100644 --- a/synthesizer/src/vm/verify.rs +++ b/synthesizer/src/vm/verify.rs @@ -44,13 +44,6 @@ impl> VM { bail!("Transaction '{}' already exists in the ledger", transaction.id()) } - // Ensure the rejected ID is unique. - if let Some(rejected_id) = rejected_id { - if self.block_store().contains_rejected_deployment_or_execution_id(&rejected_id)? { - bail!("Transaction '{}' already exists in the ledger", rejected_id) - } - } - // Compute the Merkle root of the transaction. match transaction.to_root() { // Ensure the transaction ID is correct.