Skip to content

Commit

Permalink
Merge pull request #2118 from AleoHQ/feat/store-transaction
Browse files Browse the repository at this point in the history
Updates the get_transaction interface to support confirmed and unconfirmed IDs
  • Loading branch information
howardwu authored Oct 25, 2023
2 parents b9f3fcb + 0e64285 commit 10dcbdf
Show file tree
Hide file tree
Showing 6 changed files with 188 additions and 14 deletions.
14 changes: 9 additions & 5 deletions algorithms/src/fft/polynomial/dense.rs
Original file line number Diff line number Diff line change
Expand Up @@ -420,11 +420,15 @@ impl<F: Field> CheckedDiv for DensePolynomial<F> {
fn checked_div(&self, divisor: &DensePolynomial<F>) -> Option<DensePolynomial<F>> {
let a: Polynomial<_> = self.into();
let b: Polynomial<_> = divisor.into();
let res = a.divide_with_q_and_r(&b);
if let Some((divisor, remainder)) = res {
if remainder.is_zero() { Some(divisor) } else { None }
} else {
None
match a.divide_with_q_and_r(&b) {
Some((divisor, remainder)) => {
if remainder.is_zero() {
Some(divisor)
} else {
None
}
}
None => None,
}
}
}
Expand Down
10 changes: 6 additions & 4 deletions fields/src/fp6_3over2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -428,10 +428,12 @@ impl<P: Fp6Parameters> Ord for Fp6<P> {
let c2_cmp = self.c2.cmp(&other.c2);
let c1_cmp = self.c1.cmp(&other.c1);
let c0_cmp = self.c0.cmp(&other.c0);
if c2_cmp == Ordering::Equal {
if c1_cmp == Ordering::Equal { c0_cmp } else { c1_cmp }
} else {
c2_cmp
match c2_cmp {
Ordering::Equal => match c1_cmp {
Ordering::Equal => c0_cmp,
_ => c1_cmp,
},
_ => c2_cmp,
}
}
}
Expand Down
47 changes: 46 additions & 1 deletion ledger/block/src/transactions/confirmed/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,11 @@ impl<N: Network> ConfirmedTransaction<N> {
pub const fn is_rejected(&self) -> bool {
!self.is_accepted()
}

/// Returns `true` if the confirmed transaction represents the given unconfirmed transaction ID.
pub fn contains_unconfirmed_transaction_id(&self, unconfirmed_transaction_id: &N::TransactionID) -> bool {
self.to_unconfirmed_transaction_id().map_or(false, |id| &id == unconfirmed_transaction_id)
}
}

impl<N: Network> ConfirmedTransaction<N> {
Expand Down Expand Up @@ -431,10 +436,11 @@ pub mod test_helpers {

#[cfg(test)]
mod test {

use super::*;
use crate::transactions::confirmed::test_helpers;

type CurrentNetwork = console::network::Testnet3;

#[test]
fn test_accepted_execute() {
let rng = &mut TestRng::default();
Expand Down Expand Up @@ -470,6 +476,45 @@ mod test {
assert!(confirmed.is_err());
}

#[test]
fn test_contains_unconfirmed_transaction_id() {
let rng = &mut TestRng::default();

// A helper function to check that the unconfirmed transaction ID is correct.
let check_contains_unconfirmed_transaction_id = |confirmed: ConfirmedTransaction<CurrentNetwork>| {
let rng = &mut TestRng::default();
let unconfirmed_transaction_id = confirmed.to_unconfirmed_transaction_id().unwrap();
assert!(confirmed.contains_unconfirmed_transaction_id(&unconfirmed_transaction_id));
assert!(!confirmed.contains_unconfirmed_transaction_id(&<CurrentNetwork as Network>::TransactionID::from(
Field::rand(rng)
)));
};

// Ensure that the unconfirmed transaction ID of an accepted deployment is equivalent to its confirmed transaction ID.
let accepted_deploy = test_helpers::sample_accepted_deploy(Uniform::rand(rng), true, rng);
check_contains_unconfirmed_transaction_id(accepted_deploy);
let accepted_deploy = test_helpers::sample_accepted_deploy(Uniform::rand(rng), false, rng);
check_contains_unconfirmed_transaction_id(accepted_deploy);

// Ensure that the unconfirmed transaction ID of an accepted execute is equivalent to its confirmed transaction ID.
let accepted_execution = test_helpers::sample_accepted_execute(Uniform::rand(rng), true, rng);
check_contains_unconfirmed_transaction_id(accepted_execution);
let accepted_execution = test_helpers::sample_accepted_execute(Uniform::rand(rng), false, rng);
check_contains_unconfirmed_transaction_id(accepted_execution);

// Ensure that the unconfirmed transaction ID of a rejected deployment is not equivalent to its confirmed transaction ID.
let rejected_deploy = test_helpers::sample_rejected_deploy(Uniform::rand(rng), true, rng);
check_contains_unconfirmed_transaction_id(rejected_deploy);
let rejected_deploy = test_helpers::sample_rejected_deploy(Uniform::rand(rng), false, rng);
check_contains_unconfirmed_transaction_id(rejected_deploy);

// Ensure that the unconfirmed transaction ID of a rejected execute is not equivalent to its confirmed transaction ID.
let rejected_execution = test_helpers::sample_rejected_execute(Uniform::rand(rng), true, rng);
check_contains_unconfirmed_transaction_id(rejected_execution);
let rejected_execution = test_helpers::sample_rejected_execute(Uniform::rand(rng), false, rng);
check_contains_unconfirmed_transaction_id(rejected_execution);
}

#[test]
fn test_unconfirmed_transaction_ids() {
let rng = &mut TestRng::default();
Expand Down
8 changes: 8 additions & 0 deletions ledger/block/src/transactions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,14 @@ impl<N: Network> Transactions<N> {
}

impl<N: Network> Transactions<N> {
/// Returns the confirmed transaction for the given unconfirmed transaction ID, if it exists.
pub fn find_confirmed_transaction_for_unconfirmed_transaction_id(
&self,
unconfirmed_transaction_id: &N::TransactionID,
) -> Option<&ConfirmedTransaction<N>> {
cfg_find!(self.transactions, unconfirmed_transaction_id, contains_unconfirmed_transaction_id)
}

/// Returns the transaction with the given transition ID, if it exists.
pub fn find_transaction_for_transition_id(&self, transition_id: &N::TransitionID) -> Option<&Transaction<N>> {
cfg_find!(self.transactions, transition_id, contains_transition).map(|tx| tx.transaction())
Expand Down
13 changes: 11 additions & 2 deletions ledger/src/get.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ impl<N: Network, C: ConsensusStorage<N>> Ledger<N, C> {
/// Returns the transaction for the given transaction ID.
pub fn get_transaction(&self, transaction_id: N::TransactionID) -> Result<Transaction<N>> {
// Retrieve the transaction.
match self.vm.transaction_store().get_transaction(&transaction_id)? {
match self.vm.block_store().get_transaction(&transaction_id)? {
Some(transaction) => Ok(transaction),
None => bail!("Missing transaction for ID {transaction_id}"),
}
Expand All @@ -182,9 +182,18 @@ impl<N: Network, C: ConsensusStorage<N>> Ledger<N, C> {
}
}

/// Returns the unconfirmed transaction for the given `transaction ID`.
pub fn get_unconfirmed_transaction(&self, transaction_id: &N::TransactionID) -> Result<Transaction<N>> {
// Retrieve the unconfirmed transaction.
match self.vm.block_store().get_unconfirmed_transaction(transaction_id)? {
Some(unconfirmed_transaction) => Ok(unconfirmed_transaction),
None => bail!("Missing unconfirmed transaction for ID {transaction_id}"),
}
}

/// Returns the program for the given program ID.
pub fn get_program(&self, program_id: ProgramID<N>) -> Result<Program<N>> {
match self.vm.transaction_store().get_program(&program_id)? {
match self.vm.block_store().get_program(&program_id)? {
Some(program) => Ok(program),
None => bail!("Missing program for ID {program_id}"),
}
Expand Down
110 changes: 108 additions & 2 deletions ledger/store/src/block/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -838,23 +838,59 @@ pub trait BlockStorage<N: Network>: 'static + Clone + Send + Sync {
}
}

/// Returns the transaction for the given `transaction ID`.
fn get_transaction(&self, transaction_id: &N::TransactionID) -> Result<Option<Transaction<N>>> {
// Check if the transaction was rejected or aborted.
// Note: We can only retrieve accepted or rejected transactions. We cannot retrieve aborted transactions.
match self.rejected_or_aborted_transaction_id_map().get_confirmed(transaction_id)? {
Some(block_hash) => match self.get_block_transactions(&block_hash)? {
Some(transactions) => {
match transactions.find_confirmed_transaction_for_unconfirmed_transaction_id(transaction_id) {
Some(confirmed) => Ok(Some(confirmed.transaction().clone())),
None => bail!("Missing transaction '{transaction_id}' in block storage"),
}
}
None => bail!("Missing transactions for block '{block_hash}' in block storage"),
},
None => self.transaction_store().get_transaction(transaction_id),
}
}

/// Returns the confirmed transaction for the given `transaction ID`.
fn get_confirmed_transaction(&self, transaction_id: N::TransactionID) -> Result<Option<ConfirmedTransaction<N>>> {
// Retrieve the transaction.
let transaction = match self.transaction_store().get_transaction(&transaction_id) {
let transaction = match self.get_transaction(&transaction_id) {
Ok(Some(transaction)) => transaction,
Ok(None) => bail!("Missing transaction '{transaction_id}' in block storage"),
Err(err) => return Err(err),
};
// Retrieve the confirmed attributes.
let (_, confirmed_type, blob) = match self.confirmed_transactions_map().get_confirmed(&transaction_id)? {
let (_, confirmed_type, blob) = match self.confirmed_transactions_map().get_confirmed(&transaction.id())? {
Some(confirmed_attributes) => cow_to_cloned!(confirmed_attributes),
None => bail!("Missing confirmed transaction '{transaction_id}' in block storage"),
};
// Construct the confirmed transaction.
to_confirmed_transaction(confirmed_type, transaction, blob).map(Some)
}

/// Returns the unconfirmed transaction for the given `transaction ID`.
fn get_unconfirmed_transaction(&self, transaction_id: &N::TransactionID) -> Result<Option<Transaction<N>>> {
// Check if the transaction was rejected or aborted.
// Note: We can only retrieve accepted or rejected transactions. We cannot retrieve aborted transactions.
match self.rejected_or_aborted_transaction_id_map().get_confirmed(transaction_id)? {
Some(block_hash) => match self.get_block_transactions(&block_hash)? {
Some(transactions) => {
match transactions.find_confirmed_transaction_for_unconfirmed_transaction_id(transaction_id) {
Some(confirmed) => Ok(Some(confirmed.to_unconfirmed_transaction()?)),
None => bail!("Missing transaction '{transaction_id}' in block storage"),
}
}
None => bail!("Missing transactions for block '{block_hash}' in block storage"),
},
None => self.transaction_store().get_transaction(transaction_id),
}
}

/// Returns the block for the given `block hash`.
fn get_block(&self, block_hash: &N::BlockHash) -> Result<Option<Block<N>>> {
// Retrieve the block height.
Expand Down Expand Up @@ -1152,6 +1188,11 @@ impl<N: Network, B: BlockStorage<N>> BlockStore<N, B> {
self.storage.get_block_aborted_transaction_ids(block_hash)
}

/// Returns the transaction for the given `transaction ID`.
pub fn get_transaction(&self, transaction_id: &N::TransactionID) -> Result<Option<Transaction<N>>> {
self.storage.get_transaction(transaction_id)
}

/// Returns the confirmed transaction for the given `transaction ID`.
pub fn get_confirmed_transaction(
&self,
Expand All @@ -1160,6 +1201,11 @@ impl<N: Network, B: BlockStorage<N>> BlockStore<N, B> {
self.storage.get_confirmed_transaction(*transaction_id)
}

/// Returns the unconfirmed transaction for the given `transaction ID`.
pub fn get_unconfirmed_transaction(&self, transaction_id: &N::TransactionID) -> Result<Option<Transaction<N>>> {
self.storage.get_unconfirmed_transaction(transaction_id)
}

/// Returns the block for the given `block hash`.
pub fn get_block(&self, block_hash: &N::BlockHash) -> Result<Option<Block<N>>> {
self.storage.get_block(block_hash)
Expand Down Expand Up @@ -1307,4 +1353,64 @@ mod tests {
assert_eq!(None, candidate);
}
}

#[test]
fn test_get_transaction() {
let rng = &mut TestRng::default();

// Sample the block.
let block = ledger_test_helpers::sample_genesis_block(rng);
assert!(block.transactions().num_accepted() > 0, "This test must be run with at least one transaction.");

// Initialize a new block store.
let block_store = BlockStore::<CurrentNetwork, BlockMemory<_>>::open(None).unwrap();
// Insert the block.
block_store.insert(&block).unwrap();

for confirmed in block.transactions().clone().into_iter() {
// Retrieve the transaction.
assert_eq!(block_store.get_transaction(&confirmed.id()).unwrap().unwrap(), confirmed.into_transaction());
}
}

#[test]
fn test_get_confirmed_transaction() {
let rng = &mut TestRng::default();

// Sample the block.
let block = ledger_test_helpers::sample_genesis_block(rng);
assert!(block.transactions().num_accepted() > 0, "This test must be run with at least one transaction.");

// Initialize a new block store.
let block_store = BlockStore::<CurrentNetwork, BlockMemory<_>>::open(None).unwrap();
// Insert the block.
block_store.insert(&block).unwrap();

for confirmed in block.transactions().clone().into_iter() {
// Retrieve the transaction.
assert_eq!(block_store.get_confirmed_transaction(&confirmed.id()).unwrap().unwrap(), confirmed);
}
}

#[test]
fn test_get_unconfirmed_transaction() {
let rng = &mut TestRng::default();

// Sample the block.
let block = ledger_test_helpers::sample_genesis_block(rng);
assert!(block.transactions().num_accepted() > 0, "This test must be run with at least one transaction.");

// Initialize a new block store.
let block_store = BlockStore::<CurrentNetwork, BlockMemory<_>>::open(None).unwrap();
// Insert the block.
block_store.insert(&block).unwrap();

for confirmed in block.transactions().clone().into_iter() {
// Retrieve the transaction.
assert_eq!(
block_store.get_unconfirmed_transaction(&confirmed.id()).unwrap().unwrap(),
confirmed.to_unconfirmed_transaction().unwrap()
);
}
}
}

0 comments on commit 10dcbdf

Please sign in to comment.