feat: add Babbage phase-1 validations (#405)

This commit is contained in:
Maico Leberle 2024-02-20 20:30:09 -03:00 committed by GitHub
parent ab1fd882d6
commit 181a13c3d6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
32 changed files with 4584 additions and 143 deletions

View file

@ -1,32 +1,33 @@
# Alonzo phase-1 validation rules # Alonzo phase-1 validation rules
This document covers the Alonzo era. This document covers the concepts, notation and validation rules realted to phase-1 validation in the Alonzo ledger. For further information, refer to the [Alonzo ledger white paper](https://github.com/input-output-hk/cardano-ledger/releases/latest/download/alonzo-ledger.pdf). This document covers the terminology and equations related to the Babbage ledger phase-1 validation rules. For further information, refer to the [Alonzo ledger white paper](https://github.com/input-output-hk/cardano-ledger/releases/latest/download/alonzo-ledger.pdf).
## Definitions and notation ## Definitions and notation
- **Blocks**: - **Blocks**:
- ***Block*** is the set of all possible (not necessarily valid) Alonzo blocks. When clear, we will write ***block ∈ Blocks*** to refer to the current block being validated. - ***Block*** is the set of all possible (not necessarily valid) Alonzo blocks. We will write ***block*** to refer to the current block being validated.
- ***txs(block)*** is the set of transactions of the block. - ***txs(block)*** is the set of transactions of the block.
- ***blockExUnits(block) ∈ ExUnits***, where ***ExUnits := (, )***, is the memory and execution step units resulting from the sum of memory and execution step units of all its transactions. That is, ***blockExUnits(block) := (∑ tx ∈ txs(block): txExUnits(txWits(tx)))***, where addition of execution units is defined pointwise. - ***blockExUnits(block) ∈ ExUnits***, where ***ExUnits := (, )***, is the memory and execution step units resulting from the sum of memory and execution step units of all its transactions. That is, ***blockExUnits(block) := ∑ tx ∈ txs(block): txExUnits(txWits(tx))***, where addition of execution units is defined pointwise.
- **Transactions**: - **Transactions**:
- ***Tx*** is the set of all possible (not necessarily valid) Alonzo transactions, composed of a transaction body and a witness set. When clear, we will write ***tx*** to refer to the current transaction. - ***Tx*** is the type of Alonzo transactions, composed of a transaction body and a witness set. We will write ***tx*** to refer to the current transaction.
- ***txIsPhase1Valid(block, pps, tx) ∈ Bool*** indicates whether ***tx ∈ txs(block)*** is phase-1 valid under ***pps***. - ***txIsPhase1Valid(pps, tx) ∈ Bool*** indicates whether ***tx ∈ Tx*** is phase-1 valid for the Alonzo leger under ***pps***.
- ***TxBody*** is the type of Alonzo transaction bodies. Each transaction body is composed of a set of inputs, a list of outputs, and related data. - ***TxBody*** is the type of Alonzo transaction bodies. Each transaction body is composed of a set of inputs, a list of outputs, and other related data.
- ***txBody(tx)*** is the transaction body of the transaction. When clear, we will write ***txBody*** to refer to the transaction body of the current transaction. - ***txBody(tx)*** is the transaction body of the transaction. We will write ***txBody*** to refer to the transaction body of the current transaction.
- ***TxOut = Addr x Value x DatumHash*** is the set of transaction outputs, where - ***TxOut = Addr x Value x DatumHash*** is the type of transaction outputs, where
- ***Addr*** is the set of transaction output addresses. - ***Addr*** is the set of transaction output addresses.
- ***Value*** is the type of multi-asset Alonzo values. We define addition, equality comparisons and ordering comparisons for them in a point-wise manner. - ***Value*** is the type of multi-asset Alonzo values. We define addition, equality comparison and ordering comparisons for values in a point-wise manner.
- ***getValue(txOut) ∈ Value*** gives the value contained in the transaction output. - ***getValue(txOut) ∈ Value*** gives the value contained in the transaction output.
- ***isADAOnly : Value -> Bool*** indicates whether a value contains only ADA assets. - ***isADAOnly : Value -> Bool*** indicates whether a value contains only ADA assets.
- ***balance : P(TxOut) → Value*** gives the sum of all assets in a set of transaction outputs. - ***balance : P(TxOut) → Value*** gives the sum of all assets in a set of transaction outputs.
- ***adaValueOf : -> Value*** gives the ADA-only value representation of a natural number. - ***adaValueOf : -> Value*** gives the ADA-only value representation of a natural number.
- ***valSize : Value -> *** gives the size of a value in bytes, when serialized. - ***valSize : Value -> *** gives the size of a value in bytes, when serialized.
- ***policies(v)*** gives the set of policies of the assets of the value.
- ***DatumHash ⊆ Bytes*** is the type of hashes computed from datums. This field is optional. - ***DatumHash ⊆ Bytes*** is the type of hashes computed from datums. This field is optional.
- ***txOuts(txBody) ∈ P(TxOut)*** gives the list of transaction outputs of the transaction body. - ***txOuts(txBody) ∈ P(TxOut)*** gives the list of transaction outputs of the transaction body.
- ***balance : P(TxOut) → Value*** gives the sum of all multi-asset values in a set of transaction outputs. - ***balance : P(TxOut) → Value*** gives the sum of all multi-asset values in a set of transaction outputs.
- ***utxoEntrySize(txOut) ∈ *** gives the size of the transaction output when serialized, in bytes. - ***outputEntrySize(txOut) ∈ *** gives the size of the transaction output when serialized, in bytes.
- ***TxIn = TxId x Ix*** is the set of transaction inputs, where - ***TxIn = TxId x Ix*** is the set of transaction inputs, where
- ***TxId*** is the type of transaction IDs. - ***TxId*** is the type of transaction IDs.
- ***Ix = *** is the set of indices (used to refer to a specific transaction output). - ***Ix = *** is the set of indices, which are used to refer to a specific transaction output.
- ***txIns(txBody) ∈ P(TxIn)*** gives the set of *non-collateral* inputs of the transaction. - ***txIns(txBody) ∈ P(TxIn)*** gives the set of *non-collateral* inputs of the transaction.
- ***collateral(txBody) ∈ P(TxIn)*** gives the set of *collateral* inputs of the transaction. - ***collateral(txBody) ∈ P(TxIn)*** gives the set of *collateral* inputs of the transaction.
- ***txInsVKey(txBody) ∈ P(TxIn)*** gives the set of transaction inputs of the transaction which are verification-key locked. - ***txInsVKey(txBody) ∈ P(TxIn)*** gives the set of transaction inputs of the transaction which are verification-key locked.
@ -40,12 +41,12 @@ This document covers the Alonzo era. This document covers the concepts, notation
- ***PolicyID*** is the set of all possible policy IDs associated to multi-asset values. In particular, ***adaID ∈ Policy*** is the policy of lovelaces. - ***PolicyID*** is the set of all possible policy IDs associated to multi-asset values. In particular, ***adaID ∈ Policy*** is the policy of lovelaces.
- ***consumed(utxo, txBody) ∈ *** is the *consumed value* of the transaction, which equals the sum of all multi-asset values in the inputs of the transaction. - ***consumed(utxo, txBody) ∈ *** is the *consumed value* of the transaction, which equals the sum of all multi-asset values in the inputs of the transaction.
- ***produced(txBody) ∈ *** is the *produced value* of the transaction, which equals the sum of all multi-asset values in the outputs of the transaction, plus the transaction fee, plus the minted value. - ***produced(txBody) ∈ *** is the *produced value* of the transaction, which equals the sum of all multi-asset values in the outputs of the transaction, plus the transaction fee, plus the minted value.
- ***txNetId(txBody) ∈ NetworkID*** gives the network ID of a transaction (not to be confused with the network ID of addresses of unspent transaction outputs). - ***txNetId(txBody) ∈ NetworkID*** gives the network ID of a transaction (not to be confused with the network ID of addresses of unspent transaction outputs). This field is optional.
- ***txWits(tx)*** is the transaction witness set. When clear, we will write ***txWits*** to refer to the transaction witness set of the current transaction. - ***txWits(tx)*** is the transaction witness set. When clear, we will write ***txWits*** to refer to the transaction witness set of the current transaction.
- ***txExUnits(txWits) ∈ ExUnits*** is the total execution units of the transaction. - ***txExUnits(txWits) ∈ ExUnits*** is the total execution units of the transaction.
- ***txMD(tx)*** is the metadata of the transaction. - ***txAuxDat(tx)*** is the auxiliary data of the transaction.
- ***hashMD(md)*** is the result of hasing metadata ***md***. - ***hashMD(md)*** is the result of hasing auxiliary data ***md***.
- ***txMDHash(txBody)*** is the metadata hash contained within the transaction body. - ***txAuxDatHash(txBody)*** is the auxiliary data hash contained within the transaction body.
- **Addresses**: - **Addresses**:
- ***Addr*** is the set of all valid Alonzo addresses. - ***Addr*** is the set of all valid Alonzo addresses.
- ***hashAddr : Addr -> Bytes*** is the hashing function for addresses. - ***hashAddr : Addr -> Bytes*** is the hashing function for addresses.
@ -67,9 +68,9 @@ This document covers the Alonzo era. This document covers the concepts, notation
- ***keyHash: VKey -> KeyHash*** is the hashing function for verification keys, where ***KeyHash ⊆ Bytes*** - ***keyHash: VKey -> KeyHash*** is the hashing function for verification keys, where ***KeyHash ⊆ Bytes***
- **Scripts**: - **Scripts**:
- ***Script*** is the set of all Alonzo scripts: minting policies, native scripts and Plutus scripts. We will use the term *script* to refer to any of these kinds of scripts. - ***Script*** is the set of all Alonzo scripts: minting policies, native scripts and Plutus scripts. We will use the term *script* to refer to any of these kinds of scripts.
- ***isPlutusScript(script) ∈ Bool*** assesses whether a script is a Plutus one (that is, it is not a native script). - ***isPlutusScript(script) ∈ Bool*** assesses whether a script is a Plutus script (that is, it is not a native script).
- ***scriptDataHash(txBody) ∈ Bytes*** is the hash of script-related data (transaction redeemers and relevant protocol parameters). - ***scriptDataHash(txBody) ∈ Bytes*** is the hash of script-related data (transaction redeemers and relevant protocol parameters).
- ***hashScriptIntegrity : PParams -> P((Tag, Ix, Redeemer, ExUnits)) -> Languages -> P(DaAtum) -> Bytes*** hashes the protocol parameters and data relevant to script execution. - ***hashScriptIntegrity : PParams -> P((Tag, Ix, Redeemer, ExUnits)) -> Languages -> P(Datum) -> Bytes*** hashes the protocol parameters and data relevant to script execution.
- **Protocol Parameters**: - **Protocol Parameters**:
- We will write ***pps ∈ PParams*** to represent the set of Alonzo protocol parameters, each of which contains at least the following associated functions: - We will write ***pps ∈ PParams*** to represent the set of Alonzo protocol parameters, each of which contains at least the following associated functions:
- ***maxBlockExUnits(pps) ∈ ExUnits*** gives the maximum memory and execution step units for a block. - ***maxBlockExUnits(pps) ∈ ExUnits*** gives the maximum memory and execution step units for a block.
@ -81,10 +82,10 @@ This document covers the Alonzo era. This document covers the concepts, notation
- ***collateralPercent(pps) ∈ {0,...,100}*** gives the fee percentage (multiplied by 100) that all lovelace in collateral inputs should add up to. - ***collateralPercent(pps) ∈ {0,...,100}*** gives the fee percentage (multiplied by 100) that all lovelace in collateral inputs should add up to.
- ***coinsPerUTxOWord(pps) ∈ *** is the number of lovelace a UTxO should contain per byte (when serialized). This is used to assess the minimum number of lovelace that an unspent transaction output should lock. - ***coinsPerUTxOWord(pps) ∈ *** is the number of lovelace a UTxO should contain per byte (when serialized). This is used to assess the minimum number of lovelace that an unspent transaction output should lock.
- ***costModels : PParams -> (Languages -> CostModel)*** takes the protocol parameters and returns a map associating languages to their cost models. - ***costModels : PParams -> (Languages -> CostModel)*** takes the protocol parameters and returns a map associating languages to their cost models.
- ***Languages := {PlutusV1, PlutusV2}*** is the set of Alonzo languages. - ***Languages := {PlutusV1}*** is the set of Alonzo languages.
- ***CostModel*** is the set of cost models. - ***CostModel*** is the set of cost models.
- ***Witnesses***: - ***Witnesses***:
- ***TxWits*** is the set of all possible transaction witness set. - ***TxWits*** is the type of transaction witnesses.
- ***VKey*** is the set of verification keys (a.k.a. public keys). - ***VKey*** is the set of verification keys (a.k.a. public keys).
- ***SKey*** is the set of signing keys (a.k.a. private keys). - ***SKey*** is the set of signing keys (a.k.a. private keys).
- ***Sig*** is the set of signatures (i.e., the result of signing a byte array using a signing key). - ***Sig*** is the set of signatures (i.e., the result of signing a byte array using a signing key).
@ -97,21 +98,21 @@ This document covers the Alonzo era. This document covers the concepts, notation
- To all phase-1 validation purposes, we restrict ***Tag*** to ***Tag = {Mint, Spend}***. This is used to indicate whether a script is used on minting purposes (native scripts and minting policies), or should be executed (native scripts and Plutus scripts). - To all phase-1 validation purposes, we restrict ***Tag*** to ***Tag = {Mint, Spend}***. This is used to indicate whether a script is used on minting purposes (native scripts and minting policies), or should be executed (native scripts and Plutus scripts).
- Recall that ***Ix := ***, and represents an index on a list-like structure. - Recall that ***Ix := ***, and represents an index on a list-like structure.
- ***Redeemer*** is the low-level representation of a redeemer, required by executors to execute validation on Plutus scripts. - ***Redeemer*** is the low-level representation of a redeemer, required by executors to execute validation on Plutus scripts.
- ***scriptsNeeded(utxo, txBody) ∈ P((ScriptPurpose x ScriptHash))*** assembles all the ***ScriptPurpose*** terms for validation of every transaction that may require script validation, each one paired with the hash of the corresponding witnessing script. This collects hashes of both native and Plutus scripts. - ***scriptsNeeded(utxo, txBody) ∈ P((ScriptPurpose x ScriptHash))*** assembles all the ***(ScriptPurpose, ScriptHash)*** values for validation of every aspect of the transaction that may require script validation. This collects hashes of both native and Plutus scripts.
- ***ScriptPurpose := {PolicyID, TxIn}*** indicates whether the script is related to minting purposes (***PolicyID***) or should be executed to spend an input of the transaction (***TxIn***). - ***ScriptPurpose := {PolicyID, TxIn}*** indicates whether the script is related to minting purposes (***PolicyID***) or should be executed to spend an input of the transaction (***TxIn***).
- ***ScriptHash ⊆ Bytes*** is the type of validator hashes. - ***ScriptHash ⊆ Bytes*** is the type of validator hashes.
- ***scriptHash : Script -> ScriptHash*** is the hashing function for scripts. - ***scriptHash : Script -> ScriptHash*** is the hashing function for scripts.
- ***redeemerPointer: TxBody -> ScriptPurpose -> (Tag, Ix)*** builds a redeemer pointer (that is, a representation suitable for matching with ***txRedeemers(txWits)***), setting the tag according to the type of the script purpose, and the index according to the order of the item represented by the script purpose (either a policy ID or a transaction input) in its container. For example, applying ***redeemerPoint*** on script purpose ***txIn ∈ TxIn*** yields the index of ***txIn*** within ***txIns(txBody)***. - ***redeemerPointer: TxBody -> ScriptPurpose -> (Tag, Ix)*** builds a redeemer pointer (that is, a representation suitable for matching with ***txRedeemers(txWits)***), setting the tag according to the type of the script purpose, and the index according to the order of the item represented by the script purpose (either a policy ID or a transaction input) in its container. For example, applying ***redeemerPoint*** on script purpose ***txIn ∈ TxIn*** yields the index of ***txIn*** within ***txIns(txBody)***.
- ***txScripts(txWits) P(Script)*** is the set of scripts in the transaction witness set, both native and Plutus. - ***txScripts(txWits) P(Script)*** is the set of scripts in the transaction witness set, both native and Plutus.
- ***txDats(txWits) ∈ P(Datum)*** is the set of all script-related datum objects of the transaction. - ***txDats(txWits) ∈ P(Datum)*** is the set of all script-related datum objects of the transaction.
- ***datumHash: Datum -> DatumHash*** is the application of the hashing function on a ***Datum*** value. - ***datumHash: Datum -> DatumHash*** is the application of the hashing function on a ***Datum*** value.
- ***languages(txWits) ∈ Languages*** is the set of *languages* required by the scripts of the transaction. - ***languages(txWits) ∈ Languages*** is the set of *languages* required by the Plutus scripts of the transaction.
## Validation rules for blocks ## Validation rules for blocks
Let ***block ∈ Block*** be an Alonzo block, and let ***tx ∈ Tx*** be one of its Alonzo transactions, with transaction body ***txBody ∈ TxBody*** and witness set ***txWits ∈ TxWits***. We say that ***block*** is a phase-1 valid block if and only if the total sum of execution units of all its transactions does not exceed the maximum allowed by the protocol, and all its transactions are phase-1 valid. That is, ***block*** is phase-1 valid if and only if: Let ***block ∈ Block*** be an Alonzo block, and let ***tx ∈ Tx*** be one of its Alonzo transactions, with transaction body ***txBody ∈ TxBody*** and witness set ***txWits ∈ TxWits***. We say that ***block*** is a phase-1 valid block if and only if the total sum of execution units of all its transactions does not exceed the maximum allowed by the protocol, and all its transactions are phase-1 valid. That is, ***block*** is phase-1 valid if and only if:
<code>maxBlockExUnits(pps) ≥ blockExUnits(block) ∧ ∀ tx ∈ txs(block): txIsPhase1Valid(block, tx)</code> <code>maxBlockExUnits(pps) ≥ blockExUnits(block) ∧ ∀ tx ∈ txs(block): txIsPhase1Valid(pps, tx)</code>
## Validation rules for transactions ## Validation rules for transactions
@ -120,13 +121,13 @@ Let ***tx ∈ Tx*** be one of its Alonzo transactions, with transaction body ***
- **The set of transaction inputs is not empty**: - **The set of transaction inputs is not empty**:
<code>txIns(txBody) ≠ ∅</code> <code>txIns(txBody) ≠ ∅</code>
- **All transaction inputs and collateral inputs are in the set of (yet) unspent transaction outputs**: - **All transaction inputs (regular inputs, collateral inputs and reference inputs) are in the UTxO**:
<code>txIns(txBody) collateral(txBody) ⊆ dom(utxo)</code> <code>txIns(txBody) collateral(txBody) ⊆ dom(utxo)</code>
- **The block slot is contained in the transaction validity interval**: - **The block slot is contained in the transaction validity interval**:
<code>slot ∈ txValidityInterval(txBody)</code> <code>slot ∈ txValidityInterval(txBody)</code>
- **The upper bound of the validity time interval is suitable for script execution**: if there are minting policies, native or Plutus scripts in the transaction, and the upper bound of its validity interval is defined, then the upper bound slot of the interval is translatable to system time. That is, if there are neeeded scripts in the transaction, then it is the case that ***txValidityInterval(txBody) := (\_, ub)*** where ***ub*** is defined. - **The upper bound of the validity time interval is suitable for script execution**: if there are minting policies, native scripts or Plutus scripts involved in the transaction, and if the upper bound of its validity interval is a finite number, then it can be translated to system time.
- **Fees**: - **Fees**:
- **The fee paid by the transaction should be greater than or equal to the minimum fee**: - **The fee paid by the transaction should be greater than or equal to the minimum fee**:
@ -151,10 +152,10 @@ Let ***tx ∈ Tx*** be one of its Alonzo transactions, with transaction body ***
- **The preservation of value property holds**: Assuming no staking or delegation actions are involved, it should be that - **The preservation of value property holds**: Assuming no staking or delegation actions are involved, it should be that
<code>consumed(utxo, txBody) = produced(txBody) + fee(txBody) + minted(txBody)</code> <code>consumed(utxo, txBody) = produced(txBody) + fee(txBody) + minted(txBody)</code>
- **All transaction outputs should contain at least the minimum lovelace**: - **All transaction outputs (regular outputs and collateral return outputs) should contain at least the minimum lovelace**:
<code>∀ txOut ∈ txOuts(txBody): adaValueOf(coinsPerUTxOWord(pps) * utxoEntrySize(txOut)) ≤ getValue(txOut)</code> <code>∀ txOut ∈ txOuts(txBody): adaValueOf(coinsPerUTxOWord(pps) * outputEntrySize(txOut)) ≤ getValue(txOut)</code>
- **The size of the value in each of the outputs should not be greater than the maximum allowed**: - **The size of the value in each of the transaction outputs (regular outputs and collateral return outputs) should not be greater than the maximum allowed**:
<code>valSize(getValue(txOut)) ≤ maxValSize(pps)</code> <code>valSize(getValue(txOut)) ≤ maxValSize(pps)</code>
- **The network ID of each output matches the global network ID**: - **The network ID of each output matches the global network ID**:
@ -167,6 +168,9 @@ Let ***tx ∈ Tx*** be one of its Alonzo transactions, with transaction body ***
- **The number of execution units of the transaction should not exceed the maximum allowed**: - **The number of execution units of the transaction should not exceed the maximum allowed**:
<code>txExUnits(txBody) ≤ maxTxExUnits(pps)</code> <code>txExUnits(txBody) ≤ maxTxExUnits(pps)</code>
- **No ADA is minted**:
<code>adaID ∉ policies(minted(txBody))</code>
- **Witnesses**: - **Witnesses**:
- **Minting policy, native script and Plutus script witnesses**: - **Minting policy, native script and Plutus script witnesses**:
@ -187,19 +191,16 @@ Let ***tx ∈ Tx*** be one of its Alonzo transactions, with transaction body ***
- <code>verify(vk, σ, ⟦txBody⟧<sub>TxBody</sub>)</code> - <code>verify(vk, σ, ⟦txBody⟧<sub>TxBody</sub>)</code>
- <code>paymentCredential<sub>utxo</sub>(txIn) = keyHash(vk)</code> - <code>paymentCredential<sub>utxo</sub>(txIn) = keyHash(vk)</code>
- **All required signers (needed by a Plutus script) have a corresponding match in the transaction witness set**: for each ***key_hash ∈ requiredSigners(txBody)***, there should exist ***(vk, σ) ∈ txVKWits(tx)*** such that: - **All required signers (needed by one of the Plutus scripts of the transaction) have a corresponding match in the transaction witness set**: for each ***key_hash ∈ requiredSigners(txBody)***, there should exist ***(vk, σ) ∈ txVKWits(tx)*** such that:
- <code>verify(vk, σ, ⟦txBody⟧<sub>TxBody</sub>)</code> - <code>verify(vk, σ, ⟦txBody⟧<sub>TxBody</sub>)</code>
- <code>keyHash(vk) = key_hash</code> - <code>keyHash(vk) = key_hash</code>
- **The required script languages are included in the protocol parameters**: - **The required script languages are included in the protocol parameters**:
<code>languages(txWits) ⊆ {l : (l -> _) ∈ costModels(pps, language)}</code> <code>languages(txWits) ⊆ {l : (l -> _) ∈ costModels(pps, language)}</code>
- **The metadata of the transaction is valid**: - **The auxiliary data of the transaction is valid**:
<code>txMDHash(tx) = hashMD(txMD(tx))</code> <code>txAuxDatHash(tx) = hashMD(txAuxDat(tx))</code>
- **The script data integrity hash matches the hash of the redeemers, languages and datums of the transaction witness set**: - **The script data integrity hash matches the hash of the redeemers, languages and datums of the transaction witness set**:
<code>scriptDataHash(txBody) = hashScriptIntegrity(pps, txRedeemers(txWits), languages(txWits), txDats(txWits))</code> <code>scriptDataHash(txBody) = hashScriptIntegrity(pps, txRedeemers(txWits), languages(txWits), txDats(txWits))</code>
- **No ADA is minted**:
<code>adaID ∉ policies(mint(txBody))</code>

View file

@ -0,0 +1,239 @@
# Babbage phase-1 validation rules
This document covers the terminology and equations related to the Babbage ledger phase-1 validation rules. For further information, refer to the [Babbage ledger white paper](https://github.com/IntersectMBO/cardano-ledger/releases/download/cardano-ledger-spec-2023-04-03/babbage-ledger.pdf).
## Definitions and notation
- **Blocks**:
- ***Block*** is the type of Babbage blocks. We will write ***block*** to refer to the current block being validated.
- ***txs(block)*** is the set of transactions of the block.
- ***blockExUnits(block) ∈ ExUnits***, where ***ExUnits := (, )***, is the memory and execution step units resulting from the sum of memory and execution step units of all its transactions. That is, ***blockExUnits(block) := ∑ tx ∈ txs(block): txExUnits(txWits(tx))***, where addition of execution units is defined pointwise.
- **Transactions**:
- ***Tx*** is the type of Babbage transactions, composed of a transaction body and a witness set. We will write ***tx*** to refer to the current transaction.
- ***txIsPhase1Valid(pps, tx) ∈ Bool*** indicates whether ***tx ∈ Tx*** is phase-1 valid for the Babbage leger under ***pps***.
- ***TxBody*** is the type of Babbage transaction bodies. Each transaction body is composed of a set of inputs, a list of outputs, and other related data.
- ***txBody(tx)*** is the transaction body of the transaction. We will write ***txBody*** to refer to the transaction body of the current transaction.
- ***TxOut = Addr x Value x DatumOption x ScriptRef*** is the set of transaction outputs, where
- ***Addr*** is the type of transaction output addresses.
- ***Value*** is the type of multi-asset Babbage values. We define addition, equality comparison and ordering comparisons for values in a point-wise manner.
- ***getValue(txOut) ∈ Value*** gives the value contained in the transaction output.
- ***isADAOnly : Value -> Bool*** indicates whether a value contains only ADA assets.
- ***balance : P(TxOut) → Value*** gives the sum of all assets in a set of transaction outputs.
- ***adaValueOf : -> Value*** gives the ADA-only value representation of a natural number.
- ***valSize : Value -> *** gives the size of a value in bytes, when serialized.
- ***policies(v) ∈ P(PolicyID)*** gives the set of policies of the assets of the value.
- ***DatumOption ⊆ DatumHash U Datum*** is the union type of datum hashes and datums. This field is optional, and combines the datum hash feature from Alonzo with the possibility to store datums *inline*.
- ***isDatum : DatumOption -> Bool*** returns ***true*** if the datum option is in ***Datum***.
- ***isWellFormedDatum(b) ∈ Bool*** assesses whether bytestring ***b*** corresponds to the CBOR of a well-formed datum.
- ***isDatumHash : DatumOption -> Bool*** returns ***true*** if the datum option is in ***DatumHash***.
- ***ScriptRef*** is the type of script references in transaction outputs. This novel Babbage feature allows transactions to use a script without having to spend an output.
- ***txOuts(txBody) ∈ P(TxOut)*** gives the list of transaction outputs of the transaction body.
- ***txCollateralReturn(txBody) ∈ TxOut*** is the collateral return output of the transaction.
- ***allOuts(txBody) ∈ P(TxOut)*** is defined as ***txOuts(txBody) {txCollateralReturn(txBody)}***.
- ***balance : P(TxOut) → Value*** gives the sum of all multi-asset values in a set of transaction outputs.
- ***txCollateralBalance(txBody, utxo) ∈ Value*** gives the value paid as collateral by the transaction, defined by the following equation: ***txCollatralBalance(txBody, utxo) := balance(txCollateralIns(txBody) ◁ utxo) - balance(txCollateralReturn(txBody))***.
- ***outputEntrySize(txOut) ∈ *** gives the size of the transaction output when serialized, in bytes (plus an offset required only in the Babbage era).
- ***TxIn = TxId x Ix*** is the set of transaction inputs, where
- ***TxId*** is the type of transaction IDs.
- ***Ix = *** is the set of indices, which are used to refer to a specific transaction output.
- ***txSpendIns(txBody) ∈ P(TxIn)*** gives the set of *regular* inputs—i.e., transaction inputs without taking into account collateral and reference inputs).
- ***txSpendInsVKey(txBody) ∈ P(TxIn)*** gives the subset of regular inputs of the transaction which are verification-key locked—i.e., without taking into account script inputs from ***txSpendIns(txBody)***.
- ***txCollateralIns(txBody) ∈ P(TxIn)*** gives the set of *collateral* inputs of the transaction.
- ***txReferenceIns(txBody) ∈ P(TxIn)*** gives the set of *reference* inputs of the transaction.
- ***utxo : TxIn → TxOut*** is a (partial) map that gives the unspent transaction output (UTxO) associated with a transaction input.
- Given ***A ⊆ dom(utxo)***, we will write ***A ◁ utxo := {txOut ∈ TxOut / ∃ txIn ∈ dom utxo: utxo(txIn) = txOut}***. For example, we will write ***txSpendIns(txBody) ◁ utxo := {txOut ∈ TxOut / ∃ ti ∈ dom(utxo): utxo(txIn) = txOut}*** to express the set of unspent transaction outputs associated with the set of inputs of the transaction.
- ***txTotalColl(txBody) ∈ *** is the collateral paid by the transaction. Note that this is merely an annotation, and that validations should check whether this number actually equals the balance between the lovelace in all collateral inputs and the lovelace in the collateral return output.
- ***txValidityInterval(txBody) ∈ (Slot, Slot)*** is the transaction validity interval, made of a lower and upper bound, both of which are optional.
- ***requiredSigners(txBody) ∈ P(KeyHash)*** is the set of hashes of verification keys required for the execution of Plutus scripts, where ***KeyHash ⊆ Bytes***.
- ***txSize(txBody) ∈ *** is the size of the transaction in bytes, when serialized.
- ***fee(txBody) ∈ *** is the fee paid by the transaction.
- ***minted(txBody)*** is the multi-asset value minted (or burned) in the transaction.
- ***PolicyID*** is the set of all possible policy IDs associated to multi-asset values. In particular, ***adaID ∈ Policy*** is the policy of lovelaces.
- ***consumed(utxo, txBody) ∈ *** is the *consumed value* of the transaction, which equals the sum of all multi-asset values in the inputs of the transaction.
- ***produced(txBody) ∈ *** is the *produced value* of the transaction, which equals the sum of all multi-asset values in the outputs of the transaction, plus the transaction fee, plus the minted value.
- ***txNetId(txBody) ∈ NetworkID*** gives the network ID of a transaction (not to be confused with the network ID of addresses of unspent transaction outputs).
- ***txWits(tx)*** is the transaction witness set. We will write ***txWits*** to refer to the transaction witness set of the current transaction.
- ***txExUnits(txWits) ∈ ExUnits*** is the total execution units of the transaction.
- ***txAuxDat(tx)*** is the auxiliary data of the transaction.
- ***hashMD(md)*** is the result of hasing auxiliary data ***md***.
- ***txAuxDatHash(txBody)*** is the auxiliary data hash contained within the transaction body.
- **Addresses**:
- ***Addr*** is the set of all valid Babbage addresses.
- ***hashAddr : Addr -> Bytes*** is the hashing function for addresses.
- ***NetworkId*** is the global network ID.
- ***netId : Addr -> NetworkID*** gives the network ID of an address.
- ***isVKeyAddress(addr) -> Bool*** assesses whether the address is that of a verification key.
- ***isPlutusScriptAddress(addr, txWits)*** assesses whether the address is that of a Plutus script.
- ***Time***:
- ***Slot ∈ *** is the set of slots. When necessary, we write ***slot ∈ Slot*** to refer to the slot associated to the current block.
- ***UTCTime*** is the system time (UTC time zone).
- ***EpochInfo*** is the Babbage epoch info.
- ***SystemStart*** is the start time of the system.
- ***epochInfoSlotToUTCTime: EpochInfo -> SystemStart -> Slot -> UTCTime*** translates a slot number to system time. The result is not always computable, as the slot number may be too far in the future for the system to predict the exact time to which it refers.
- **Serialization**:
- ***Bytes*** is the set of byte arrays (a.k.a. data, upon which signatures are built).
- ***⟦_⟧<sub>A</sub> : A -> Bytes*** takes an element of type ***A*** and returns a byte array resulting from serializing it.
- **Hashing**:
- ***hash: A -> Bytes*** is the abstract function (considering that ***A*** is a generic type) we use to refer to a hashing function.
- ***keyHash: VKey -> KeyHash*** is the hashing function for verification keys, where ***KeyHash ⊆ Bytes***
- **Scripts**:
- ***Script*** is the set of all Babbage scripts: minting policies, native scripts and Plutus scripts. We will use the term *script* to refer to any of these kinds of scripts.
- ***isWellFormedScript(script) ∈ Bool*** assesses whether a script is well formed.
- ***isPlutusScript(script) ∈ Bool*** assesses whether a script is a Plutus script (that is, it is not a native script).
- ***scriptDataHash(txBody) ∈ Bytes*** is the hash of script-related data (transaction redeemers and relevant protocol parameters).
- ***hashScriptIntegrity : PParams -> P((Tag, Ix, Redeemer, ExUnits)) -> Languages -> P(Datum) -> Bytes*** hashes the protocol parameters and data relevant to script execution.
- ***txWitScripts(txWits) ∈ P(Script)*** is the set of scripts contained in the witness set of the transaction.
- ***refScripts(txBody, utxo) ∈ P(Script)*** is the set of scripts contained in reference inputs.
- ***auxDataScripts(tx) ∈ P(Script)*** is the set of scripts contained in the auxiliary data of the transaction.
- **Protocol Parameters**:
- We will write ***pps ∈ PParams*** to represent the set of Babbage protocol parameters, each of which contains at least the following associated functions:
- ***maxBlockExUnits(pps) ∈ ExUnits*** gives the maximum memory and execution step units for a block.
- ***maxTxExUnits(pps) ∈ ExUnits*** gives the maximum memory and execution step units for a transaction.
- ***minFees(pps, txBody) ∈ *** gives the minimum number of lovelace that must be paid by the transaction as fee.
- ***maxCollateralInputs(pps) ∈ *** gives the maximum number of collateral inputs allowed per transaction.
- ***maxTxSize(pps) ∈ *** gives the maximum size any transaction can have.
- ***maxValSize(pps) ∈ *** gives the maximum size in bytes allowed for values, when serialized.
- ***collateralPercent(pps) ∈ {0,...,100}*** gives the fee percentage (multiplied by 100) that all lovelace in collateral inputs should add up to.
- ***coinsPerUTxOWord(pps) ∈ *** is the number of lovelace a UTxO should contain per byte (when serialized). This is used to assess the minimum number of lovelace that an unspent transaction output should lock.
- ***costModels : PParams -> (Languages -> CostModel)*** takes the protocol parameters and returns a map associating languages to their cost models.
- ***Languages := {PlutusV1, PlutusV2}*** is the set of Babbage languages.
- ***CostModel*** is the set of cost models.
- ***Witnesses***:
- ***TxWits*** is the type of transaction witnesses.
- ***VKey*** is the set of verification keys (a.k.a. public keys).
- ***SKey*** is the set of signing keys (a.k.a. private keys).
- ***Sig*** is the set of signatures (i.e., the result of signing a byte array using a signing key).
- ***sig : SKey x Bytes -> Sig*** is the signing function.
- ***verify : VKey x Sig x Bytes -> Bool*** assesses whether the result of applying the verification key to the signature equals the byte array parameter.
- The assumption is that if ***sk*** and ***vk*** are, respectively, a pair of secret and verification keys associated with one another. Thus, if ***sig(sk, d) = σ***, then it must be that ***verify(vk, σ, d) = true***.
- ***txVKWits(txWits) ⊆ P(VKey x Sig)*** gives the list of pairs of verification keys and signatures of the transaction.
- ***paymentCredential<sub>utxo</sub>(txIn) ∈ KeyHash*** gets from ***txIn*** the associated transaction output in ***utxo***, extracts the address contained in it, and returns its hash. In other words, given ***utxo*** and transaction input ***txIn*** such that ***utxo(txIn) = (a, \_, \_, \_)***, we have that ***paymentCredential<sub>utxo</sub>(txIn) = hashAddr(a)***.
- ***txRedeemers(txWits) ⊆ P((Tag, Ix, Redeemer, ExUnits))*** is the set of redeemers of the transaction. This (seemingly artificial) conjunction of values of different types will be useful to assess phase-1 validity of the transaction in a concise way.
- To all phase-1 validation purposes, we restrict ***Tag*** to ***Tag = {Mint, Spend}***. This is used to indicate whether a script is used on minting purposes (native scripts and minting policies), or should be executed (native scripts and Plutus scripts).
- Recall that ***Ix := ***, and represents an index on a list-like structure.
- ***Redeemer*** is the low-level representation of a redeemer, required by executors to execute validation on Plutus scripts.
- ***scriptsNeeded(utxo, txBody) ∈ P((ScriptPurpose x ScriptHash))*** assembles all the ***(ScriptPurpose, ScriptHash)*** values for validation of every aspect of the transaction that may require script validation. This collects hashes of both native and Plutus scripts, and is comprised of the minting policies, the hash of all native and Plutus scripts in ***txSpendIns(txBody)***, and the hash of all elements in ***txReferenceIns(tx)***—that is, the hash of all reference scripts.
- ***ScriptPurpose := {PolicyID, TxIn}*** indicates whether the script is related to minting purposes (***PolicyID***) or should be executed to spend an input of the transaction (***TxIn***).
- ***ScriptHash ⊆ Bytes*** is the type of validator hashes.
- ***scriptHash : Script -> ScriptHash*** is the hashing function for scripts.
- ***redeemerPointer: TxBody -> ScriptPurpose -> (Tag, Ix)*** builds a redeemer pointer (that is, a representation suitable for matching with ***txRedeemers(txWits)***), setting the tag according to the type of the script purpose, and the index according to the order of the item represented by the script purpose (either a policy ID or a transaction input) in its container. For example, applying ***redeemerPoint*** on script purpose ***txIn ∈ TxIn*** yields the index of ***txIn*** within ***txSpendIns(txBody)***.
- ***txScripts(tx, utxo) ∈ P(Script)*** is the set of scripts in the transaction witness set of ***tx***, both native and Plutus, as well as those in reference inputs—i.e., the scripts obtained applying ***utxo*** on the reference inputs of ***tx***.
- ***txDats(txWits) ∈ P(Datum)*** is the set of all script-related datum objects of the transaction.
- ***datumHash: Datum -> DatumHash*** is the application of the hashing function on a ***Datum*** value.
- ***languages(tx, utxo) ∈ Languages*** is the set of *languages* required by the Plutus scripts in ***tx***.
## Validation rules for blocks
Let ***block ∈ Block*** be an Babbage block, and let ***tx ∈ Tx*** be one of its Babbage transactions, with transaction body ***txBody ∈ TxBody*** and witness set ***txWits ∈ TxWits***. We say that ***block*** is a phase-1 valid block if and only if the total sum of execution units of all its transactions does not exceed the maximum allowed by the protocol, and all its transactions are phase-1 valid. That is, ***block*** is phase-1 valid if and only if:
<code>maxBlockExUnits(pps) ≥ blockExUnits(block) ∧ ∀ tx ∈ txs(block): txIsPhase1Valid(pps, tx)</code>
## Validation rules for transactions
Let ***tx ∈ Tx*** be one of its Babbage transactions, with transaction body ***txBody ∈ TxBody*** and witness set ***txWits***. We say that ***tx*** is a phase-1 valid transaction if and only if
- **The set of transaction inputs is not empty**:
<code>txSpendIns(txBody) ≠ ∅</code>
- **All transaction inputs, collateral inputs and reference inputs are in the UTxO**:
<code>txSpendIns(txBody) txCollateralIns(txBody) txReferenceIns(txBody) ⊆ dom(utxo)</code>
- **The block slot is contained in the transaction validity interval**:
<code>slot ∈ txValidityInterval(txBody)</code>
- **The upper bound of the validity time interval is suitable for script execution**: if there are minting policies, native scripts or Plutus scripts involved in the transaction, and if the upper bound of its validity interval is a finite number, then it can be translated to system time.
- **Fees**:
- **The fee paid by the transaction is greater than or equal to the minimum fee**:
<code>fee(txBody) ≥ minFees(pps, txBody)</code>
- **Collateral**: if there are Plutus scripts in the transaction, then
- **The set of collateral inputs is not empty**:
<code>txCollateralIns(txBody) ≠ ∅</code>
- **The number of collateral inputs is not above maximum**:
<code>∥txCollateralIns(txBody)∥ ≤ maxCollateralInputs(pps)</code>
- **Each collateral input refers to a verification-key address**:
<code>∀(a,\_,\_,\_) ∈ txCollateralIns(txBody) ◁ utxo: isVKeyAddress(a)</code>
- **The balance between collateral inputs and outputs contains only ADA**:
<code>isADAOnly(txCollateralBalance(txBody, utxo))</code>
- **The paid collateral is greater than or equal to the minimum fee percentage**:
<code>txCollateralBalance(txBody, utxo) >= fee(txBody) * collateralPercent(pps)</code>
- **If a number of collateral lovelace is specified in the transaction body, then it equals the actual collateral paid by the transaction**:
<code>balance(txCollateralIns(txBody) ◁ utxo) - balance(txCollateralReturn(txBody)) = txTotalColl(txBody)</code>
- **The preservation of value property holds**: Assuming no staking or delegation actions are involved, it is the case that
<code>consumed(utxo, txBody) = produced(txBody) + fee(txBody) + minted(txBody)</code>
- **All transaction outputs (regular outputs and collateral return outputs) contains at least the minimum lovelace**:
<code>∀ txOut ∈ txOuts(txBody): adaValueOf(coinsPerUTxOWord(pps) * (outputEntrySize(txOut) + 160)) ≤ getValue(txOut)</code>
- **The size of the value in each of the outputs is not greater than the maximum allowed**:
<code>valSize(getValue(txOut)) ≤ maxValSize(pps)</code>
- **The network ID of each regular output as well as that of the collateral return output match the global network ID**:
<code>∀(a,\_) ∈ txOuts(txBody): netId(a) = NetworkId</code>
- **The network ID of the transaction body is either undefined or equal to the global network ID**
- **The transaction size does not exceed the protocol limit**:
<code>txSize(txBody) ≤ maxTxSize(pps)</code>
- **The number of execution units of the transaction does not exceed the maximum allowed**:
<code>txExUnits(txBody) ≤ maxTxExUnits(pps)</code>
- **No ADA is minted**:
<code>adaID ∉ policies(minted(txBody))</code>
- **Well-formedness of all datums and scripts**:
- **All datums in the witness set are well-formed**:
<code>∀ d ∈ txDats(txWits): isWellFormedDatum(d)</code>
- **All scripts in the witness set are well-formed**:
<code>∀ s ∈ txWitScripts(txWits): isWellFormedScript(s)</code>
- **All scripts in the auxiliary data are well-formed**:
<code>∀ s ∈ auxDataScripts(txAuxDat(tx)): isWellFormedScript(s)</code>
- **All output datums are well-formed**:
<code>∀ (\_,\_,d,\_) ∈ allOuts(txBody): isDatum(d) => isWellFormedDatum(d)</code>
- **All output scripts are well-formed**:
<code>∀ (\_,\_,\_,d) ∈ allOuts(txBody): isWellFormedScript(d)</code>
- **Witnesses**:
- **Minting policies, native scripts and Plutus scripts, reference scripts**:
- **Each minting policy or script hash in a script input address can be matched to a script in the transaction witness set, except when it can be found in a reference input**:
<code>{h: (\_, h) ∈ scriptsNeeded(utxo, txBody)} - {scriptHash(s): s ∈ refScripts(txBody, utxo)} = {scriptHash(s) : s ∈ txScripts(tx, utxo)}</code>
- **Each datum hash in a Plutus script input matches the hash of a datum in the transaction witness set**:
<code>{h : (a,\_,h,\_) ∈ txSpendIns(txBody) ◁ utxo, isPlutusScriptAddress(a, txWits)} ⊆ {datumHash(d) : d ∈ txDats(txWits)}</code>
- **Each datum in the transaction witness set can be related to the datum hash in a Plutus script input, or in a reference input, or in a regular output, or in the collateral return output**:
<code>{datumHash(d): d ∈ txDats(txWits)} ⊆ {h: (a,\_,h,\_) ∈ txSpendIns(txBody) ◁ utxo, isPlutusScriptAddress(a, txWits), isDatumHash(h)} {h: (\_,\_,h,\_) ∈ txReferenceIns(tx) ◁ utxo, isDatumHash(h)} {h: (\_,\_,h,\_) ∈ allOuts(txBody), isDatumHash(h)}</code>
- **The set of redeemers in the transaction witness set matches the set of Plutus scripts needed to validate the transaction**:
<code>{(tag, index): (tag, index, \_, \_) ∈ txRedeemers(txWits)} = {redeemerPointer(txBody, sp): (sp, h) ∈ scriptsNeeded(utxo, txBody), (∃s ∈ txScripts(tx, utxo): isPlutusScript(s), h = scriptHash(s)}</code>
- **Verification-key witnesses**:
- **The owner of each transaction input and each collateral input has signed the transaction**:
<code>∀ txIn ∈ txSpendInsVKey(txBody): (∃ (vk, σ) ∈ txVKWits(tx): verify(vk, σ, ⟦txBody⟧<sub>TxBody</sub>) ∧ paymentCredential<sub>utxo</sub>(txIn) = keyHash(vk))</code>
- **All required signers (needed by one of the Plutus scripts of the transaction) have a corresponding match in the transaction witness set**: for each ***key_hash ∈ requiredSigners(txBody)***, there exists ***(vk, σ) ∈ txVKWits(tx)*** such that:
<code>∀ key_hash ∈ requiredSigners(txBody): (∃ (vk, σ) ∈ txVKWits(tx): verify(vk, σ, ⟦txBody⟧<sub>TxBody</sub>) ∧ keyHash(vk) = key_hash) </code>
- **The required script languages are included in the protocol parameters**:
<code>languages(tx, utxo) ⊆ {l : (l -> _) ∈ costModels(pps, language)}</code>
- **The auxiliary data of the transaction is valid**:
<code>txAuxDatHash(tx) = hashMD(txAuxDat(tx))</code>
- **The script data integrity hash matches the hash of the redeemers, languages and datums of the transaction witness set**:
<code>scriptDataHash(txBody) = hashScriptIntegrity(pps, txRedeemers(txWits), languages(tx, utxo), txDats(txWits))</code>
- **Each minted / burned asset can be related to the corresponding native or Plutus script in the transaction witness set**
<code>policies(minted(txBody)) ⊆ {scriptHash(s): s ∈ txScripts(tx, utxo)}</code>

View file

@ -1,9 +1,10 @@
//! Utilities required for Shelley-era transaction validation. //! Utilities required for Alonzo-era transaction validation.
use crate::utils::{ use crate::utils::{
add_minted_value, add_values, empty_value, extract_auxiliary_data, get_alonzo_comp_tx_size, add_minted_value, add_values, aux_data_from_alonzo_minted_tx, compute_native_script_hash,
get_lovelace_from_alonzo_val, get_network_id_value, get_payment_part, get_shelley_address, compute_plutus_script_hash, empty_value, get_alonzo_comp_tx_size, get_lovelace_from_alonzo_val,
get_val_size_in_words, mk_alonzo_vk_wits_check_list, values_are_equal, verify_signature, get_network_id_value, get_payment_part, get_shelley_address, get_val_size_in_words,
mk_alonzo_vk_wits_check_list, values_are_equal, verify_signature,
AlonzoError::*, AlonzoError::*,
AlonzoProtParams, FeePolicy, UTxOs, AlonzoProtParams, FeePolicy, UTxOs,
ValidationError::{self, *}, ValidationError::{self, *},
@ -18,9 +19,9 @@ use pallas_codec::{
use pallas_crypto::hash::Hash; use pallas_crypto::hash::Hash;
use pallas_primitives::{ use pallas_primitives::{
alonzo::{ alonzo::{
AddrKeyhash, Mint, MintedTx, MintedWitnessSet, NativeScript, PlutusData, PlutusScript, AddrKeyhash, Mint, MintedTx, MintedWitnessSet, Multiasset, NativeScript, PlutusData,
PolicyId, Redeemer, RedeemerPointer, RedeemerTag, RequiredSigners, TransactionBody, PlutusScript, PolicyId, Redeemer, RedeemerPointer, RedeemerTag, RequiredSigners,
TransactionInput, TransactionOutput, VKeyWitness, Value, TransactionBody, TransactionInput, TransactionOutput, VKeyWitness, Value,
}, },
byron::TxOut, byron::TxOut,
}; };
@ -48,7 +49,7 @@ pub fn validate_alonzo_tx(
check_tx_ex_units(mtx, prot_pps)?; check_tx_ex_units(mtx, prot_pps)?;
check_witness_set(mtx, utxos)?; check_witness_set(mtx, utxos)?;
check_languages(mtx, prot_pps)?; check_languages(mtx, prot_pps)?;
check_metadata(tx_body, mtx)?; check_auxiliary_data(tx_body, mtx)?;
check_script_data_hash(tx_body, mtx)?; check_script_data_hash(tx_body, mtx)?;
check_minting(tx_body, mtx) check_minting(tx_body, mtx)
} }
@ -201,14 +202,15 @@ fn check_collaterals_address(collaterals: &[TransactionInput], utxos: &UTxOs) ->
Some(multi_era_output) => { Some(multi_era_output) => {
if let Some(alonzo_comp_output) = MultiEraOutput::as_alonzo(multi_era_output) { if let Some(alonzo_comp_output) = MultiEraOutput::as_alonzo(multi_era_output) {
if let ShelleyPaymentPart::Script(_) = if let ShelleyPaymentPart::Script(_) =
get_payment_part(alonzo_comp_output).ok_or(Alonzo(InputDecoding))? get_payment_part(&alonzo_comp_output.address)
.ok_or(Alonzo(InputDecoding))?
{ {
return Err(Alonzo(CollateralNotVKeyLocked)); return Err(Alonzo(CollateralNotVKeyLocked));
} }
} }
} }
None => return Err(Alonzo(CollateralNotInUTxO)), None => return Err(Alonzo(CollateralNotInUTxO)),
}; }
} }
Ok(()) Ok(())
} }
@ -258,12 +260,11 @@ fn check_collaterals_assets(
// The preservation of value property holds. // The preservation of value property holds.
fn check_preservation_of_value(tx_body: &TransactionBody, utxos: &UTxOs) -> ValidationResult { fn check_preservation_of_value(tx_body: &TransactionBody, utxos: &UTxOs) -> ValidationResult {
let neg_val_err: ValidationError = Alonzo(NegativeValue); let mut input: Value = get_consumed(tx_body, utxos)?;
let input: Value = get_consumed(tx_body, utxos)?;
let produced: Value = get_produced(tx_body)?; let produced: Value = get_produced(tx_body)?;
let output: Value = add_values(&produced, &Value::Coin(tx_body.fee), &neg_val_err)?; let output: Value = add_values(&produced, &Value::Coin(tx_body.fee), &Alonzo(NegativeValue))?;
if let Some(m) = &tx_body.mint { if let Some(m) = &tx_body.mint {
add_minted_value(&output, m, &neg_val_err)?; input = add_minted_value(&input, m, &Alonzo(NegativeValue))?;
} }
if !values_are_equal(&input, &output) { if !values_are_equal(&input, &output) {
return Err(Alonzo(PreservationOfValue)); return Err(Alonzo(PreservationOfValue));
@ -272,17 +273,18 @@ fn check_preservation_of_value(tx_body: &TransactionBody, utxos: &UTxOs) -> Vali
} }
fn get_consumed(tx_body: &TransactionBody, utxos: &UTxOs) -> Result<Value, ValidationError> { fn get_consumed(tx_body: &TransactionBody, utxos: &UTxOs) -> Result<Value, ValidationError> {
let neg_val_err: ValidationError = Alonzo(NegativeValue);
let mut res: Value = empty_value(); let mut res: Value = empty_value();
for input in tx_body.inputs.iter() { for input in tx_body.inputs.iter() {
let utxo_value: &MultiEraOutput = utxos let utxo_value: &MultiEraOutput = utxos
.get(&MultiEraInput::from_alonzo_compatible(input)) .get(&MultiEraInput::from_alonzo_compatible(input))
.ok_or(Alonzo(InputNotInUTxO))?; .ok_or(Alonzo(InputNotInUTxO))?;
match MultiEraOutput::as_alonzo(utxo_value) { match MultiEraOutput::as_alonzo(utxo_value) {
Some(TransactionOutput { amount, .. }) => res = add_values(&res, amount, &neg_val_err)?, Some(TransactionOutput { amount, .. }) => {
res = add_values(&res, amount, &Alonzo(NegativeValue))?
}
None => match MultiEraOutput::as_byron(utxo_value) { None => match MultiEraOutput::as_byron(utxo_value) {
Some(TxOut { amount, .. }) => { Some(TxOut { amount, .. }) => {
res = add_values(&res, &Value::Coin(*amount), &neg_val_err)? res = add_values(&res, &Value::Coin(*amount), &Alonzo(NegativeValue))?
} }
_ => return Err(Alonzo(InputNotInUTxO)), _ => return Err(Alonzo(InputNotInUTxO)),
}, },
@ -292,10 +294,9 @@ fn get_consumed(tx_body: &TransactionBody, utxos: &UTxOs) -> Result<Value, Valid
} }
fn get_produced(tx_body: &TransactionBody) -> Result<Value, ValidationError> { fn get_produced(tx_body: &TransactionBody) -> Result<Value, ValidationError> {
let neg_val_err: ValidationError = Alonzo(NegativeValue);
let mut res: Value = empty_value(); let mut res: Value = empty_value();
for TransactionOutput { amount, .. } in tx_body.outputs.iter() { for TransactionOutput { amount, .. } in tx_body.outputs.iter() {
res = add_values(&res, amount, &neg_val_err)?; res = add_values(&res, amount, &Alonzo(NegativeValue))?;
} }
Ok(res) Ok(res)
} }
@ -311,12 +312,12 @@ fn check_min_lovelace(tx_body: &TransactionBody, prot_pps: &AlonzoProtParams) ->
} }
fn compute_min_lovelace(output: &TransactionOutput, prot_pps: &AlonzoProtParams) -> u64 { fn compute_min_lovelace(output: &TransactionOutput, prot_pps: &AlonzoProtParams) -> u64 {
let utxo_entry_size: u64 = get_val_size_in_words(&output.amount) let output_entry_size: u64 = get_val_size_in_words(&output.amount)
+ match output.datum_hash { + match output.datum_hash {
Some(_) => 37, // utxoEntrySizeWithoutVal (27) + dataHashSize (10) Some(_) => 37, // utxoEntrySizeWithoutVal (27) + dataHashSize (10)
None => 27, // utxoEntrySizeWithoutVal None => 27, // utxoEntrySizeWithoutVal
}; };
prot_pps.coins_per_utxo_word * utxo_entry_size prot_pps.coins_per_utxo_word * output_entry_size
} }
// The size of the value in each of the outputs should not be greater than the // The size of the value in each of the outputs should not be greater than the
@ -433,7 +434,7 @@ fn check_needed_scripts_are_included(
return Err(Alonzo(UnneededNativeScript)); return Err(Alonzo(UnneededNativeScript));
} }
} }
for (plutus_script_covered, _) in native_scripts.iter() { for (plutus_script_covered, _) in plutus_scripts.iter() {
if !plutus_script_covered { if !plutus_script_covered {
return Err(Alonzo(UnneededPlutusScript)); return Err(Alonzo(UnneededPlutusScript));
} }
@ -533,19 +534,23 @@ fn check_redeemers(
.collect(), .collect(),
None => Vec::new(), None => Vec::new(),
}; };
let plutus_scripts: Vec<RedeemerPointer> = let plutus_scripts: Vec<RedeemerPointer> = mk_plutus_script_redeemer_pointers(
mk_plutus_script_redeemer_pointers(tx_body, tx_wits, utxos); &sort_inputs(&tx_body.inputs),
&tx_body.mint,
tx_wits,
utxos,
);
redeemer_pointers_coincide(&redeemer_pointers, &plutus_scripts) redeemer_pointers_coincide(&redeemer_pointers, &plutus_scripts)
} }
fn mk_plutus_script_redeemer_pointers( fn mk_plutus_script_redeemer_pointers(
tx_body: &TransactionBody, sorted_inputs: &[TransactionInput],
mint: &Option<Multiasset<i64>>,
tx_wits: &MintedWitnessSet, tx_wits: &MintedWitnessSet,
utxos: &UTxOs, utxos: &UTxOs,
) -> Vec<RedeemerPointer> { ) -> Vec<RedeemerPointer> {
match &tx_wits.plutus_script { match &tx_wits.plutus_script {
Some(plutus_scripts) => { Some(plutus_scripts) => {
let sorted_inputs: Vec<TransactionInput> = sort_inputs(&tx_body.inputs);
let mut res: Vec<RedeemerPointer> = Vec::new(); let mut res: Vec<RedeemerPointer> = Vec::new();
for (index, input) in sorted_inputs.iter().enumerate() { for (index, input) in sorted_inputs.iter().enumerate() {
if let Some(script_hash) = get_script_hash_from_input(input, utxos) { if let Some(script_hash) = get_script_hash_from_input(input, utxos) {
@ -560,7 +565,7 @@ fn mk_plutus_script_redeemer_pointers(
} }
} }
} }
match &tx_body.mint { match mint {
Some(minted_value) => { Some(minted_value) => {
let sorted_policies: Vec<PolicyId> = sort_policies(minted_value); let sorted_policies: Vec<PolicyId> = sort_policies(minted_value);
for (index, policy) in sorted_policies.iter().enumerate() { for (index, policy) in sorted_policies.iter().enumerate() {
@ -664,7 +669,7 @@ fn get_script_hash_from_input(input: &TransactionInput, utxos: &UTxOs) -> Option
utxos utxos
.get(&MultiEraInput::from_alonzo_compatible(input)) .get(&MultiEraInput::from_alonzo_compatible(input))
.and_then(MultiEraOutput::as_alonzo) .and_then(MultiEraOutput::as_alonzo)
.and_then(get_payment_part) .and_then(|tx_out| get_payment_part(&tx_out.address))
.and_then(|payment_part| match payment_part { .and_then(|payment_part| match payment_part {
ShelleyPaymentPart::Script(script_hash) => Some(script_hash), ShelleyPaymentPart::Script(script_hash) => Some(script_hash),
_ => None, _ => None,
@ -707,19 +712,6 @@ fn check_minting_policies(
} }
} }
fn compute_native_script_hash(script: &NativeScript) -> PolicyId {
let mut payload = Vec::new();
let _ = encode(script, &mut payload);
payload.insert(0, 0);
pallas_crypto::hash::Hasher::<224>::hash(&payload)
}
fn compute_plutus_script_hash(script: &PlutusScript) -> PolicyId {
let mut payload: Vec<u8> = Vec::from(script.as_ref());
payload.insert(0, 1);
pallas_crypto::hash::Hasher::<224>::hash(&payload)
}
// The owner of each transaction input and each collateral input should have // The owner of each transaction input and each collateral input should have
// signed the transaction. // signed the transaction.
fn check_vkey_input_wits( fn check_vkey_input_wits(
@ -741,7 +733,9 @@ fn check_vkey_input_wits(
match utxos.get(&MultiEraInput::from_alonzo_compatible(input)) { match utxos.get(&MultiEraInput::from_alonzo_compatible(input)) {
Some(multi_era_output) => { Some(multi_era_output) => {
if let Some(alonzo_comp_output) = MultiEraOutput::as_alonzo(multi_era_output) { if let Some(alonzo_comp_output) = MultiEraOutput::as_alonzo(multi_era_output) {
match get_payment_part(alonzo_comp_output).ok_or(Alonzo(InputDecoding))? { match get_payment_part(&alonzo_comp_output.address)
.ok_or(Alonzo(InputDecoding))?
{
ShelleyPaymentPart::Key(payment_key_hash) => { ShelleyPaymentPart::Key(payment_key_hash) => {
check_vk_wit(&payment_key_hash, vk_wits, tx_hash)? check_vk_wit(&payment_key_hash, vk_wits, tx_hash)?
} }
@ -833,8 +827,11 @@ fn check_languages(_mtx: &MintedTx, _prot_pps: &AlonzoProtParams) -> ValidationR
} }
// The metadata of the transaction is valid. // The metadata of the transaction is valid.
fn check_metadata(tx_body: &TransactionBody, mtx: &MintedTx) -> ValidationResult { fn check_auxiliary_data(tx_body: &TransactionBody, mtx: &MintedTx) -> ValidationResult {
match (&tx_body.auxiliary_data_hash, extract_auxiliary_data(mtx)) { match (
&tx_body.auxiliary_data_hash,
aux_data_from_alonzo_minted_tx(mtx),
) {
(Some(metadata_hash), Some(metadata)) => { (Some(metadata_hash), Some(metadata)) => {
if metadata_hash.as_slice() if metadata_hash.as_slice()
== pallas_crypto::hash::Hasher::<256>::hash(metadata).as_ref() == pallas_crypto::hash::Hasher::<256>::hash(metadata).as_ref()
@ -925,7 +922,11 @@ fn check_minting(tx_body: &TransactionBody, mtx: &MintedTx) -> ValidationResult
.map(|x| x.clone().unwrap()) .map(|x| x.clone().unwrap())
.collect(), .collect(),
}; };
let plutus_script_wits: Vec<PlutusScript> = Vec::new(); let plutus_script_wits: Vec<PlutusScript> =
match &mtx.transaction_witness_set.plutus_script {
None => Vec::new(),
Some(plutus_script_wits) => plutus_script_wits.clone(),
};
for (policy, _) in minted_value.iter() { for (policy, _) in minted_value.iter() {
if native_script_wits if native_script_wits
.iter() .iter()

File diff suppressed because it is too large Load diff

View file

@ -1,11 +1,13 @@
//! Logic for validating and applying new blocks and txs to the chain state //! Logic for validating and applying new blocks and txs to the chain state
pub mod alonzo; pub mod alonzo;
pub mod babbage;
pub mod byron; pub mod byron;
pub mod shelley_ma; pub mod shelley_ma;
pub mod utils; pub mod utils;
use alonzo::validate_alonzo_tx; use alonzo::validate_alonzo_tx;
use babbage::validate_babbage_tx;
use byron::validate_byron_tx; use byron::validate_byron_tx;
use pallas_traverse::{Era, MultiEraTx}; use pallas_traverse::{Era, MultiEraTx};
use shelley_ma::validate_shelley_ma_tx; use shelley_ma::validate_shelley_ma_tx;
@ -40,5 +42,11 @@ pub fn validate(metx: &MultiEraTx, utxos: &UTxOs, env: &Environment) -> Validati
} }
_ => Err(TxAndProtParamsDiffer), _ => Err(TxAndProtParamsDiffer),
}, },
MultiEraProtParams::Babbage(bpp) => match metx {
MultiEraTx::Babbage(mtx) => {
validate_babbage_tx(mtx, utxos, bpp, env.block_slot(), env.network_id())
}
_ => Err(TxAndProtParamsDiffer),
},
} }
} }

View file

@ -1,9 +1,10 @@
//! Utilities required for ShelleyMA-era transaction validation. //! Utilities required for ShelleyMA-era transaction validation.
use crate::utils::{ use crate::utils::{
add_minted_value, add_values, empty_value, extract_auxiliary_data, get_alonzo_comp_tx_size, add_minted_value, add_values, aux_data_from_alonzo_minted_tx, empty_value,
get_lovelace_from_alonzo_val, get_payment_part, get_shelley_address, get_val_size_in_words, get_alonzo_comp_tx_size, get_lovelace_from_alonzo_val, get_payment_part, get_shelley_address,
mk_alonzo_vk_wits_check_list, values_are_equal, verify_signature, FeePolicy, get_val_size_in_words, mk_alonzo_vk_wits_check_list, values_are_equal, verify_signature,
FeePolicy,
ShelleyMAError::*, ShelleyMAError::*,
ShelleyProtParams, UTxOs, ShelleyProtParams, UTxOs,
ValidationError::{self, *}, ValidationError::{self, *},
@ -195,7 +196,10 @@ fn check_network_id(tx_body: &TransactionBody, network_id: &u8) -> ValidationRes
} }
fn check_metadata(tx_body: &TransactionBody, mtx: &MintedTx) -> ValidationResult { fn check_metadata(tx_body: &TransactionBody, mtx: &MintedTx) -> ValidationResult {
match (&tx_body.auxiliary_data_hash, extract_auxiliary_data(mtx)) { match (
&tx_body.auxiliary_data_hash,
aux_data_from_alonzo_minted_tx(mtx),
) {
(Some(metadata_hash), Some(metadata)) => { (Some(metadata_hash), Some(metadata)) => {
if metadata_hash.as_slice() if metadata_hash.as_slice()
== pallas_crypto::hash::Hasher::<256>::hash(metadata).as_ref() == pallas_crypto::hash::Hasher::<256>::hash(metadata).as_ref()
@ -222,7 +226,9 @@ fn check_witnesses(
match utxos.get(&MultiEraInput::from_alonzo_compatible(input)) { match utxos.get(&MultiEraInput::from_alonzo_compatible(input)) {
Some(multi_era_output) => { Some(multi_era_output) => {
if let Some(alonzo_comp_output) = MultiEraOutput::as_alonzo(multi_era_output) { if let Some(alonzo_comp_output) = MultiEraOutput::as_alonzo(multi_era_output) {
match get_payment_part(alonzo_comp_output).ok_or(ShelleyMA(AddressDecoding))? { match get_payment_part(&alonzo_comp_output.address)
.ok_or(ShelleyMA(AddressDecoding))?
{
ShelleyPaymentPart::Key(payment_key_hash) => { ShelleyPaymentPart::Key(payment_key_hash) => {
check_vk_wit(&payment_key_hash, tx_hash, vk_wits)? check_vk_wit(&payment_key_hash, tx_hash, vk_wits)?
} }

View file

@ -10,9 +10,12 @@ use pallas_codec::{
utils::{Bytes, KeepRaw, KeyValuePairs}, utils::{Bytes, KeepRaw, KeyValuePairs},
}; };
use pallas_crypto::key::ed25519::{PublicKey, Signature}; use pallas_crypto::key::ed25519::{PublicKey, Signature};
use pallas_primitives::alonzo::{ use pallas_primitives::{
AssetName, AuxiliaryData, Coin, MintedTx, Multiasset, NetworkId, PolicyId, TransactionBody, alonzo::{
TransactionOutput, VKeyWitness, Value, AssetName, AuxiliaryData, Coin, MintedTx as AlonzoMintedTx, Multiasset, NativeScript,
NetworkId, PlutusScript, PolicyId, TransactionBody, VKeyWitness, Value,
},
babbage::{MintedTransactionBody, MintedTx as BabbageMintedTx, PlutusV2Script},
}; };
use pallas_traverse::{MultiEraInput, MultiEraOutput}; use pallas_traverse::{MultiEraInput, MultiEraOutput};
use std::collections::HashMap; use std::collections::HashMap;
@ -29,6 +32,14 @@ pub fn get_alonzo_comp_tx_size(tx_body: &TransactionBody) -> Option<u64> {
} }
} }
pub fn get_babbage_tx_size(tx_body: &MintedTransactionBody) -> Option<u64> {
let mut buff: Vec<u8> = Vec::new();
match encode(tx_body, &mut buff) {
Ok(()) => Some(buff.len() as u64),
Err(_) => None,
}
}
pub fn empty_value() -> Value { pub fn empty_value() -> Value {
Value::Multiasset(0, Multiasset::<Coin>::from(Vec::new())) Value::Multiasset(0, Multiasset::<Coin>::from(Vec::new()))
} }
@ -52,6 +63,61 @@ pub fn add_values(
} }
} }
pub fn lovelace_diff_or_fail(
first: &Value,
second: &Value,
err: &ValidationError,
) -> Result<u64, ValidationError> {
match (first, second) {
(Value::Coin(f), Value::Coin(s)) => {
if f >= s {
Ok(f - s)
} else {
Err(err.clone())
}
}
(Value::Coin(_), Value::Multiasset(_, _)) => Err(err.clone()),
(Value::Multiasset(f, fma), Value::Coin(s)) => {
if f >= s && fma.is_empty() {
Ok(f - s)
} else {
Err(err.clone())
}
}
(Value::Multiasset(f, fma), Value::Multiasset(s, sma)) => {
if f >= s && multi_assets_are_equal(fma, sma) {
Ok(f - s)
} else {
Err(err.clone())
}
}
}
}
pub fn multi_assets_are_equal(fma: &Multiasset<Coin>, sma: &Multiasset<Coin>) -> bool {
for (fpolicy, fassets) in fma.iter() {
match find_policy(sma, fpolicy) {
Some(sassets) => {
for (fasset_name, famount) in fassets.iter() {
// Discard the case where there is 0 of an asset
if *famount != 0 {
match find_assets(&sassets, fasset_name) {
Some(samount) => {
if *famount != samount {
return false;
}
}
None => return false,
};
}
}
}
None => return false,
}
}
true
}
pub fn add_minted_value( pub fn add_minted_value(
base_value: &Value, base_value: &Value,
minted_value: &Multiasset<i64>, minted_value: &Multiasset<i64>,
@ -155,24 +221,7 @@ pub fn values_are_equal(first: &Value, second: &Value) -> bool {
if f != s { if f != s {
false false
} else { } else {
for (fpolicy, fassets) in fma.iter() { multi_assets_are_equal(fma, sma)
match find_policy(sma, fpolicy) {
Some(sassets) => {
for (fasset_name, famount) in fassets.iter() {
match find_assets(&sassets, fasset_name) {
Some(samount) => {
if *famount != samount {
return false;
}
}
None => return false,
};
}
}
None => return false,
}
}
true
} }
} }
} }
@ -235,8 +284,8 @@ pub fn verify_signature(vk_wit: &VKeyWitness, data_to_verify: &[u8]) -> bool {
public_key.verify(data_to_verify, &sig) public_key.verify(data_to_verify, &sig)
} }
pub fn get_payment_part(tx_out: &TransactionOutput) -> Option<ShelleyPaymentPart> { pub fn get_payment_part(address: &Bytes) -> Option<ShelleyPaymentPart> {
let addr: ShelleyAddress = get_shelley_address(Bytes::deref(&tx_out.address))?; let addr: ShelleyAddress = get_shelley_address(Bytes::deref(address))?;
Some(addr.payment().clone()) Some(addr.payment().clone())
} }
@ -247,7 +296,17 @@ pub fn get_shelley_address(address: &[u8]) -> Option<ShelleyAddress> {
} }
} }
pub fn extract_auxiliary_data<'a>(mtx: &'a MintedTx) -> Option<&'a [u8]> { pub fn is_byron_address(address: &[u8]) -> bool {
matches!(Address::from_bytes(address), Ok(Address::Byron(_)))
}
pub fn aux_data_from_alonzo_minted_tx<'a>(mtx: &'a AlonzoMintedTx) -> Option<&'a [u8]> {
Option::<KeepRaw<AuxiliaryData>>::from((mtx.auxiliary_data).clone())
.as_ref()
.map(KeepRaw::raw_cbor)
}
pub fn aux_data_from_babbage_minted_tx<'a>(mtx: &'a BabbageMintedTx) -> Option<&'a [u8]> {
Option::<KeepRaw<AuxiliaryData>>::from((mtx.auxiliary_data).clone()) Option::<KeepRaw<AuxiliaryData>>::from((mtx.auxiliary_data).clone())
.as_ref() .as_ref()
.map(KeepRaw::raw_cbor) .map(KeepRaw::raw_cbor)
@ -258,3 +317,22 @@ pub fn get_val_size_in_words(val: &Value) -> u64 {
let _ = encode(val, &mut tx_buf); let _ = encode(val, &mut tx_buf);
(tx_buf.len() as u64 + 7) / 8 // ceiling of the result of dividing (tx_buf.len() as u64 + 7) / 8 // ceiling of the result of dividing
} }
pub fn compute_native_script_hash(script: &NativeScript) -> PolicyId {
let mut payload = Vec::new();
let _ = encode(script, &mut payload);
payload.insert(0, 0);
pallas_crypto::hash::Hasher::<224>::hash(&payload)
}
pub fn compute_plutus_script_hash(script: &PlutusScript) -> PolicyId {
let mut payload: Vec<u8> = Vec::from(script.as_ref());
payload.insert(0, 1);
pallas_crypto::hash::Hasher::<224>::hash(&payload)
}
pub fn compute_plutus_v2_script_hash(script: &PlutusV2Script) -> PolicyId {
let mut payload: Vec<u8> = Vec::from(script.as_ref());
payload.insert(0, 1);
pallas_crypto::hash::Hasher::<224>::hash(&payload)
}

View file

@ -16,6 +16,7 @@ pub enum MultiEraProtParams {
Byron(ByronProtParams), Byron(ByronProtParams),
Shelley(ShelleyProtParams), Shelley(ShelleyProtParams),
Alonzo(AlonzoProtParams), Alonzo(AlonzoProtParams),
Babbage(BabbageProtParams),
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -51,6 +52,20 @@ pub struct AlonzoProtParams {
pub coins_per_utxo_word: u64, pub coins_per_utxo_word: u64,
} }
#[derive(Debug, Clone)]
pub struct BabbageProtParams {
pub fee_policy: FeePolicy,
pub max_tx_size: u64,
pub max_block_ex_mem: u64,
pub max_block_ex_steps: u64,
pub max_tx_ex_mem: u32,
pub max_tx_ex_steps: u64,
pub max_val_size: u64,
pub collateral_percent: u64,
pub max_collateral_inputs: u64,
pub coins_per_utxo_word: u64,
}
impl Environment { impl Environment {
pub fn prot_params(&self) -> &MultiEraProtParams { pub fn prot_params(&self) -> &MultiEraProtParams {
&self.prot_params &self.prot_params

View file

@ -7,6 +7,7 @@ pub enum ValidationError {
Byron(ByronError), Byron(ByronError),
ShelleyMA(ShelleyMAError), ShelleyMA(ShelleyMAError),
Alonzo(AlonzoError), Alonzo(AlonzoError),
Babbage(BabbageError),
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -91,4 +92,51 @@ pub enum AlonzoError {
ScriptIntegrityHash, ScriptIntegrityHash,
} }
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum BabbageError {
UnknownTxSize,
TxInsEmpty,
InputNotInUTxO,
CollateralNotInUTxO,
ReferenceInputNotInUTxO,
RefInputNotInUTxO,
BlockPrecedesValInt,
BlockExceedsValInt,
FeeBelowMin,
CollateralMissing,
TooManyCollaterals,
InputDecoding,
CollateralNotVKeyLocked,
CollateralMinLovelace,
NonLovelaceCollateral,
CollateralWrongAssets,
NegativeValue,
CollateralAnnotation,
PreservationOfValue,
MinLovelaceUnreached,
MaxValSizeExceeded,
AddressDecoding,
OutputWrongNetworkID,
TxWrongNetworkID,
TxExUnitsExceeded,
RedeemerMissing,
UnneededRedeemer,
MaxTxSizeExceeded,
MintingLacksPolicy,
MetadataHash,
DatumMissing,
UnneededDatum,
ScriptWitnessMissing,
UnneededNativeScript,
UnneededPlutusV1Script,
UnneededPlutusV2Script,
ReqSignerMissing,
ReqSignerWrongSig,
VKWitnessMissing,
VKWrongSignature,
UnsupportedPlutusLanguage,
ScriptIntegrityHash,
}
pub type ValidationResult = Result<(), ValidationError>; pub type ValidationResult = Result<(), ValidationError>;

View file

@ -87,3 +87,42 @@ List of negative unit tests:
- **min_lovelace_unreached** takes sucessful_mainnet_tx and submits validation on it with an environment requesting more lovelace on outputs than the amount actually paid by one of the outputs of the transaction. - **min_lovelace_unreached** takes sucessful_mainnet_tx and submits validation on it with an environment requesting more lovelace on outputs than the amount actually paid by one of the outputs of the transaction.
- **max_val_exceeded** takes sucessful_mainnet_tx and submits validation on it with an environment disallowing value sizes as high as the size ofg one of the values in one of the transaction outputs of sucessful_mainnet_tx. - **max_val_exceeded** takes sucessful_mainnet_tx and submits validation on it with an environment disallowing value sizes as high as the size ofg one of the values in one of the transaction outputs of sucessful_mainnet_tx.
- **script_integrity_hash** takes sucessful_mainnet_tx_with_plutus_script and modifies the execution values of one of the redeemers in the witness set of the transaction, in such a way that all checks pass but the integrity hash of script-related data of the transaction is different from the script data hash contained in the body of the transaction. - **script_integrity_hash** takes sucessful_mainnet_tx_with_plutus_script and modifies the execution values of one of the redeemers in the witness set of the transaction, in such a way that all checks pass but the integrity hash of script-related data of the transaction is different from the script data hash contained in the body of the transaction.
### Babbage
*pallas-applying/tests/babbage.rs* contains multiple unit tests for validation in the Alonzo era.
Babbage introduces novel ways to provide Plutus-script-related data, like the introduction of reference scripts and novel ways to provide for collateral.
List of positive unit tests:
- **successful_mainnet_tx** ([here](https://cexplorer.io/tx/b17d685c42e714238c1fb3abcd40e5c6291ebbb420c9c69b641209607bd00c7d) to see on Cardano explorer) is a simple Babbage transaction, with no native, Plutus V1 or Plutus V2 scripts, nor metadata or minting.
- **successful_mainnet_tx_with_plutus_v1_script** ([here](https://cexplorer.io/tx/f33d6f7eb877132af7307e385bb24a7d2c12298c8ac0b1460296748810925ccc) to see on Cardano explorer) is a Babbage transaction with a Plutus V1 script.
- **successful_mainnet_tx_with_plutus_v2_script** ([here](https://cexplorer.io/tx/ac96a0a2dfdb876b237a8ae674eadab453fd146fb97b221cfd29a1812046fa36) to see on Cardano explorer) is a Babbage transaction with a Plutus V2 script.
- **successful_mainnet_tx_with_minting** ([here](https://cexplorer.io/tx/8702b0a5835c16663101f68295e33e3b3868c487f736d3c8a0a4246242675a15) to see on Cardano explorer) is a simple Babbage transaction with minting.
- **successful_mainnet_tx_with_metadata** ([here](https://cexplorer.io/tx/7ae8cbe887d5d4cdaa51bce93d296206d4fcc77963e65fad3a64d0e6df672260) to see on Cardano explorer) is a simple Babbage transaction with metadata.
List of negative unit tests:
- **empty_ins** takes successful_mainnet_tx and removes its input.
- **unfound_utxo_input** takes successful_mainnet_tx and calls validation on it with an empty UTxO (which causes the input to be unfound).
- **validity_interval_lower_bound_unreached** takes sucessful_mainnet_tx and modifies its time interval in such a way that its validity time interval *lower* bound is located exactly one slot after the block slot.
- **validity_interval_upper_bound_surpassed** takes sucessful_mainnt_tx and modifies its time interval in such a way that its validity time interval *upper* bound is located exactly one slot before the block slot.
- **min_fees_unreached** submits validation on sucessful_mainnet_tx with an environment requesting the minimum fee to be higher than the one that the transaction actually paid.
- **no_collateral_inputs** takes successful_mainnet_tx_with_plutus_v1_script and removes its collateral inputs before submitting the transaction for validation.
- **too_many_collateral_inputs** takes successful_mainnet_tx_with_plutus_v1_script and submits its for validation with an environment allowing no collateral inputs.
- **collateral_is_not_verification_key_locked** takes sucessful_mainnet_tx_with_plutus_v1_script and modifies the address of one of the collateral inputs to become a script-locked output instead of a verification-key-locked one.
- **collateral_with_other_assets** takes sucessful_mainnet_tx_with_plutus_v1_script and adds non-lovelace assets to it.
- **collateral_without_min_lovelace** takes sucessful_mainnet_tx_with_plutus_v1_script and submits it for validation with an environment requesting a higher lovelace percentage (when compared to the fee paid by the transaction) in collateral inputs than the actual amount paid by the transaction collateral.
- **collateral_annotation** takes sucessful_mainnet_tx_with_plutus_v1_script and modifies the collateral annotation to make it wrong.
- **preservation_of_value** modifies sucessful_mainnet_tx_with_plutus_v1_script in such a way that the preservation-of-value equality does not hold.
- **min_lovelace_unreached** takes sucessful_mainnet_tx and submits validation on it with an environment requesting more lovelace on outputs than the amount actually paid by one of the outputs of the transaction.
- **max_val_exceeded** takes sucessful_mainnet_tx and submits validation on it with an environment disallowing value sizes as high as the size ofg one of the values in one of the transaction outputs of sucessful_mainnet_tx.
- **output_network_id** takes sucessful_mainnet_tx and modifies the network ID in the address of one of its outputs.
- **tx_network_id** takes sucessful_mainnet_tx and modifies its network ID.
- **tx_ex_units_exceeded** takes sucessful_mainnet_tx_with_plutus_v1_script and validates it with an environment whose Plutus script execution values are below the needs of the transaction.
- **max_tx_size_exceeded** takes sucessful_mainnet_tx and validates it with an environment allowing only transactions whose size is lower than that of sucessful_mainnet_tx.
- **minting_lacks_policy** takes sucessful_mainnet_tx_with_minting and removes the native script policy contained in it before submitting it for validation.
- **auxiliary_data_removed** takes sucessful_mainnet_tx_with_metadata and removes its auxiliary data (a.k.a. metadata).
- **script_input_lacks_script** takes sucessful_mainnet_tx_with_plutus_v1_script and clears the Plutus V1 scripts list in the witness set, making the script required by the transaction inexistent.
- **missing_input_datum** takes sucessful_mainnet_tx_with_plutus_v1_script and removes the datum contained in its witness set.
- **extra_input_datum** takes sucessful_mainnet_tx_with_plutus_v1_script and adds an unneded datum to its witness set.
- **extra_redeemer** takes sucessful_mainnet_tx_with_plutus_v1_script and adds an unneeded redeemer to its witness set.
- **script_integrity_hash** takes sucessful_mainnet_tx_with_plutus_v1_script and modifies the execution values of one of the redeemers in the witness set of the transaction, in such a way that all checks pass but the integrity hash of script-related data of the transaction is different from the script data hash contained in the body of the transaction.

View file

@ -148,7 +148,7 @@ mod alonzo_tests {
), ),
], ],
); );
add_collateral( add_collateral_alonzo(
&mtx.transaction_body, &mtx.transaction_body,
&mut utxos, &mut utxos,
&[( &[(
@ -315,7 +315,7 @@ mod alonzo_tests {
} }
#[test] #[test]
// Same as successful_mainnet_tx, but the validation is called with an empty // Same as successful_mainnet_tx, but validation is called with an empty
// UTxO set. // UTxO set.
fn unfound_utxo_input() { fn unfound_utxo_input() {
let cbor_bytes: Vec<u8> = cbor_to_bytes(include_str!("../../test_data/alonzo1.tx")); let cbor_bytes: Vec<u8> = cbor_to_bytes(include_str!("../../test_data/alonzo1.tx"));
@ -454,7 +454,7 @@ mod alonzo_tests {
#[test] #[test]
// Same as succesful_mainnet_tx, except that validation is called with an // Same as succesful_mainnet_tx, except that validation is called with an
// Environment requesting fees that exceed those paid by the transaction. // Environment requesting fees that exceed those paid by the transaction.
fn min_fees_unreached() { fn min_fee_unreached() {
let cbor_bytes: Vec<u8> = cbor_to_bytes(include_str!("../../test_data/alonzo1.tx")); let cbor_bytes: Vec<u8> = cbor_to_bytes(include_str!("../../test_data/alonzo1.tx"));
let mtx: MintedTx = minted_tx_from_cbor(&cbor_bytes); let mtx: MintedTx = minted_tx_from_cbor(&cbor_bytes);
let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Alonzo); let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Alonzo);
@ -573,7 +573,7 @@ mod alonzo_tests {
), ),
], ],
); );
add_collateral( add_collateral_alonzo(
&mtx.transaction_body, &mtx.transaction_body,
&mut utxos, &mut utxos,
&[( &[(
@ -698,7 +698,7 @@ mod alonzo_tests {
), ),
], ],
); );
add_collateral( add_collateral_alonzo(
&mtx.transaction_body, &mtx.transaction_body,
&mut utxos, &mut utxos,
&[( &[(
@ -954,7 +954,7 @@ mod alonzo_tests {
), ),
], ],
); );
add_collateral( add_collateral_alonzo(
&mtx.transaction_body, &mtx.transaction_body,
&mut utxos, &mut utxos,
&[( &[(
@ -1082,7 +1082,7 @@ mod alonzo_tests {
), ),
], ],
); );
add_collateral( add_collateral_alonzo(
&mtx.transaction_body, &mtx.transaction_body,
&mut utxos, &mut utxos,
&[( &[(
@ -1122,7 +1122,7 @@ mod alonzo_tests {
#[test] #[test]
// Same as succesful_mainnet_tx, except that the fee is reduced by exactly 1, // Same as succesful_mainnet_tx, except that the fee is reduced by exactly 1,
// and so the "preservation of value" property doesn't hold. // and so the "preservation of value" property does not hold.
fn preservation_of_value() { fn preservation_of_value() {
let cbor_bytes: Vec<u8> = cbor_to_bytes(include_str!("../../test_data/alonzo1.tx")); let cbor_bytes: Vec<u8> = cbor_to_bytes(include_str!("../../test_data/alonzo1.tx"));
let mut mtx: MintedTx = minted_tx_from_cbor(&cbor_bytes); let mut mtx: MintedTx = minted_tx_from_cbor(&cbor_bytes);
@ -1162,7 +1162,7 @@ mod alonzo_tests {
network_id: 1, network_id: 1,
}; };
match validate(&metx, &utxos, &env) { match validate(&metx, &utxos, &env) {
Ok(()) => panic!("Preservation of value doesn't hold"), Ok(()) => panic!("Preservation of value does not hold"),
Err(err) => match err { Err(err) => match err {
Alonzo(AlonzoError::PreservationOfValue) => (), Alonzo(AlonzoError::PreservationOfValue) => (),
_ => panic!("Unexpected error ({:?})", err), _ => panic!("Unexpected error ({:?})", err),
@ -1232,7 +1232,7 @@ mod alonzo_tests {
network_id: 1, network_id: 1,
}; };
match validate(&metx, &utxos, &env) { match validate(&metx, &utxos, &env) {
Ok(()) => panic!("Transaction network ID should match environment network_id"), Ok(()) => panic!("Output network ID should match environment network ID"),
Err(err) => match err { Err(err) => match err {
Alonzo(AlonzoError::OutputWrongNetworkID) => (), Alonzo(AlonzoError::OutputWrongNetworkID) => (),
_ => panic!("Unexpected error ({:?})", err), _ => panic!("Unexpected error ({:?})", err),
@ -1282,7 +1282,7 @@ mod alonzo_tests {
network_id: 1, network_id: 1,
}; };
match validate(&metx, &utxos, &env) { match validate(&metx, &utxos, &env) {
Ok(()) => panic!("Transaction network ID should match environment network_id"), Ok(()) => panic!("Transaction network ID should match environment network ID"),
Err(err) => match err { Err(err) => match err {
Alonzo(AlonzoError::TxWrongNetworkID) => (), Alonzo(AlonzoError::TxWrongNetworkID) => (),
_ => panic!("Unexpected error ({:?})", err), _ => panic!("Unexpected error ({:?})", err),
@ -1292,7 +1292,7 @@ mod alonzo_tests {
#[test] #[test]
// Same as successful_mainnet_tx_with_plutus_script, except that the Environment // Same as successful_mainnet_tx_with_plutus_script, except that the Environment
// execution values are below the ones assocaited with the transaction. // execution values are below the ones associated with the transaction.
fn tx_ex_units_exceeded() { fn tx_ex_units_exceeded() {
let cbor_bytes: Vec<u8> = cbor_to_bytes(include_str!("../../test_data/alonzo2.tx")); let cbor_bytes: Vec<u8> = cbor_to_bytes(include_str!("../../test_data/alonzo2.tx"));
let mtx: MintedTx = minted_tx_from_cbor(&cbor_bytes); let mtx: MintedTx = minted_tx_from_cbor(&cbor_bytes);
@ -1369,7 +1369,7 @@ mod alonzo_tests {
), ),
], ],
); );
add_collateral( add_collateral_alonzo(
&mtx.transaction_body, &mtx.transaction_body,
&mut utxos, &mut utxos,
&[( &[(
@ -1533,7 +1533,7 @@ mod alonzo_tests {
), ),
], ],
); );
add_collateral( add_collateral_alonzo(
&mtx.transaction_body, &mtx.transaction_body,
&mut utxos, &mut utxos,
&[( &[(
@ -1768,7 +1768,7 @@ mod alonzo_tests {
), ),
], ],
); );
add_collateral( add_collateral_alonzo(
&mtx.transaction_body, &mtx.transaction_body,
&mut utxos, &mut utxos,
&[( &[(
@ -1891,7 +1891,7 @@ mod alonzo_tests {
), ),
], ],
); );
add_collateral( add_collateral_alonzo(
&mtx.transaction_body, &mtx.transaction_body,
&mut utxos, &mut utxos,
&[( &[(
@ -1986,7 +1986,7 @@ mod alonzo_tests {
network_id: 1, network_id: 1,
}; };
match validate(&metx, &utxos, &env) { match validate(&metx, &utxos, &env) {
Ok(()) => panic!("Minting policy is not supported by native script"), Ok(()) => panic!("Minting policy is not supported by the correponding native script"),
Err(err) => match err { Err(err) => match err {
Alonzo(AlonzoError::MintingLacksPolicy) => (), Alonzo(AlonzoError::MintingLacksPolicy) => (),
_ => panic!("Unexpected error ({:?})", err), _ => panic!("Unexpected error ({:?})", err),
@ -2072,7 +2072,7 @@ mod alonzo_tests {
), ),
], ],
); );
add_collateral( add_collateral_alonzo(
&mtx.transaction_body, &mtx.transaction_body,
&mut utxos, &mut utxos,
&[( &[(
@ -2195,7 +2195,7 @@ mod alonzo_tests {
), ),
], ],
); );
add_collateral( add_collateral_alonzo(
&mtx.transaction_body, &mtx.transaction_body,
&mut utxos, &mut utxos,
&[( &[(
@ -2324,7 +2324,7 @@ mod alonzo_tests {
), ),
], ],
); );
add_collateral( add_collateral_alonzo(
&mtx.transaction_body, &mtx.transaction_body,
&mut utxos, &mut utxos,
&[( &[(
@ -2454,7 +2454,7 @@ mod alonzo_tests {
), ),
], ],
); );
add_collateral( add_collateral_alonzo(
&mtx.transaction_body, &mtx.transaction_body,
&mut utxos, &mut utxos,
&[( &[(
@ -2536,7 +2536,7 @@ mod alonzo_tests {
network_id: 1, network_id: 1,
}; };
match validate(&metx, &utxos, &env) { match validate(&metx, &utxos, &env) {
Ok(()) => panic!("Unneeded redeemer"), Ok(()) => panic!("Transaction auxiliary data removed"),
Err(err) => match err { Err(err) => match err {
Alonzo(AlonzoError::MetadataHash) => (), Alonzo(AlonzoError::MetadataHash) => (),
_ => panic!("Unexpected error ({:?})", err), _ => panic!("Unexpected error ({:?})", err),
@ -2545,7 +2545,7 @@ mod alonzo_tests {
} }
#[test] #[test]
// Same as successful_mainnet_tx, except that the minimum lovelace in the UTxO // Same as successful_mainnet_tx, except that the minimum lovelace in an output
// is unreached. // is unreached.
fn min_lovelace_unreached() { fn min_lovelace_unreached() {
let cbor_bytes: Vec<u8> = cbor_to_bytes(include_str!("../../test_data/alonzo1.tx")); let cbor_bytes: Vec<u8> = cbor_to_bytes(include_str!("../../test_data/alonzo1.tx"));
@ -2573,14 +2573,14 @@ mod alonzo_tests {
max_val_size: 5000, max_val_size: 5000,
collateral_percent: 150, collateral_percent: 150,
max_collateral_inputs: 3, max_collateral_inputs: 3,
coins_per_utxo_word: 10000000, coins_per_utxo_word: 10000000, // This was 34482 during Alonzo on mainnet.
}), }),
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 44237276, block_slot: 44237276,
network_id: 1, network_id: 1,
}; };
match validate(&metx, &utxos, &env) { match validate(&metx, &utxos, &env) {
Ok(()) => panic!("Unneeded redeemer"), Ok(()) => panic!("Output minimum lovelace is unreached"),
Err(err) => match err { Err(err) => match err {
Alonzo(AlonzoError::MinLovelaceUnreached) => (), Alonzo(AlonzoError::MinLovelaceUnreached) => (),
_ => panic!("Unexpected error ({:?})", err), _ => panic!("Unexpected error ({:?})", err),
@ -2624,7 +2624,7 @@ mod alonzo_tests {
network_id: 1, network_id: 1,
}; };
match validate(&metx, &utxos, &env) { match validate(&metx, &utxos, &env) {
Ok(()) => panic!("Unneeded redeemer"), Ok(()) => panic!("Max value size exceeded"),
Err(err) => match err { Err(err) => match err {
Alonzo(AlonzoError::MaxValSizeExceeded) => (), Alonzo(AlonzoError::MaxValSizeExceeded) => (),
_ => panic!("Unexpected error ({:?})", err), _ => panic!("Unexpected error ({:?})", err),
@ -2712,7 +2712,7 @@ mod alonzo_tests {
), ),
], ],
); );
add_collateral( add_collateral_alonzo(
&mtx.transaction_body, &mtx.transaction_body,
&mut utxos, &mut utxos,
&[( &[(

File diff suppressed because one or more lines are too long

View file

@ -2,12 +2,17 @@ use pallas_applying::UTxOs;
use pallas_codec::{minicbor::bytes::ByteVec, utils::TagWrap}; use pallas_codec::{minicbor::bytes::ByteVec, utils::TagWrap};
use pallas_primitives::{ use pallas_primitives::{
alonzo::{MintedTx, TransactionBody, TransactionOutput, Value}, alonzo::{MintedTx, TransactionBody, TransactionOutput, Value},
babbage::{
MintedDatumOption, MintedPostAlonzoTransactionOutput, MintedScriptRef,
MintedTransactionBody, MintedTransactionOutput, MintedTx as BabbageMintedTx,
PseudoTransactionOutput,
},
byron::{Address, MintedTxPayload, Tx, TxOut}, byron::{Address, MintedTxPayload, Tx, TxOut},
}; };
use pallas_traverse::{MultiEraInput, MultiEraOutput}; use pallas_traverse::{MultiEraInput, MultiEraOutput};
use std::{borrow::Cow, iter::zip, vec::Vec}; use std::{borrow::Cow, iter::zip, vec::Vec};
use pallas_codec::utils::Bytes; use pallas_codec::utils::{Bytes, CborWrap};
use pallas_crypto::hash::Hash; use pallas_crypto::hash::Hash;
pub fn cbor_to_bytes(input: &str) -> Vec<u8> { pub fn cbor_to_bytes(input: &str) -> Vec<u8> {
@ -18,8 +23,12 @@ pub fn minted_tx_from_cbor(tx_cbor: &[u8]) -> MintedTx<'_> {
pallas_codec::minicbor::decode::<MintedTx>(tx_cbor).unwrap() pallas_codec::minicbor::decode::<MintedTx>(tx_cbor).unwrap()
} }
pub fn minted_tx_payload_from_cbor(tx_cbor: &[u8]) -> MintedTxPayload<'_> { pub fn babbage_minted_tx_from_cbor(tx_cbor: &[u8]) -> BabbageMintedTx<'_> {
pallas_codec::minicbor::decode::<MintedTxPayload>(tx_cbor).unwrap() pallas_codec::minicbor::decode::<BabbageMintedTx>(&tx_cbor[..]).unwrap()
}
pub fn minted_tx_payload_from_cbor<'a>(tx_cbor: &'a Vec<u8>) -> MintedTxPayload<'a> {
pallas_codec::minicbor::decode::<MintedTxPayload>(&tx_cbor[..]).unwrap()
} }
pub fn mk_utxo_for_byron_tx<'a>(tx: &Tx, tx_outs_info: &[(String, u64)]) -> UTxOs<'a> { pub fn mk_utxo_for_byron_tx<'a>(tx: &Tx, tx_outs_info: &[(String, u64)]) -> UTxOs<'a> {
@ -45,10 +54,16 @@ pub fn mk_utxo_for_byron_tx<'a>(tx: &Tx, tx_outs_info: &[(String, u64)]) -> UTxO
pub fn mk_utxo_for_alonzo_compatible_tx<'a>( pub fn mk_utxo_for_alonzo_compatible_tx<'a>(
tx_body: &TransactionBody, tx_body: &TransactionBody,
tx_outs_info: &[(String, Value, Option<Hash<32>>)], tx_outs_info: &[(
String, // address in string format
Value,
Option<Hash<32>>,
)],
) -> UTxOs<'a> { ) -> UTxOs<'a> {
let mut utxos: UTxOs = UTxOs::new(); let mut utxos: UTxOs = UTxOs::new();
for (tx_in, (address, amount, datum_hash)) in zip(tx_body.inputs.clone(), tx_outs_info) { for (tx_in, (address, amount, datum_hash)) in zip(tx_body.inputs.clone(), tx_outs_info) {
let multi_era_in: MultiEraInput =
MultiEraInput::AlonzoCompatible(Box::new(Cow::Owned(tx_in)));
let address_bytes: Bytes = match hex::decode(address) { let address_bytes: Bytes = match hex::decode(address) {
Ok(bytes_vec) => Bytes::from(bytes_vec), Ok(bytes_vec) => Bytes::from(bytes_vec),
_ => panic!("Unable to decode input address"), _ => panic!("Unable to decode input address"),
@ -58,8 +73,6 @@ pub fn mk_utxo_for_alonzo_compatible_tx<'a>(
amount: amount.clone(), amount: amount.clone(),
datum_hash: *datum_hash, datum_hash: *datum_hash,
}; };
let multi_era_in: MultiEraInput =
MultiEraInput::AlonzoCompatible(Box::new(Cow::Owned(tx_in)));
let multi_era_out: MultiEraOutput = let multi_era_out: MultiEraOutput =
MultiEraOutput::AlonzoCompatible(Box::new(Cow::Owned(tx_out))); MultiEraOutput::AlonzoCompatible(Box::new(Cow::Owned(tx_out)));
utxos.insert(multi_era_in, multi_era_out); utxos.insert(multi_era_in, multi_era_out);
@ -67,10 +80,44 @@ pub fn mk_utxo_for_alonzo_compatible_tx<'a>(
utxos utxos
} }
pub fn add_collateral( pub fn mk_utxo_for_babbage_tx<'a>(
tx_body: &MintedTransactionBody,
tx_outs_info: &'a [(
String, // address in string format
Value,
Option<MintedDatumOption>,
Option<CborWrap<MintedScriptRef>>,
)],
) -> UTxOs<'a> {
let mut utxos: UTxOs = UTxOs::new();
for (tx_in, (addr, val, datum_opt, script_ref)) in zip(tx_body.inputs.clone(), tx_outs_info) {
let multi_era_in: MultiEraInput =
MultiEraInput::AlonzoCompatible(Box::new(Cow::Owned(tx_in)));
let address_bytes: Bytes = match hex::decode(addr) {
Ok(bytes_vec) => Bytes::from(bytes_vec),
_ => panic!("Unable to decode input address"),
};
let tx_out: MintedTransactionOutput =
PseudoTransactionOutput::PostAlonzo(MintedPostAlonzoTransactionOutput {
address: address_bytes,
value: val.clone(),
datum_option: datum_opt.clone(),
script_ref: script_ref.clone(),
});
let multi_era_out: MultiEraOutput = MultiEraOutput::Babbage(Box::new(Cow::Owned(tx_out)));
utxos.insert(multi_era_in, multi_era_out);
}
utxos
}
pub fn add_collateral_alonzo<'a>(
tx_body: &TransactionBody, tx_body: &TransactionBody,
utxos: &mut UTxOs<'_>, utxos: &mut UTxOs<'_>,
collateral_info: &[(String, Value, Option<Hash<32>>)], collateral_info: &[(
String, // address in string format
Value,
Option<Hash<32>>,
)],
) { ) {
match &tx_body.collateral { match &tx_body.collateral {
Some(collaterals) => { Some(collaterals) => {
@ -94,3 +141,85 @@ pub fn add_collateral(
None => panic!("Adding collateral to UTxO failed due to an empty list of collaterals"), None => panic!("Adding collateral to UTxO failed due to an empty list of collaterals"),
} }
} }
pub fn add_collateral_babbage<'a>(
tx_body: &MintedTransactionBody,
utxos: &mut UTxOs<'a>,
collateral_info: &'a [(
String, // address in string format
Value,
Option<MintedDatumOption>,
Option<CborWrap<MintedScriptRef>>,
)],
) {
match &tx_body.collateral {
Some(collaterals) => {
if collaterals.is_empty() {
panic!("UTxO addition error - collateral input missing")
} else {
for (tx_in, (addr, val, datum_opt, script_ref)) in
zip(collaterals.clone(), collateral_info)
{
let multi_era_in: MultiEraInput =
MultiEraInput::AlonzoCompatible(Box::new(Cow::Owned(tx_in)));
let address_bytes: Bytes = match hex::decode(addr) {
Ok(bytes_vec) => Bytes::from(bytes_vec),
_ => panic!("Unable to decode input address"),
};
let tx_out: MintedTransactionOutput =
PseudoTransactionOutput::PostAlonzo(MintedPostAlonzoTransactionOutput {
address: address_bytes,
value: val.clone(),
datum_option: datum_opt.clone(),
script_ref: script_ref.clone(),
});
let multi_era_out: MultiEraOutput =
MultiEraOutput::Babbage(Box::new(Cow::Owned(tx_out)));
utxos.insert(multi_era_in, multi_era_out);
}
}
}
None => panic!("UTxO addition error - collateral input missing"),
}
}
pub fn add_ref_input_babbage<'a>(
tx_body: &MintedTransactionBody,
utxos: &mut UTxOs<'a>,
ref_input_info: &'a [(
String, // address in string format
Value,
Option<MintedDatumOption>,
Option<CborWrap<MintedScriptRef>>,
)],
) {
match &tx_body.reference_inputs {
Some(ref_inputs) => {
if ref_inputs.is_empty() {
panic!("UTxO addition error - reference input missing")
} else {
for (tx_in, (addr, val, datum_opt, script_ref)) in
zip(ref_inputs.clone(), ref_input_info)
{
let multi_era_in: MultiEraInput =
MultiEraInput::AlonzoCompatible(Box::new(Cow::Owned(tx_in)));
let address_bytes: Bytes = match hex::decode(addr) {
Ok(bytes_vec) => Bytes::from(bytes_vec),
_ => panic!("Unable to decode input address"),
};
let tx_out: MintedTransactionOutput =
PseudoTransactionOutput::PostAlonzo(MintedPostAlonzoTransactionOutput {
address: address_bytes,
value: val.clone(),
datum_option: datum_opt.clone(),
script_ref: script_ref.clone(),
});
let multi_era_out: MultiEraOutput =
MultiEraOutput::Babbage(Box::new(Cow::Owned(tx_out)));
utxos.insert(multi_era_in, multi_era_out);
}
}
}
None => panic!("UTxO addition error - reference input missing"),
}
}

View file

@ -0,0 +1 @@
011be1f490912af2fc39f8e3637a2bade2ecbebefe63e8bfef10989cd6f593309a155b0ebb45ff830747e61f98e5b77feaf7529ce9df351382

1
test_data/babbage3.tx Normal file
View file

@ -0,0 +1 @@
84a40081825820f193aa92b0c401c4ab4694622501b4890330e7a4a7a20533d833a5639b7fc9e601018282581d61775a6cb8a363c28daacd0e535c8914df21533ad07458c23a99f9043a1a007a120082583901e241af5d59f4675250fcbf0f6f50e6c203a268955f92e036a5018243f593309a155b0ebb45ff830747e61f98e5b77feaf7529ce9df3513821a05abfc02021a00028cad031a044fa19ea10081825820f2fc4a141e5d2d2678434f851997a71610bf40ae2cd6b18cb67e8f188d8304ba5840f7b3cefab0d828972e6221e8896c336187ba55f8c79fe83d5a6de9c1a32e6ea788629d7294785c76ef49b9fdb7cd7a932fe32c2761d640c19aa7840a44fc060bf5f6

View file

@ -0,0 +1 @@
11a55f409501bf65805bb0dc76f6f9ae90b61e19ed870bc0025681360881728e7ed4cf324e1323135e7e6d931f01e30792d9cdf17129cb806d

View file

@ -0,0 +1 @@
01f1e126304308006938d2e8571842ff87302fff95a037b3fd838451b8b3c9396d0680d912487139cb7fc85aa279ea70e8cdacee4c6cae40fd

View file

@ -0,0 +1 @@
01f1e126304308006938d2e8571842ff87302fff95a037b3fd838451b8b3c9396d0680d912487139cb7fc85aa279ea70e8cdacee4c6cae40fd

1
test_data/babbage4.tx Normal file

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
719b85d5e8611945505f078aeededcbed1d6ca11053f61e3f9d999fe44

View file

@ -0,0 +1 @@
0121316dbc84420a5ee7461438483564c41fae876029319b3ee641fe4422339411d2df4c9c7c50b3d8f88db98d475e9d1bccd4244b412fbe5e

View file

@ -0,0 +1 @@
0121316dbc84420a5ee7461438483564c41fae876029319b3ee641fe4422339411d2df4c9c7c50b3d8f88db98d475e9d1bccd4244b412fbe5e

1
test_data/babbage5.tx Normal file

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
11A55F409501BF65805BB0DC76F6F9AE90B61E19ED870BC0025681360881728E7ED4CF324E1323135E7E6D931F01E30792D9CDF17129CB806D

View file

@ -0,0 +1 @@
01EDA33318624ADE03D53B7E954713D9E69440891F0D02E823267B610D6018DC6C7989A46EC26822425A3D2BAC60EEC2682A022740361ED957

View file

@ -0,0 +1 @@
01eda33318624ade03d53b7e954713d9e69440891f0d02e823267b610d6018dc6c7989a46ec26822425a3d2bac60eec2682a022740361ed957

1
test_data/babbage6.tx Normal file

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
119068A7A3F008803EDAC87AF1619860F2CDCDE40C26987325ACE138AD81728E7ED4CF324E1323135E7E6D931F01E30792D9CDF17129CB806D

View file

@ -0,0 +1 @@
01A7D37F1D43D1197A994D95B3CE15D9AF3B4697CC7CDF9BCD1F81688D3499AC08066B36BC6C2D86A21243B940E84DBE5CAC3FAB5F76AB9229

View file

@ -0,0 +1 @@
01a7d37f1d43d1197a994d95b3ce15d9af3b4697cc7cdf9bcd1f81688d3499ac08066b36bc6c2d86a21243b940e84dbe5cac3fab5f76ab9229

View file

@ -0,0 +1 @@
119068a7a3f008803edac87af1619860f2cdcde40c26987325ace138ad81728e7ed4cf324e1323135e7e6d931f01e30792d9cdf17129cb806d

1
test_data/babbage7.tx Normal file
View file

@ -0,0 +1 @@
84a90082825820806560bebbd9ba3759d091efff97f007ce82fad9bc53d1d8a6d44f8760d6f76100825820a349f9146f6c638505e3dbd2625af1200dbaf4fd17bb8513bb966db70147e30d04018682583901f434b2c9818daa419e631a520e6bb98437add6c658fee52c1e07a20d6e4af7adcff67f1c1e7e5805b7c9155125092dd5dee927dd43ec64301a000f32a08258390170e60f3b5ea7153e0acc7a803e4401d44b8ed1bae1c7baaad1a62a721e78aae7c90cc36d624f7b3bb6d86b52696dc84e490f343eba89005f1a000f32a0825839011a5bd6e94501aa6b9a36a221b4dec2ef7ec007c99dcb7c54416428abbe45266367e628fb1ce04d4dee752dbf2e79ad91d22437b400cec0ae1a0098b5c082583901a7d37f1d43d1197a994d95b3ce15d9af3b4697cc7cdf9bcd1f81688d3499ac08066b36bc6c2d86a21243b940e84dbe5cac3fab5f76ab9229821a0011e360a1581c95ab9a125c900c14cf7d39093e3577b0c8e39c9f7548a8301a28ee2da14c4164614964696f74313132350182583901a7d37f1d43d1197a994d95b3ce15d9af3b4697cc7cdf9bcd1f81688d3499ac08066b36bc6c2d86a21243b940e84dbe5cac3fab5f76ab92291a068cc3d782583901a7d37f1d43d1197a994d95b3ce15d9af3b4697cc7cdf9bcd1f81688d3499ac08066b36bc6c2d86a21243b940e84dbe5cac3fab5f76ab92291a0686852e021a00063ea9031a04b263ec081a04b259600b58200c38855f28468901ccd7d2537b7b9438d84573f1d69fa43271a54b645fbbb4b00d81825820d53715d1b45852588d554b4fb14d78132e0c0022141fd30fe07a305c45f93827000e81581ca7d37f1d43d1197a994d95b3ce15d9af3b4697cc7cdf9bcd1f81688d12818258209a32459bd4ef6bbafdeb8cf3b909d0e3e2ec806e4cc6268529280b0fc1d06f5b00a300818258204bdb6862c2c613165eecf49a460d5abb885c258d95cd5bc3e867693a2f5a862b58405816ff0da3d3c152f5cc8f301b05ac54233f1a3ec9351e267150677523ce1bb55d69b1a17ee3e170fe2d2ea9b142c4190e192c9de3859e610d14359aa137d60c049fd8799f581c1a5bd6e94501aa6b9a36a221b4dec2ef7ec007c99dcb7c54416428ab9fd8799fd8799fd8799f581cf434b2c9818daa419e631a520e6bb98437add6c658fee52c1e07a20dffd8799fd8799fd8799f581c6e4af7adcff67f1c1e7e5805b7c9155125092dd5dee927dd43ec6430ffffffffa140d8799f00a1401a000f32a0ffffd8799fd8799fd8799f581c70e60f3b5ea7153e0acc7a803e4401d44b8ed1bae1c7baaad1a62a72ffd8799fd8799fd8799f581c1e78aae7c90cc36d624f7b3bb6d86b52696dc84e490f343eba89005fffffffffa140d8799f00a1401a000f32a0ffffd8799fd8799fd8799f581c1a5bd6e94501aa6b9a36a221b4dec2ef7ec007c99dcb7c54416428abffd8799fd8799fd8799f581cbe45266367e628fb1ce04d4dee752dbf2e79ad91d22437b400cec0aeffffffffa140d8799f00a1401a0098b5c0ffffffffff0581840000d87a80821a0027e6981a2bc017a9f5f6