Skip to content

Conversation

@golddydev
Copy link
Collaborator

@golddydev golddydev commented Dec 2, 2025

Description

This PR implements transaction phase 1 validation rule: UTxO rule for Shelley Era.

Related Issue(s)

Related to #329

How was this tested?

  • Add test cases for each UTxO rule
cargo test --package acropolis_module_tx_unpacker --lib -- validations::shelley::utxo::tests::shelley_test --nocapture 

Checklist

  • My code builds and passes local tests
  • I added/updated tests for my changes, where applicable
  • I updated documentation (if applicable)
  • CI is green for this PR

Impact / Side effects

No side effects

Reviewer notes / Areas to focus

Every UTxO rule validation lives under tx_unpacker/validations/shelley/utxo.rs

  • ValueNotConservedUTxO , UpdateFailure rules will be implemented by following PRs.
  • OutputBootAddrAttrsTooBig: For now, we only store raw payload for Byron address, should we decode all Byron addresses and validate this rule?

@golddydev golddydev marked this pull request as ready for review December 2, 2025 16:36
@golddydev golddydev linked an issue Dec 2, 2025 that may be closed by this pull request
14 tasks

/// **Cause:** Some of transaction inputs are not in current UTxOs set.
#[error("Bad inputs: bad_input={bad_input}, bad_input_index={bad_input_index}")]
BadInputsUTxO {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BadInputUTxO would be more appropriate in my opinion.

Copy link
Collaborator

@lowhung lowhung Dec 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, the Shelley Rules pdf (if we want to go by that as well) defines it as

In the case of any input not being valid, there is a BadInput failure.

I think it's worth being as semantically aligned with the Shelley ledger rules as we can be. Thoughts?

/// Validate transaction's TTL field
/// pass if ttl >= current_slot
/// Reference: https://github.com/IntersectMBO/cardano-ledger/blob/24ef1741c5e0109e4d73685a24d8e753e225656d/eras/shelley/impl/src/Cardano/Ledger/Shelley/Rules/Utxo.hs#L421
pub fn validate_time_to_live(tx: &alonzo::MintedTx, current_slot: u64) -> UTxOValidationResult {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my opinion TTL validation isn't related to UTxO validation.

Copy link
Collaborator

@lowhung lowhung Dec 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious why you think this? Looking at the ledger rules for UTxOs in Shelley, they say

The UTXO rule has ten predicate failures:

  • In the case of the current slot being greater than the time-to-live of the current transaction,
    there is an Expired failure.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wonder why it's UTxO-level if it only involves Tx-level fields

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that is true that ttl is transaction level.

The reason why I chose this is to use the same Error types (naming) and category as cardano-ledger so it is easier to match with the ledger code.

Or, should we update the naming and category as it makes sense to us?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you are correct to match cardano-ledger. Cardano-ledger made wacky decisions, but it makes sense to me to follow them.


/// Validate transaction size is under the limit
/// Reference: https://github.com/IntersectMBO/cardano-ledger/blob/24ef1741c5e0109e4d73685a24d8e753e225656d/eras/shelley/impl/src/Cardano/Ledger/Shelley/Rules/Utxo.hs#L575
pub fn validate_max_tx_size_utxo(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also wouldn't consider tx size validation as apart of UTxO validation.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm also curious why not here? This is also specified in the formal ledger rules for UTxO.

In the case that the transaction is too large, there is a MaxTxSize failure.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As Simon said above, tx size and TTL are both transaction level rules which are not specific to UTxOs (Although the size and number of UTxOs do count toward tx size).

Comment on lines 10 to 24
pub fn validate_shelley_tx<F>(
raw_tx: &[u8],
shelley_params: &ShelleyParams,
current_slot: u64,
lookup_by_hash: F,
) -> Result<(), TransactionValidationError>
where
F: Fn(TxOutRef) -> Result<TxIdentifier>,
{
let tx = MultiEraTx::decode_for_era(traverse::Era::Shelley, raw_tx)
.map_err(|e| TransactionValidationError::CborDecodeError(e.to_string()))?;
utxo::validate_shelley_tx(&tx, shelley_params, current_slot, lookup_by_hash).map_err(|e| *e)?;

Ok(())
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it makes more sense to call validate_shelley_tx when BlockInfo::Era >= Shelley in tx_unpacker.rs and passing in the already decoded tx.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But, some some checks (e.g. ttl check) are not available from Allegra era. (Will integrate this validate function to omnibus process in coming PR)

validate_wrong_network(transaction_body, network_id.clone())?;
validate_wrong_network_withdrawal(transaction_body, network_id.clone())?;
validate_output_too_small_utxo(transaction_body, shelley_params)?;
validate_max_tx_size_utxo(tx_size, shelley_params)?;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are two other rules I don't see here, but that are part of the Shelley Ledger rules for UTxOs, unless those are accounted for elsewhere? Those being

  1. OutputBootAddrAttrsTooBig -- output bootstrap address attributes too big

If the transaction creates any boostrap outputs whose attributes have size bigger than 64,
there is a OutputBootAddrAttrsTooBig failure.

In this case, Bootstrap (i.e. Byron) addresses have variable sized attributes in them. It is important to limit their overall size.

  1. ValueNotConservedUTxO - ensure that value consumed and produced matches up exactly

If the transaction does not correctly conserve the balance, there is a ValueNotConserved
failure.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see you have the latter accounted for here.


/// **Cause:** The transaction size is too big.
#[error("Max tx size: supplied={supplied}, max={max}")]
MaxTxSizeUTxO { supplied: u32, max: u32 },
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
MaxTxSizeUTxO { supplied: u32, max: u32 },
MaxTxSize { supplied: u32, max: u32 },

@golddydev golddydev merged commit 4327be9 into main Dec 3, 2025
2 checks passed
@golddydev golddydev deleted the gd/add-utxo-validations branch December 3, 2025 18:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants