Skip to content

Commit

Permalink
Merge pull request #2130 from AleoHQ/feat/rejected_map
Browse files Browse the repository at this point in the history
Adds replay prevention on rejected executions
  • Loading branch information
howardwu authored Oct 26, 2023
2 parents 18fdfd8 + 301573b commit d17d7e2
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 29 deletions.
6 changes: 3 additions & 3 deletions ledger/block/src/transaction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,19 +82,19 @@ impl<N: Network> Transaction<N> {
impl<N: Network> Transaction<N> {
/// 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(..))
}
}
Expand Down
73 changes: 58 additions & 15 deletions ledger/store/src/block/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,21 +62,22 @@ pub enum ConfirmedTxType {
}

/// Separates the confirmed transaction into a tuple.
#[allow(clippy::type_complexity)]
fn to_confirmed_tuple<N: Network>(
confirmed: ConfirmedTransaction<N>,
) -> Result<(ConfirmedTxType, Transaction<N>, Vec<u8>)> {
) -> Result<(ConfirmedTxType, Transaction<N>, Vec<u8>, Option<Rejected<N>>)> {
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.
Expand All @@ -92,7 +93,7 @@ fn to_confirmed_tuple<N: Network>(
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.
Expand All @@ -108,7 +109,7 @@ fn to_confirmed_tuple<N: Network>(
finalize.write_le(&mut blob)?;

// Return the confirmed tuple.
Ok((ConfirmedTxType::RejectedExecute(index), tx, blob))
Ok((ConfirmedTxType::RejectedExecute(index), tx, blob, Some(rejected)))
}
}
}
Expand Down Expand Up @@ -195,7 +196,11 @@ pub trait BlockStorage<N: Network>: '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<u8>` to `Vec<FinalizeOps>`.
type ConfirmedTransactionsMap: for<'a> Map<'a, N::TransactionID, (N::BlockHash, ConfirmedTxType, Vec<u8>)>;
/// The rejected deployment or execution map.
type RejectedDeploymentOrExecutionMap: for<'a> Map<'a, Field<N>, Rejected<N>>;
/// The transaction storage.
type TransactionStorage: TransactionStorage<N, TransitionStorage = Self::TransitionStorage>;
/// The transition storage.
Expand Down Expand Up @@ -232,6 +237,8 @@ pub trait BlockStorage<N: Network>: '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<N, Self::TransactionStorage>;

Expand Down Expand Up @@ -261,6 +268,7 @@ pub trait BlockStorage<N: Network>: '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();
}

Expand All @@ -280,6 +288,7 @@ pub trait BlockStorage<N: Network>: '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()
}

Expand All @@ -299,6 +308,7 @@ pub trait BlockStorage<N: Network>: '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();
}

Expand All @@ -318,6 +328,7 @@ pub trait BlockStorage<N: Network>: '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();
}

Expand All @@ -337,6 +348,7 @@ pub trait BlockStorage<N: Network>: '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();
}

Expand All @@ -356,6 +368,7 @@ pub trait BlockStorage<N: Network>: '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();
}

Expand All @@ -375,6 +388,7 @@ pub trait BlockStorage<N: Network>: '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()
}

Expand Down Expand Up @@ -454,9 +468,13 @@ pub trait BlockStorage<N: Network>: '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)?;
}
Expand Down Expand Up @@ -496,12 +514,12 @@ pub trait BlockStorage<N: Network>: '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 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())
.map(|tx| tx.to_unconfirmed_transaction_id())
.map(|tx| Ok((tx.to_unconfirmed_transaction_id()?, tx.to_rejected_id()?)))
.collect::<Result<Vec<_>>>()?,
None => Vec::new(),
};
Expand Down Expand Up @@ -560,9 +578,14 @@ pub trait BlockStorage<N: Network>: '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_id {
// 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.
Expand All @@ -577,6 +600,22 @@ pub trait BlockStorage<N: Network>: 'static + Clone + Send + Sync {
})
}

/// Returns `true` if the given transaction ID exists.
fn contains_transaction_id(&self, transaction_id: &N::TransactionID) -> Result<bool> {
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<bool> {
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<N>) -> Result<bool> {
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<Option<u32>> {
match self.reverse_state_root_map().get_confirmed(&state_root)? {
Expand Down Expand Up @@ -1240,13 +1279,17 @@ impl<N: Network, B: BlockStorage<N>> BlockStore<N, B> {

/// Returns `true` if the given transaction ID exists.
pub fn contains_transaction_id(&self, transaction_id: &N::TransactionID) -> Result<bool> {
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<bool> {
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<N>) -> Result<bool> {
self.storage.contains_rejected_deployment_or_execution_id(rejected_id)
}

/// Returns `true` if the given certificate ID exists.
Expand Down
15 changes: 12 additions & 3 deletions ledger/store/src/helpers/memory/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -51,10 +51,12 @@ pub struct BlockMemory<N: Network> {
transactions_map: MemoryMap<N::BlockHash, Vec<N::TransactionID>>,
/// The aborted transaction IDs map.
aborted_transaction_ids_map: MemoryMap<N::BlockHash, Vec<N::TransactionID>>,
/// The rejected or aborted transaction ID map.
/// The rejected transaction ID or aborted transaction ID map.
rejected_or_aborted_transaction_id_map: MemoryMap<N::TransactionID, N::BlockHash>,
/// The confirmed transactions map.
confirmed_transactions_map: MemoryMap<N::TransactionID, (N::BlockHash, ConfirmedTxType, Vec<u8>)>,
/// The rejected deployment or execution map.
rejected_deployment_or_execution_map: MemoryMap<Field<N>, Rejected<N>>,
/// The transaction store.
transaction_store: TransactionStore<N, TransactionMemory<N>>,
}
Expand All @@ -75,6 +77,7 @@ impl<N: Network> BlockStorage<N> for BlockMemory<N> {
type AbortedTransactionIDsMap = MemoryMap<N::BlockHash, Vec<N::TransactionID>>;
type RejectedOrAbortedTransactionIDMap = MemoryMap<N::TransactionID, N::BlockHash>;
type ConfirmedTransactionsMap = MemoryMap<N::TransactionID, (N::BlockHash, ConfirmedTxType, Vec<u8>)>;
type RejectedDeploymentOrExecutionMap = MemoryMap<Field<N>, Rejected<N>>;
type TransactionStorage = TransactionMemory<N>;
type TransitionStorage = TransitionMemory<N>;

Expand All @@ -100,6 +103,7 @@ impl<N: Network> BlockStorage<N> for BlockMemory<N> {
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,
})
}
Expand Down Expand Up @@ -164,7 +168,7 @@ impl<N: Network> BlockStorage<N> for BlockMemory<N> {
&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
}
Expand All @@ -174,6 +178,11 @@ impl<N: Network> BlockStorage<N> for BlockMemory<N> {
&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<N, Self::TransactionStorage> {
&self.transaction_store
Expand Down
13 changes: 11 additions & 2 deletions ledger/store/src/helpers/rocksdb/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -61,6 +61,8 @@ pub struct BlockDB<N: Network> {
rejected_or_aborted_transaction_id_map: DataMap<N::TransactionID, N::BlockHash>,
/// The confirmed transactions map.
confirmed_transactions_map: DataMap<N::TransactionID, (N::BlockHash, ConfirmedTxType, Vec<u8>)>,
/// The rejected deployment or execution map.
rejected_deployment_or_execution_map: DataMap<Field<N>, Rejected<N>>,
/// The transaction store.
transaction_store: TransactionStore<N, TransactionDB<N>>,
}
Expand All @@ -81,6 +83,7 @@ impl<N: Network> BlockStorage<N> for BlockDB<N> {
type AbortedTransactionIDsMap = DataMap<N::BlockHash, Vec<N::TransactionID>>;
type RejectedOrAbortedTransactionIDMap = DataMap<N::TransactionID, N::BlockHash>;
type ConfirmedTransactionsMap = DataMap<N::TransactionID, (N::BlockHash, ConfirmedTxType, Vec<u8>)>;
type RejectedDeploymentOrExecutionMap = DataMap<Field<N>, Rejected<N>>;
type TransactionStorage = TransactionDB<N>;
type TransitionStorage = TransitionDB<N>;

Expand All @@ -106,6 +109,7 @@ impl<N: Network> BlockStorage<N> for BlockDB<N> {
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,
})
}
Expand Down Expand Up @@ -170,7 +174,7 @@ impl<N: Network> BlockStorage<N> for BlockDB<N> {
&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
}
Expand All @@ -180,6 +184,11 @@ impl<N: Network> BlockStorage<N> for BlockDB<N> {
&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<N, Self::TransactionStorage> {
&self.transaction_store
Expand Down
4 changes: 4 additions & 0 deletions ledger/store/src/helpers/rocksdb/internal/id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -269,6 +270,9 @@ enum DataID {
ProgramIDMap,
KeyValueMap,

// TODO (howardwu): For mainnet - Reorder this up above.
BlockRejectedDeploymentOrExecutionMap,

// Testing
#[cfg(test)]
Test,
Expand Down
8 changes: 4 additions & 4 deletions synthesizer/program/src/resources/credits.aleo
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
Loading

0 comments on commit d17d7e2

Please sign in to comment.