Skip to content

Commit

Permalink
refactor!: move transaction error out of block payload
Browse files Browse the repository at this point in the history
Signed-off-by: Marin Veršić <[email protected]>
  • Loading branch information
mversic committed Oct 21, 2024
1 parent 184c62a commit de63dec
Show file tree
Hide file tree
Showing 20 changed files with 340 additions and 472 deletions.
18 changes: 7 additions & 11 deletions crates/iroha/tests/tx_history.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ fn client_has_rejected_and_accepted_txs_should_return_tx_history() -> Result<()>

let transactions = client
.query(transaction::all())
.filter_with(|tx| tx.transaction.value.authority.eq(account_id.clone()))
.filter_with(|tx| tx.value.authority.eq(account_id.clone()))
.with_pagination(Pagination {
limit: Some(nonzero!(50_u64)),
offset: 1,
Expand All @@ -52,15 +52,11 @@ fn client_has_rejected_and_accepted_txs_should_return_tx_history() -> Result<()>
assert_eq!(transactions.len(), 50);

let mut prev_creation_time = core::time::Duration::from_millis(0);
transactions
.iter()
.map(AsRef::as_ref)
.map(AsRef::as_ref)
.for_each(|tx| {
assert_eq!(tx.authority(), &account_id);
//check sorted
assert!(tx.creation_time() >= prev_creation_time);
prev_creation_time = tx.creation_time();
});
transactions.iter().map(AsRef::as_ref).for_each(|tx| {
assert_eq!(tx.authority(), &account_id);
//check sorted
assert!(tx.creation_time() >= prev_creation_time);
prev_creation_time = tx.creation_time();
});
Ok(())
}
4 changes: 1 addition & 3 deletions crates/iroha_core/benches/blocks/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,7 @@ pub fn create_block<'a>(
.unwrap();

// Verify that transactions are valid
for tx in block.as_ref().transactions() {
assert_eq!(tx.error, None);
}
assert_eq!(block.as_ref().errors().count(), 0);

(block, state_block)
}
Expand Down
78 changes: 20 additions & 58 deletions crates/iroha_core/src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use iroha_data_model::{
block::*,
events::prelude::*,
peer::PeerId,
transaction::{error::TransactionRejectionReason, prelude::*},
transaction::{error::TransactionRejectionReason, SignedTransaction},
};
use thiserror::Error;

Expand Down Expand Up @@ -583,7 +583,7 @@ mod valid {
if block.transactions().any(|tx| {
state
.transactions()
.get(&tx.as_ref().hash())
.get(&tx.hash())
// In case of soft-fork transaction is check if it was added at the same height as candidate block
.is_some_and(|height| height.get() < expected_block_height)
}) {
Expand All @@ -606,7 +606,6 @@ mod valid {

let errors = block
.transactions()
.map(AsRef::as_ref)
// FIXME: Redundant clone
.cloned()
.enumerate()
Expand Down Expand Up @@ -859,7 +858,7 @@ mod valid {

let transactions = block.payload().transactions.as_slice();
for transaction in transactions {
if transaction.value.authority() != genesis_account {
if transaction.authority() != genesis_account {
return Err(InvalidGenesisError::UnexpectedAuthority);
}
}
Expand Down Expand Up @@ -1095,15 +1094,16 @@ mod event {
fn produce_events(&self) -> impl Iterator<Item = PipelineEventBox> {
let block_height = self.as_ref().header().height;

let tx_events = self.as_ref().transactions().map(move |tx| {
let status = tx.error.as_ref().map_or_else(
let block = self.as_ref();
let tx_events = block.transactions().enumerate().map(move |(idx, tx)| {
let status = block.error(idx).map_or_else(
|| TransactionStatus::Approved,
|error| TransactionStatus::Rejected(error.clone()),
|error| TransactionStatus::Rejected(Box::new(error.clone())),
);

TransactionEvent {
block_height: Some(block_height),
hash: tx.as_ref().hash(),
hash: tx.hash(),
status,
}
});
Expand Down Expand Up @@ -1216,23 +1216,8 @@ mod tests {
let valid_block = unverified_block.categorize(&mut state_block).unpack(|_| {});
state_block.commit();

// The first transaction should be confirmed
assert!(valid_block
.as_ref()
.transactions()
.next()
.unwrap()
.error
.is_none());

// The second transaction should be rejected
assert!(valid_block
.as_ref()
.transactions()
.nth(1)
.unwrap()
.error
.is_some());
// The 1st transaction should be confirmed and the 2nd rejected
assert_eq!(*valid_block.as_ref().errors().next().unwrap().0, 1);
}

#[tokio::test]
Expand Down Expand Up @@ -1297,23 +1282,10 @@ mod tests {
let valid_block = unverified_block.categorize(&mut state_block).unpack(|_| {});
state_block.commit();

// The first transaction should fail
assert!(valid_block
.as_ref()
.transactions()
.next()
.unwrap()
.error
.is_some());

// The third transaction should succeed
assert!(valid_block
.as_ref()
.transactions()
.nth(2)
.unwrap()
.error
.is_none());
// The 1st transaction should fail and 2nd succeed
let mut errors = valid_block.as_ref().errors();
assert_eq!(0, *errors.next().unwrap().0);
assert!(errors.next().is_none());
}

#[tokio::test]
Expand Down Expand Up @@ -1363,27 +1335,17 @@ mod tests {
let valid_block = unverified_block.categorize(&mut state_block).unpack(|_| {});
state_block.commit();

// The first transaction should be rejected
assert!(
valid_block
.as_ref()
.transactions()
.next()
.unwrap()
.error
.is_some(),
let mut errors = valid_block.as_ref().errors();
// The 1st transaction should be rejected
assert_eq!(
0,
*errors.next().unwrap().0,
"The first transaction should be rejected, as it contains `Fail`."
);

// The second transaction should be accepted
assert!(
valid_block
.as_ref()
.transactions()
.nth(1)
.unwrap()
.error
.is_none(),
errors.next().is_none(),
"The second transaction should be accepted."
);
}
Expand Down
11 changes: 2 additions & 9 deletions crates/iroha_core/src/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,8 @@ impl MetricsReporter {
break;
};
block_index += 1;
let mut block_txs_accepted = 0;
let mut block_txs_rejected = 0;
for tx in block.transactions() {
if tx.error.is_none() {
block_txs_accepted += 1;
} else {
block_txs_rejected += 1;
}
}
let block_txs_rejected = block.errors().count() as u64;
let block_txs_accepted = block.transactions().count() as u64 - block_txs_rejected;

self.metrics
.txs
Expand Down
31 changes: 11 additions & 20 deletions crates/iroha_core/src/smartcontracts/isi/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ use eyre::Result;
use iroha_data_model::{
prelude::*,
query::{
error::QueryExecutionFail as Error, parameters::QueryParams, QueryBox, QueryOutputBatchBox,
QueryRequest, QueryRequestWithAuthority, QueryResponse, SingularQueryBox,
SingularQueryOutputBox,
error::QueryExecutionFail as Error, parameters::QueryParams, CommittedTransaction,
QueryBox, QueryOutputBatchBox, QueryRequest, QueryRequestWithAuthority, QueryResponse,
SingularQueryBox, SingularQueryOutputBox,
},
};

Expand Down Expand Up @@ -68,7 +68,7 @@ impl SortableQueryOutput for RoleId {
}
}

impl SortableQueryOutput for TransactionQueryOutput {
impl SortableQueryOutput for CommittedTransaction {
fn get_metadata_sorting_key(&self, _key: &Name) -> Option<Json> {
None
}
Expand Down Expand Up @@ -576,15 +576,11 @@ mod tests {

assert_eq!(txs.len() as u64, num_blocks * 2);
assert_eq!(
txs.iter()
.filter(|txn| txn.transaction.error.is_some())
.count() as u64,
txs.iter().filter(|txn| txn.error.is_some()).count() as u64,
num_blocks
);
assert_eq!(
txs.iter()
.filter(|txn| txn.transaction.error.is_none())
.count() as u64,
txs.iter().filter(|txn| txn.error.is_none()).count() as u64,
num_blocks
);

Expand Down Expand Up @@ -638,9 +634,7 @@ mod tests {

let not_found = FindTransactions::new()
.execute(
TransactionQueryOutputPredicateBox::build(|tx| {
tx.transaction.value.hash.eq(wrong_hash)
}),
CommittedTransactionPredicateBox::build(|tx| tx.value.hash.eq(wrong_hash)),
&state_view,
)
.expect("Query execution should not fail")
Expand All @@ -649,20 +643,17 @@ mod tests {

let found_accepted = FindTransactions::new()
.execute(
TransactionQueryOutputPredicateBox::build(|tx| {
tx.transaction.value.hash.eq(va_tx.as_ref().hash())
CommittedTransactionPredicateBox::build(|tx| {
tx.value.hash.eq(va_tx.as_ref().hash())
}),
&state_view,
)
.expect("Query execution should not fail")
.next()
.expect("Query should return a transaction");

if found_accepted.transaction.error.is_none() {
assert_eq!(
va_tx.as_ref().hash(),
found_accepted.as_ref().as_ref().hash()
)
if found_accepted.error.is_none() {
assert_eq!(va_tx.as_ref().hash(), found_accepted.as_ref().hash())
}
Ok(())
}
Expand Down
36 changes: 21 additions & 15 deletions crates/iroha_core/src/smartcontracts/isi/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,10 @@ use iroha_data_model::{
prelude::*,
query::{
error::QueryExecutionFail,
predicate::{
predicate_atoms::block::TransactionQueryOutputPredicateBox, CompoundPredicate,
},
TransactionQueryOutput,
predicate::{predicate_atoms::block::CommittedTransactionPredicateBox, CompoundPredicate},
CommittedTransaction,
},
transaction::CommittedTransaction,
transaction::error::TransactionRejectionReason,
};
use iroha_telemetry::metrics;
use nonzero_ext::nonzero;
Expand Down Expand Up @@ -51,28 +49,36 @@ impl BlockTransactionRef {
self.0.hash()
}

fn value(&self) -> CommittedTransaction {
self.0
.transactions()
.nth(self.1)
.expect("The transaction is not found")
.clone()
fn value(&self) -> (SignedTransaction, Option<TransactionRejectionReason>) {
(
self.0
.transactions()
.nth(self.1)
.expect("INTERNAL BUG: The transaction is not found")
.clone(),
self.0.error(self.1).cloned(),
)
}
}

impl ValidQuery for FindTransactions {
#[metrics(+"find_transactions")]
fn execute(
self,
filter: CompoundPredicate<TransactionQueryOutputPredicateBox>,
filter: CompoundPredicate<CommittedTransactionPredicateBox>,
state_ro: &impl StateReadOnly,
) -> Result<impl Iterator<Item = Self::Item>, QueryExecutionFail> {
Ok(state_ro
.all_blocks(nonzero!(1_usize))
.flat_map(BlockTransactionIter::new)
.map(|tx| TransactionQueryOutput {
block_hash: tx.block_hash(),
transaction: tx.value(),
.map(|tx| {
let (value, error) = tx.value();

CommittedTransaction {
block_hash: tx.block_hash(),
value,
error,
}
})
.filter(move |tx| filter.applies(tx)))
}
Expand Down
12 changes: 5 additions & 7 deletions crates/iroha_core/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1329,15 +1329,14 @@ impl<'state> StateBlock<'state> {
/// # Errors
/// Fails if transaction instruction execution fails
fn execute_transactions(&mut self, block: &CommittedBlock) -> Result<()> {
let block = block.as_ref();

// TODO: Should this block panic instead?
for tx in block.as_ref().transactions() {
if tx.error.is_none() {
for (idx, tx) in block.transactions().enumerate() {
if block.error(idx).is_none() {
// Execute every tx in it's own transaction
let mut transaction = self.transaction();
transaction.process_executable(
tx.as_ref().instructions(),
tx.as_ref().authority().clone(),
)?;
transaction.process_executable(tx.instructions(), tx.authority().clone())?;
transaction.apply();
}
}
Expand Down Expand Up @@ -1369,7 +1368,6 @@ impl<'state> StateBlock<'state> {
block
.as_ref()
.transactions()
.map(|tx| &tx.value)
.map(SignedTransaction::hash)
.for_each(|tx_hash| {
self.transactions.insert(tx_hash, block_height);
Expand Down
Loading

0 comments on commit de63dec

Please sign in to comment.