-
Notifications
You must be signed in to change notification settings - Fork 20
chore: make sure rules validation returns a structured result #186
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
8353b00
3882293
5e7d747
952c0a1
ce6fa51
bf02cb6
e7906ab
2c67b6c
ef6b78d
a65f00a
bbd1f3c
90fce18
8193936
bd88eae
d38bfe4
5b349a9
3b31683
f769bd4
cb5c794
9a97099
a45baef
e47695d
9237a86
0608748
ec96b89
2e82638
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -38,3 +38,6 @@ coverage/ | |
|
|
||
| # Cardano node support files | ||
| /cardano-node-config/ | ||
|
|
||
| # llvm-cov generated file | ||
| lcov.info | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -16,76 +16,109 @@ pub mod body_size; | |||||||||||||||||||||||||||||||||||
| pub mod ex_units; | ||||||||||||||||||||||||||||||||||||
| pub mod header_size; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| pub use crate::rules::block::{ | ||||||||||||||||||||||||||||||||||||
| body_size::InvalidBlockSize, ex_units::InvalidExUnits, header_size::InvalidBlockHeader, | ||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||
| use crate::{ | ||||||||||||||||||||||||||||||||||||
| context::ValidationContext, | ||||||||||||||||||||||||||||||||||||
| rules::{transaction, transaction::InvalidTransaction}, | ||||||||||||||||||||||||||||||||||||
| state::FailedTransactions, | ||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||
| use amaru_kernel::{ | ||||||||||||||||||||||||||||||||||||
| protocol_parameters::ProtocolParameters, AuxiliaryData, HasExUnits, Hash, MintedBlock, | ||||||||||||||||||||||||||||||||||||
| protocol_parameters::ProtocolParameters, AuxiliaryData, ExUnits, HasExUnits, Hash, MintedBlock, | ||||||||||||||||||||||||||||||||||||
| OriginalHash, StakeCredential, TransactionPointer, | ||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||
| use std::ops::Deref; | ||||||||||||||||||||||||||||||||||||
| use thiserror::Error; | ||||||||||||||||||||||||||||||||||||
| use std::ops::{ControlFlow, Deref, FromResidual, Try}; | ||||||||||||||||||||||||||||||||||||
| use tracing::{instrument, Level}; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| #[derive(Debug, Error)] | ||||||||||||||||||||||||||||||||||||
| pub enum InvalidBlock { | ||||||||||||||||||||||||||||||||||||
| #[error("Invalid block's size: {0}")] | ||||||||||||||||||||||||||||||||||||
| Size(#[from] InvalidBlockSize), | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| #[error("Invalid block's execution units: {0}")] | ||||||||||||||||||||||||||||||||||||
| ExUnits(#[from] InvalidExUnits), | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| #[error("Invalid block header: {0}")] | ||||||||||||||||||||||||||||||||||||
| Header(#[from] InvalidBlockHeader), | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| #[error( | ||||||||||||||||||||||||||||||||||||
| "Invalid transaction (hash: {transaction_hash}, index: {transaction_index}): {violation} " | ||||||||||||||||||||||||||||||||||||
| )] | ||||||||||||||||||||||||||||||||||||
| #[derive(Debug)] | ||||||||||||||||||||||||||||||||||||
| pub enum InvalidBlockDetails { | ||||||||||||||||||||||||||||||||||||
| BlockSizeMismatch { | ||||||||||||||||||||||||||||||||||||
| supplied: usize, | ||||||||||||||||||||||||||||||||||||
| actual: usize, | ||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||
| TooManyExUnits { | ||||||||||||||||||||||||||||||||||||
| provided: ExUnits, | ||||||||||||||||||||||||||||||||||||
| max: ExUnits, | ||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||
| HeaderSizeTooBig { | ||||||||||||||||||||||||||||||||||||
| supplied: usize, | ||||||||||||||||||||||||||||||||||||
| max: usize, | ||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||
| Transaction { | ||||||||||||||||||||||||||||||||||||
| transaction_hash: Hash<32>, | ||||||||||||||||||||||||||||||||||||
| transaction_index: u32, | ||||||||||||||||||||||||||||||||||||
| violation: InvalidTransaction, | ||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| // TODO: This error shouldn't exist, it's a placeholder for better error handling in less straight forward cases | ||||||||||||||||||||||||||||||||||||
| #[error("Uncategorized error: {0}")] | ||||||||||||||||||||||||||||||||||||
| UncategorizedError(String), | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| #[derive(Debug)] | ||||||||||||||||||||||||||||||||||||
| pub enum BlockValidation { | ||||||||||||||||||||||||||||||||||||
| Valid, | ||||||||||||||||||||||||||||||||||||
| Invalid(InvalidBlockDetails), | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| impl Try for BlockValidation { | ||||||||||||||||||||||||||||||||||||
| type Output = (); | ||||||||||||||||||||||||||||||||||||
| type Residual = InvalidBlockDetails; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| fn from_output((): Self::Output) -> Self { | ||||||||||||||||||||||||||||||||||||
| BlockValidation::Valid | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| fn branch(self) -> ControlFlow<Self::Residual, Self::Output> { | ||||||||||||||||||||||||||||||||||||
| match self { | ||||||||||||||||||||||||||||||||||||
| BlockValidation::Valid => ControlFlow::Continue(()), | ||||||||||||||||||||||||||||||||||||
| BlockValidation::Invalid(e) => ControlFlow::Break(e), | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| impl FromResidual for BlockValidation { | ||||||||||||||||||||||||||||||||||||
| fn from_residual(residual: InvalidBlockDetails) -> Self { | ||||||||||||||||||||||||||||||||||||
| BlockValidation::Invalid(residual) | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| impl From<BlockValidation> for Result<(), InvalidBlockDetails> { | ||||||||||||||||||||||||||||||||||||
| fn from(validation: BlockValidation) -> Self { | ||||||||||||||||||||||||||||||||||||
| match validation { | ||||||||||||||||||||||||||||||||||||
| BlockValidation::Valid => Ok(()), | ||||||||||||||||||||||||||||||||||||
| BlockValidation::Invalid(e) => Err(e), | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| #[instrument(level = Level::TRACE, skip_all)] | ||||||||||||||||||||||||||||||||||||
| pub fn execute<C: ValidationContext<FinalState = S>, S: From<C>>( | ||||||||||||||||||||||||||||||||||||
| mut context: C, | ||||||||||||||||||||||||||||||||||||
| context: &mut C, | ||||||||||||||||||||||||||||||||||||
| protocol_params: ProtocolParameters, | ||||||||||||||||||||||||||||||||||||
| block: MintedBlock<'_>, | ||||||||||||||||||||||||||||||||||||
| ) -> Result<S, InvalidBlock> { | ||||||||||||||||||||||||||||||||||||
| block: &MintedBlock<'_>, | ||||||||||||||||||||||||||||||||||||
| ) -> BlockValidation { | ||||||||||||||||||||||||||||||||||||
| header_size::block_header_size_valid(block.header.raw_cbor(), &protocol_params)?; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| body_size::block_body_size_valid(&block.header.header_body, &block)?; | ||||||||||||||||||||||||||||||||||||
| body_size::block_body_size_valid(block)?; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| ex_units::block_ex_units_valid(block.ex_units(), &protocol_params)?; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| let failed_transactions = FailedTransactions::from_block(&block); | ||||||||||||||||||||||||||||||||||||
| let failed_transactions = FailedTransactions::from_block(block); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| let witness_sets = block.transaction_witness_sets.deref().to_vec(); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| let transactions = block.transaction_bodies.to_vec(); | ||||||||||||||||||||||||||||||||||||
| let transactions = block.transaction_bodies.deref().to_vec(); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| // using `zip` here instead of enumerate as it is safer to cast from u32 to usize than usize to u32 | ||||||||||||||||||||||||||||||||||||
| // Realistically, we're never gonna hit the u32 limit with the number of transactions in a block (a boy can dream) | ||||||||||||||||||||||||||||||||||||
| for (i, transaction) in (0u32..).zip(transactions.into_iter()) { | ||||||||||||||||||||||||||||||||||||
| let transaction_hash = transaction.original_hash(); | ||||||||||||||||||||||||||||||||||||
|
Comment on lines
104
to
111
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Avoid full
-let witness_sets = block.transaction_witness_sets.deref().to_vec();
-let transactions = block.transaction_bodies.deref().to_vec();
+let witness_sets = &block.transaction_witness_sets;
+let transactions = &block.transaction_bodies;Then rewrite the loop: -for (i, transaction) in (0u32..).zip(transactions.into_iter()) {
+for (i, (transaction, witness_set)) in transactions.iter().zip(witness_sets).enumerate() {
+ let i = i as u32;This keeps memory usage lean and sidesteps needless copies like Neo dodging bullets. 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| let witness_set = witness_sets | ||||||||||||||||||||||||||||||||||||
| .get(i as usize) | ||||||||||||||||||||||||||||||||||||
| .ok_or(InvalidBlock::UncategorizedError(format!( | ||||||||||||||||||||||||||||||||||||
| "Missing witness set for transaction index {}", | ||||||||||||||||||||||||||||||||||||
| i | ||||||||||||||||||||||||||||||||||||
| )))?; | ||||||||||||||||||||||||||||||||||||
| let witness_set = match witness_sets.get(i as usize) { | ||||||||||||||||||||||||||||||||||||
| Some(witness_set) => witness_set, | ||||||||||||||||||||||||||||||||||||
| None => { | ||||||||||||||||||||||||||||||||||||
| return BlockValidation::Invalid(InvalidBlockDetails::UncategorizedError(format!( | ||||||||||||||||||||||||||||||||||||
| "Witness set not found for transaction index {}", | ||||||||||||||||||||||||||||||||||||
| i | ||||||||||||||||||||||||||||||||||||
| ))); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| let auxiliary_data: Option<&AuxiliaryData> = block | ||||||||||||||||||||||||||||||||||||
| .auxiliary_data_set | ||||||||||||||||||||||||||||||||||||
|
|
@@ -108,21 +141,22 @@ pub fn execute<C: ValidationContext<FinalState = S>, S: From<C>>( | |||||||||||||||||||||||||||||||||||
| transaction_index: i as usize, // From u32 | ||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| transaction::execute( | ||||||||||||||||||||||||||||||||||||
| &mut context, | ||||||||||||||||||||||||||||||||||||
| if let Err(err) = transaction::execute( | ||||||||||||||||||||||||||||||||||||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||||||||||||||||||||||||||||||
| context, | ||||||||||||||||||||||||||||||||||||
| &protocol_params, | ||||||||||||||||||||||||||||||||||||
| pointer, | ||||||||||||||||||||||||||||||||||||
| !failed_transactions.has(i), | ||||||||||||||||||||||||||||||||||||
| transaction, | ||||||||||||||||||||||||||||||||||||
| witness_set, | ||||||||||||||||||||||||||||||||||||
| auxiliary_data, | ||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||
| .map_err(|err| InvalidBlock::Transaction { | ||||||||||||||||||||||||||||||||||||
| transaction_hash, | ||||||||||||||||||||||||||||||||||||
| transaction_index: i, | ||||||||||||||||||||||||||||||||||||
| violation: err, | ||||||||||||||||||||||||||||||||||||
| })?; | ||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||
| return BlockValidation::Invalid(InvalidBlockDetails::Transaction { | ||||||||||||||||||||||||||||||||||||
| transaction_hash, | ||||||||||||||||||||||||||||||||||||
| transaction_index: i, | ||||||||||||||||||||||||||||||||||||
| violation: err, | ||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| Ok(context.into()) | ||||||||||||||||||||||||||||||||||||
| BlockValidation::Valid | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not understand this comment about
map?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lastcan be replaced by next_back in theory here but in practice it fails some test. This implies that this call expectsmapto traverse all element by calling a function with side-effects (select_roll_forward).mapshould be called with pure functions as a way to construct another iterator.for_eachmight help clarify the expectation here (but at the price of a more contrived usage as the last element should be tracked).