diff --git a/pallas-applying/docs/alonzo.md b/pallas-applying/docs/alonzo.md index 9850ab05..d45a3f01 100644 --- a/pallas-applying/docs/alonzo.md +++ b/pallas-applying/docs/alonzo.md @@ -1,32 +1,33 @@ # 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 - **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. - - ***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**: - - ***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. - - ***txIsPhase1Valid(block, pps, tx) ∈ Bool*** indicates whether ***tx ∈ txs(block)*** is phase-1 valid 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(tx)*** is the transaction body of the transaction. When clear, 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 + - ***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(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 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 DatumHash*** is the type of transaction outputs, where - ***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. - ***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)*** 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. - ***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. - - ***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 - ***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. - ***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. @@ -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. - ***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). + - ***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. - ***txExUnits(txWits) ∈ ExUnits*** is the total execution units of the transaction. - - ***txMD(tx)*** is the metadata of the transaction. - - ***hashMD(md)*** is the result of hasing metadata ***md***. - - ***txMDHash(txBody)*** is the metadata hash contained within the transaction body. + - ***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 Alonzo 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*** - **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). - - ***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**: - 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. @@ -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. - ***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 Alonzo languages. + - ***Languages := {PlutusV1}*** is the set of Alonzo languages. - ***CostModel*** is the set of cost models. - ***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). - ***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). @@ -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). - 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*** 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***). - ***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 ***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. - ***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 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: -maxBlockExUnits(pps) ≥ blockExUnits(block) ∧ ∀ tx ∈ txs(block): txIsPhase1Valid(block, tx) +maxBlockExUnits(pps) ≥ blockExUnits(block) ∧ ∀ tx ∈ txs(block): txIsPhase1Valid(pps, tx) ## 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**: txIns(txBody) ≠ ∅ -- **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**: txIns(txBody) ∪ collateral(txBody) ⊆ dom(utxo) - **The block slot is contained in the transaction validity interval**: slot ∈ txValidityInterval(txBody) -- **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**: - **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 consumed(utxo, txBody) = produced(txBody) + fee(txBody) + minted(txBody) -- **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**: - ∀ txOut ∈ txOuts(txBody): adaValueOf(coinsPerUTxOWord(pps) * utxoEntrySize(txOut)) ≤ getValue(txOut) -- **The size of the value in each of the outputs should not be greater than the maximum allowed**: + ∀ txOut ∈ txOuts(txBody): adaValueOf(coinsPerUTxOWord(pps) * outputEntrySize(txOut)) ≤ getValue(txOut) +- **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**: valSize(getValue(txOut)) ≤ maxValSize(pps) - **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**: txExUnits(txBody) ≤ maxTxExUnits(pps) +- **No ADA is minted**: + + adaID ∉ policies(minted(txBody)) - **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 *** - verify(vk, σ, ⟦txBody⟧TxBody) - paymentCredentialutxo(txIn) = keyHash(vk) - - **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: - verify(vk, σ, ⟦txBody⟧TxBody) - keyHash(vk) = key_hash - **The required script languages are included in the protocol parameters**: languages(txWits) ⊆ {l : (l -> _) ∈ costModels(pps, language)} -- **The metadata of the transaction is valid**: +- **The auxiliary data of the transaction is valid**: - txMDHash(tx) = hashMD(txMD(tx)) + txAuxDatHash(tx) = hashMD(txAuxDat(tx)) - **The script data integrity hash matches the hash of the redeemers, languages and datums of the transaction witness set**: scriptDataHash(txBody) = hashScriptIntegrity(pps, txRedeemers(txWits), languages(txWits), txDats(txWits)) -- **No ADA is minted**: - - adaID ∉ policies(mint(txBody)) diff --git a/pallas-applying/docs/babbage.md b/pallas-applying/docs/babbage.md new file mode 100644 index 00000000..030fe78a --- /dev/null +++ b/pallas-applying/docs/babbage.md @@ -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). + - ***⟦_⟧A : 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. + - ***paymentCredentialutxo(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 ***paymentCredentialutxo(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: + +maxBlockExUnits(pps) ≥ blockExUnits(block) ∧ ∀ tx ∈ txs(block): txIsPhase1Valid(pps, tx) + +## 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**: + + txSpendIns(txBody) ≠ ∅ +- **All transaction inputs, collateral inputs and reference inputs are in the UTxO**: + + txSpendIns(txBody) ∪ txCollateralIns(txBody) ∪ txReferenceIns(txBody) ⊆ dom(utxo) +- **The block slot is contained in the transaction validity interval**: + + slot ∈ txValidityInterval(txBody) +- **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**: + + fee(txBody) ≥ minFees(pps, txBody) + - **Collateral**: if there are Plutus scripts in the transaction, then + - **The set of collateral inputs is not empty**: + + txCollateralIns(txBody) ≠ ∅ + - **The number of collateral inputs is not above maximum**: + + ∥txCollateralIns(txBody)∥ ≤ maxCollateralInputs(pps) + - **Each collateral input refers to a verification-key address**: + + ∀(a,\_,\_,\_) ∈ txCollateralIns(txBody) ◁ utxo: isVKeyAddress(a) + - **The balance between collateral inputs and outputs contains only ADA**: + + isADAOnly(txCollateralBalance(txBody, utxo)) + - **The paid collateral is greater than or equal to the minimum fee percentage**: + + txCollateralBalance(txBody, utxo) >= fee(txBody) * collateralPercent(pps) + - **If a number of collateral lovelace is specified in the transaction body, then it equals the actual collateral paid by the transaction**: + + balance(txCollateralIns(txBody) ◁ utxo) - balance(txCollateralReturn(txBody)) = txTotalColl(txBody) +- **The preservation of value property holds**: Assuming no staking or delegation actions are involved, it is the case that + + consumed(utxo, txBody) = produced(txBody) + fee(txBody) + minted(txBody) +- **All transaction outputs (regular outputs and collateral return outputs) contains at least the minimum lovelace**: + + ∀ txOut ∈ txOuts(txBody): adaValueOf(coinsPerUTxOWord(pps) * (outputEntrySize(txOut) + 160)) ≤ getValue(txOut) +- **The size of the value in each of the outputs is not greater than the maximum allowed**: + + valSize(getValue(txOut)) ≤ maxValSize(pps) +- **The network ID of each regular output as well as that of the collateral return output match the global network ID**: + + ∀(a,\_) ∈ txOuts(txBody): netId(a) = NetworkId +- **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**: + + txSize(txBody) ≤ maxTxSize(pps) +- **The number of execution units of the transaction does not exceed the maximum allowed**: + + txExUnits(txBody) ≤ maxTxExUnits(pps) +- **No ADA is minted**: + + adaID ∉ policies(minted(txBody)) +- **Well-formedness of all datums and scripts**: + - **All datums in the witness set are well-formed**: + + ∀ d ∈ txDats(txWits): isWellFormedDatum(d) + - **All scripts in the witness set are well-formed**: + + ∀ s ∈ txWitScripts(txWits): isWellFormedScript(s) + - **All scripts in the auxiliary data are well-formed**: + + ∀ s ∈ auxDataScripts(txAuxDat(tx)): isWellFormedScript(s) + - **All output datums are well-formed**: + + ∀ (\_,\_,d,\_) ∈ allOuts(txBody): isDatum(d) => isWellFormedDatum(d) + - **All output scripts are well-formed**: + + ∀ (\_,\_,\_,d) ∈ allOuts(txBody): isWellFormedScript(d) +- **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**: + + {h: (\_, h) ∈ scriptsNeeded(utxo, txBody)} - {scriptHash(s): s ∈ refScripts(txBody, utxo)} = {scriptHash(s) : s ∈ txScripts(tx, utxo)} + - **Each datum hash in a Plutus script input matches the hash of a datum in the transaction witness set**: + + {h : (a,\_,h,\_) ∈ txSpendIns(txBody) ◁ utxo, isPlutusScriptAddress(a, txWits)} ⊆ {datumHash(d) : d ∈ txDats(txWits)} + - **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**: + + {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)} + - **The set of redeemers in the transaction witness set matches the set of Plutus scripts needed to validate the transaction**: + + {(tag, index): (tag, index, \_, \_) ∈ txRedeemers(txWits)} = {redeemerPointer(txBody, sp): (sp, h) ∈ scriptsNeeded(utxo, txBody), (∃s ∈ txScripts(tx, utxo): isPlutusScript(s), h = scriptHash(s)} + - **Verification-key witnesses**: + - **The owner of each transaction input and each collateral input has signed the transaction**: + + ∀ txIn ∈ txSpendInsVKey(txBody): (∃ (vk, σ) ∈ txVKWits(tx): verify(vk, σ, ⟦txBody⟧TxBody) ∧ paymentCredentialutxo(txIn) = keyHash(vk)) + - **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: + + ∀ key_hash ∈ requiredSigners(txBody): (∃ (vk, σ) ∈ txVKWits(tx): verify(vk, σ, ⟦txBody⟧TxBody) ∧ keyHash(vk) = key_hash) +- **The required script languages are included in the protocol parameters**: + + languages(tx, utxo) ⊆ {l : (l -> _) ∈ costModels(pps, language)} +- **The auxiliary data of the transaction is valid**: + + txAuxDatHash(tx) = hashMD(txAuxDat(tx)) +- **The script data integrity hash matches the hash of the redeemers, languages and datums of the transaction witness set**: + + scriptDataHash(txBody) = hashScriptIntegrity(pps, txRedeemers(txWits), languages(tx, utxo), txDats(txWits)) +- **Each minted / burned asset can be related to the corresponding native or Plutus script in the transaction witness set** + + policies(minted(txBody)) ⊆ {scriptHash(s): s ∈ txScripts(tx, utxo)} diff --git a/pallas-applying/src/alonzo.rs b/pallas-applying/src/alonzo.rs index fd219026..16937f41 100644 --- a/pallas-applying/src/alonzo.rs +++ b/pallas-applying/src/alonzo.rs @@ -1,9 +1,10 @@ -//! Utilities required for Shelley-era transaction validation. +//! Utilities required for Alonzo-era transaction validation. use crate::utils::{ - add_minted_value, add_values, empty_value, extract_auxiliary_data, get_alonzo_comp_tx_size, - get_lovelace_from_alonzo_val, 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, + add_minted_value, add_values, aux_data_from_alonzo_minted_tx, compute_native_script_hash, + compute_plutus_script_hash, empty_value, get_alonzo_comp_tx_size, get_lovelace_from_alonzo_val, + 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::*, AlonzoProtParams, FeePolicy, UTxOs, ValidationError::{self, *}, @@ -18,9 +19,9 @@ use pallas_codec::{ use pallas_crypto::hash::Hash; use pallas_primitives::{ alonzo::{ - AddrKeyhash, Mint, MintedTx, MintedWitnessSet, NativeScript, PlutusData, PlutusScript, - PolicyId, Redeemer, RedeemerPointer, RedeemerTag, RequiredSigners, TransactionBody, - TransactionInput, TransactionOutput, VKeyWitness, Value, + AddrKeyhash, Mint, MintedTx, MintedWitnessSet, Multiasset, NativeScript, PlutusData, + PlutusScript, PolicyId, Redeemer, RedeemerPointer, RedeemerTag, RequiredSigners, + TransactionBody, TransactionInput, TransactionOutput, VKeyWitness, Value, }, byron::TxOut, }; @@ -48,7 +49,7 @@ pub fn validate_alonzo_tx( check_tx_ex_units(mtx, prot_pps)?; check_witness_set(mtx, utxos)?; check_languages(mtx, prot_pps)?; - check_metadata(tx_body, mtx)?; + check_auxiliary_data(tx_body, mtx)?; check_script_data_hash(tx_body, mtx)?; check_minting(tx_body, mtx) } @@ -201,14 +202,15 @@ fn check_collaterals_address(collaterals: &[TransactionInput], utxos: &UTxOs) -> Some(multi_era_output) => { if let Some(alonzo_comp_output) = MultiEraOutput::as_alonzo(multi_era_output) { 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)); } } } None => return Err(Alonzo(CollateralNotInUTxO)), - }; + } } Ok(()) } @@ -258,12 +260,11 @@ fn check_collaterals_assets( // The preservation of value property holds. fn check_preservation_of_value(tx_body: &TransactionBody, utxos: &UTxOs) -> ValidationResult { - let neg_val_err: ValidationError = Alonzo(NegativeValue); - let input: Value = get_consumed(tx_body, utxos)?; + let mut input: Value = get_consumed(tx_body, utxos)?; 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 { - add_minted_value(&output, m, &neg_val_err)?; + input = add_minted_value(&input, m, &Alonzo(NegativeValue))?; } if !values_are_equal(&input, &output) { 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 { - let neg_val_err: ValidationError = Alonzo(NegativeValue); let mut res: Value = empty_value(); for input in tx_body.inputs.iter() { let utxo_value: &MultiEraOutput = utxos .get(&MultiEraInput::from_alonzo_compatible(input)) .ok_or(Alonzo(InputNotInUTxO))?; 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) { 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)), }, @@ -292,10 +294,9 @@ fn get_consumed(tx_body: &TransactionBody, utxos: &UTxOs) -> Result Result { - let neg_val_err: ValidationError = Alonzo(NegativeValue); let mut res: Value = empty_value(); 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) } @@ -311,12 +312,12 @@ fn check_min_lovelace(tx_body: &TransactionBody, prot_pps: &AlonzoProtParams) -> } 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 { Some(_) => 37, // utxoEntrySizeWithoutVal (27) + dataHashSize (10) 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 @@ -433,7 +434,7 @@ fn check_needed_scripts_are_included( return Err(Alonzo(UnneededNativeScript)); } } - for (plutus_script_covered, _) in native_scripts.iter() { + for (plutus_script_covered, _) in plutus_scripts.iter() { if !plutus_script_covered { return Err(Alonzo(UnneededPlutusScript)); } @@ -533,19 +534,23 @@ fn check_redeemers( .collect(), None => Vec::new(), }; - let plutus_scripts: Vec = - mk_plutus_script_redeemer_pointers(tx_body, tx_wits, utxos); + let plutus_scripts: Vec = mk_plutus_script_redeemer_pointers( + &sort_inputs(&tx_body.inputs), + &tx_body.mint, + tx_wits, + utxos, + ); redeemer_pointers_coincide(&redeemer_pointers, &plutus_scripts) } fn mk_plutus_script_redeemer_pointers( - tx_body: &TransactionBody, + sorted_inputs: &[TransactionInput], + mint: &Option>, tx_wits: &MintedWitnessSet, utxos: &UTxOs, ) -> Vec { match &tx_wits.plutus_script { Some(plutus_scripts) => { - let sorted_inputs: Vec = sort_inputs(&tx_body.inputs); let mut res: Vec = Vec::new(); for (index, input) in sorted_inputs.iter().enumerate() { 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) => { let sorted_policies: Vec = sort_policies(minted_value); for (index, policy) in sorted_policies.iter().enumerate() { @@ -664,7 +669,7 @@ fn get_script_hash_from_input(input: &TransactionInput, utxos: &UTxOs) -> Option utxos .get(&MultiEraInput::from_alonzo_compatible(input)) .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 { ShelleyPaymentPart::Script(script_hash) => Some(script_hash), _ => 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 = 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 // signed the transaction. fn check_vkey_input_wits( @@ -741,7 +733,9 @@ fn check_vkey_input_wits( match utxos.get(&MultiEraInput::from_alonzo_compatible(input)) { Some(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) => { 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. -fn check_metadata(tx_body: &TransactionBody, mtx: &MintedTx) -> ValidationResult { - match (&tx_body.auxiliary_data_hash, extract_auxiliary_data(mtx)) { +fn check_auxiliary_data(tx_body: &TransactionBody, mtx: &MintedTx) -> ValidationResult { + match ( + &tx_body.auxiliary_data_hash, + aux_data_from_alonzo_minted_tx(mtx), + ) { (Some(metadata_hash), Some(metadata)) => { if metadata_hash.as_slice() == 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()) .collect(), }; - let plutus_script_wits: Vec = Vec::new(); + let plutus_script_wits: Vec = + match &mtx.transaction_witness_set.plutus_script { + None => Vec::new(), + Some(plutus_script_wits) => plutus_script_wits.clone(), + }; for (policy, _) in minted_value.iter() { if native_script_wits .iter() diff --git a/pallas-applying/src/babbage.rs b/pallas-applying/src/babbage.rs new file mode 100644 index 00000000..035702f2 --- /dev/null +++ b/pallas-applying/src/babbage.rs @@ -0,0 +1,1458 @@ +//! Utilities required for Babbage-era transaction validation. + +use crate::utils::{ + add_minted_value, add_values, aux_data_from_babbage_minted_tx, compute_native_script_hash, + compute_plutus_script_hash, compute_plutus_v2_script_hash, empty_value, get_babbage_tx_size, + get_lovelace_from_alonzo_val, get_network_id_value, get_payment_part, get_shelley_address, + get_val_size_in_words, is_byron_address, lovelace_diff_or_fail, mk_alonzo_vk_wits_check_list, + values_are_equal, verify_signature, + BabbageError::*, + BabbageProtParams, FeePolicy, UTxOs, + ValidationError::{self, *}, + ValidationResult, +}; +use pallas_addresses::{ScriptHash, ShelleyAddress, ShelleyPaymentPart}; +use pallas_codec::{ + minicbor::{encode, Encoder}, + utils::{Bytes, KeepRaw}, +}; +use pallas_crypto::hash::Hash; +use pallas_primitives::{ + alonzo::{RedeemerPointer, RedeemerTag}, + babbage::{ + AddrKeyhash, Language, Mint, MintedTransactionBody, MintedTransactionOutput, MintedTx, + MintedWitnessSet, NativeScript, PlutusData, PlutusV1Script, PlutusV2Script, PolicyId, + PseudoDatumOption, PseudoScript, PseudoTransactionOutput, Redeemer, RequiredSigners, + TransactionInput, VKeyWitness, Value, + }, +}; +use pallas_traverse::{MultiEraInput, MultiEraOutput, OriginalHash}; +use std::ops::Deref; + +pub fn validate_babbage_tx( + mtx: &MintedTx, + utxos: &UTxOs, + prot_pps: &BabbageProtParams, + block_slot: &u64, + network_id: &u8, +) -> ValidationResult { + let tx_body: &MintedTransactionBody = &mtx.transaction_body.clone(); + let size: &u64 = &get_babbage_tx_size(tx_body).ok_or(Babbage(UnknownTxSize))?; + check_ins_not_empty(tx_body)?; + check_all_ins_in_utxos(tx_body, utxos)?; + check_tx_validity_interval(tx_body, block_slot)?; + check_fee(tx_body, size, mtx, utxos, prot_pps)?; + check_preservation_of_value(tx_body, utxos)?; + check_min_lovelace(tx_body, prot_pps)?; + check_output_val_size(tx_body, prot_pps)?; + check_network_id(tx_body, network_id)?; + check_tx_size(size, prot_pps)?; + check_tx_ex_units(mtx, prot_pps)?; + check_minting(tx_body, mtx)?; + check_well_formedness(tx_body, mtx)?; + check_witness_set(mtx, utxos)?; + check_languages(mtx, utxos, block_slot)?; + check_auxiliary_data(tx_body, mtx)?; + check_script_data_hash(tx_body, mtx, utxos, block_slot) +} + +// The set of transaction inputs is not empty. +fn check_ins_not_empty(tx_body: &MintedTransactionBody) -> ValidationResult { + if tx_body.inputs.is_empty() { + return Err(Babbage(TxInsEmpty)); + } + Ok(()) +} + +// All transaction inputs, collateral inputs and reference inputs are in the +// UTxO set. +fn check_all_ins_in_utxos(tx_body: &MintedTransactionBody, utxos: &UTxOs) -> ValidationResult { + for input in tx_body.inputs.iter() { + if !(utxos.contains_key(&MultiEraInput::from_alonzo_compatible(input))) { + return Err(Babbage(InputNotInUTxO)); + } + } + match &tx_body.collateral { + None => (), + Some(collaterals) => { + for collateral in collaterals { + if !(utxos.contains_key(&MultiEraInput::from_alonzo_compatible(collateral))) { + return Err(Babbage(CollateralNotInUTxO)); + } + } + } + } + match &tx_body.reference_inputs { + None => (), + Some(reference_inputs) => { + for reference_input in reference_inputs { + if !(utxos.contains_key(&MultiEraInput::from_alonzo_compatible(reference_input))) { + return Err(Babbage(ReferenceInputNotInUTxO)); + } + } + } + } + Ok(()) +} + +// The block slot is contained in the transaction validity interval, and the +// upper bound is translatable to UTC time. +fn check_tx_validity_interval( + tx_body: &MintedTransactionBody, + block_slot: &u64, +) -> ValidationResult { + check_lower_bound(tx_body, block_slot)?; + check_upper_bound(tx_body, block_slot) +} + +// If defined, the lower bound of the validity time interval does not exceed the +// block slot. +fn check_lower_bound(tx_body: &MintedTransactionBody, block_slot: &u64) -> ValidationResult { + match tx_body.validity_interval_start { + Some(lower_bound) => { + if *block_slot < lower_bound { + Err(Babbage(BlockPrecedesValInt)) + } else { + Ok(()) + } + } + None => Ok(()), + } +} + +// If defined, the upper bound of the validity time interval is not exceeded by +// the block slot, and it is translatable to UTC time. +fn check_upper_bound(tx_body: &MintedTransactionBody, block_slot: &u64) -> ValidationResult { + match tx_body.ttl { + Some(upper_bound) => { + if upper_bound < *block_slot { + Err(Babbage(BlockExceedsValInt)) + } else { + // TODO: check that `upper_bound` is translatable to UTC time. + Ok(()) + } + } + None => Ok(()), + } +} + +fn check_fee( + tx_body: &MintedTransactionBody, + size: &u64, + mtx: &MintedTx, + utxos: &UTxOs, + prot_pps: &BabbageProtParams, +) -> ValidationResult { + check_min_fee(tx_body, size, prot_pps)?; + if presence_of_plutus_scripts(mtx) { + check_collaterals(tx_body, utxos, prot_pps)? + } + Ok(()) +} + +// The fee paid by the transaction should be greater than or equal to the +// minimum fee. +fn check_min_fee( + tx_body: &MintedTransactionBody, + size: &u64, + prot_pps: &BabbageProtParams, +) -> ValidationResult { + let fee_policy: &FeePolicy = &prot_pps.fee_policy; + if tx_body.fee < fee_policy.summand + fee_policy.multiplier * size { + return Err(Babbage(FeeBelowMin)); + } + Ok(()) +} + +fn presence_of_plutus_scripts(mtx: &MintedTx) -> bool { + let minted_witness_set: &MintedWitnessSet = &mtx.transaction_witness_set; + let plutus_v1_scripts: &[PlutusV1Script] = &minted_witness_set + .plutus_v1_script + .clone() + .unwrap_or_default(); + let plutus_v2_scripts: &[PlutusV2Script] = &minted_witness_set + .plutus_v2_script + .clone() + .unwrap_or_default(); + !plutus_v1_scripts.is_empty() || !plutus_v2_scripts.is_empty() +} + +fn check_collaterals( + tx_body: &MintedTransactionBody, + utxos: &UTxOs, + prot_pps: &BabbageProtParams, +) -> ValidationResult { + let collaterals: &[TransactionInput] = &tx_body + .collateral + .clone() + .ok_or(Babbage(CollateralMissing))?; + check_collaterals_number(collaterals, prot_pps)?; + check_collaterals_address(collaterals, utxos)?; + check_collaterals_assets(tx_body, utxos, prot_pps) +} + +// The set of collateral inputs is not empty. +// The number of collateral inputs is below maximum allowed by protocol. +fn check_collaterals_number( + collaterals: &[TransactionInput], + prot_pps: &BabbageProtParams, +) -> ValidationResult { + if collaterals.is_empty() { + Err(Babbage(CollateralMissing)) + } else if collaterals.len() > prot_pps.max_collateral_inputs as usize { + Err(Babbage(TooManyCollaterals)) + } else { + Ok(()) + } +} + +// Each collateral input refers to a verification-key address. +fn check_collaterals_address(collaterals: &[TransactionInput], utxos: &UTxOs) -> ValidationResult { + for collateral in collaterals { + match utxos.get(&MultiEraInput::from_alonzo_compatible(collateral)) { + Some(multi_era_output) => { + if let Some(babbage_output) = MultiEraOutput::as_babbage(multi_era_output) { + let address: &Bytes = match babbage_output { + PseudoTransactionOutput::Legacy(inner) => &inner.address, + PseudoTransactionOutput::PostAlonzo(inner) => &inner.address, + }; + if let ShelleyPaymentPart::Script(_) = + get_payment_part(address).ok_or(Babbage(InputDecoding))? + { + return Err(Babbage(CollateralNotVKeyLocked)); + } + } + } + None => { + return Err(Babbage(CollateralNotInUTxO)); + } + } + } + Ok(()) +} + +// The balance between collateral inputs and output contains only lovelace. +// The balance is not lower than the minimum allowed. +// The balance matches exactly the collateral annotated in the transaction body. +fn check_collaterals_assets( + tx_body: &MintedTransactionBody, + utxos: &UTxOs, + prot_pps: &BabbageProtParams, +) -> ValidationResult { + match &tx_body.collateral { + Some(collaterals) => { + let mut coll_input: Value = empty_value(); + for collateral in collaterals { + match utxos.get(&MultiEraInput::from_alonzo_compatible(collateral)) { + Some(multi_era_output) => { + coll_input = add_values( + &coll_input, + &val_from_multi_era_output(multi_era_output), + &Babbage(NegativeValue), + )? + } + None => { + return Err(Babbage(CollateralNotInUTxO)); + } + } + } + let coll_return: Value = match &tx_body.collateral_return { + Some(PseudoTransactionOutput::Legacy(output)) => output.amount.clone(), + Some(PseudoTransactionOutput::PostAlonzo(output)) => output.value.clone(), + None => Value::Coin(0), + }; + // The balance between collateral inputs and output contains only lovelace. + let paid_collateral: u64 = + lovelace_diff_or_fail(&coll_input, &coll_return, &Babbage(NonLovelaceCollateral))?; + let fee_percentage: u64 = tx_body.fee * prot_pps.collateral_percent; + // The balance is not lower than the minimum allowed. + if paid_collateral * 100 < fee_percentage { + return Err(Babbage(CollateralMinLovelace)); + } + // The balance matches exactly the collateral annotated in the transaction body. + if let Some(annotated_collateral) = &tx_body.total_collateral { + if paid_collateral != *annotated_collateral { + return Err(Babbage(CollateralAnnotation)); + } + } + } + None => return Err(Babbage(CollateralMissing)), + } + Ok(()) +} + +fn val_from_multi_era_output(multi_era_output: &MultiEraOutput) -> Value { + match multi_era_output { + MultiEraOutput::Byron(output) => Value::Coin(output.amount), + MultiEraOutput::AlonzoCompatible(output) => output.amount.clone(), + babbage_output => match babbage_output.as_babbage() { + Some(PseudoTransactionOutput::Legacy(output)) => output.amount.clone(), + Some(PseudoTransactionOutput::PostAlonzo(output)) => output.value.clone(), + None => unimplemented!(), /* If this is the case, then it must be that non-exhaustive + * type MultiEraOutput was extended with another variant */ + }, + } +} + +// The preservation of value property holds. +fn check_preservation_of_value(tx_body: &MintedTransactionBody, utxos: &UTxOs) -> ValidationResult { + let mut input: Value = get_consumed(tx_body, utxos)?; + let produced: Value = get_produced(tx_body)?; + let output: Value = add_values( + &produced, + &Value::Coin(tx_body.fee), + &Babbage(NegativeValue), + )?; + if let Some(m) = &tx_body.mint { + input = add_minted_value(&input, m, &Babbage(NegativeValue))?; + } + if !values_are_equal(&input, &output) { + return Err(Babbage(PreservationOfValue)); + } + Ok(()) +} + +fn get_consumed(tx_body: &MintedTransactionBody, utxos: &UTxOs) -> Result { + let mut res: Value = empty_value(); + for input in tx_body.inputs.iter() { + let multi_era_output: &MultiEraOutput = utxos + .get(&MultiEraInput::from_alonzo_compatible(input)) + .ok_or(Babbage(InputNotInUTxO))?; + let val: Value = val_from_multi_era_output(multi_era_output); + res = add_values(&res, &val, &Babbage(NegativeValue))?; + } + Ok(res) +} + +fn get_produced(tx_body: &MintedTransactionBody) -> Result { + let mut res: Value = empty_value(); + for output in tx_body.outputs.iter() { + match output { + PseudoTransactionOutput::Legacy(output) => { + res = add_values(&res, &output.amount, &Babbage(NegativeValue))? + } + PseudoTransactionOutput::PostAlonzo(output) => { + res = add_values(&res, &output.value, &Babbage(NegativeValue))? + } + } + } + Ok(res) +} + +fn check_min_lovelace( + tx_body: &MintedTransactionBody, + prot_pps: &BabbageProtParams, +) -> ValidationResult { + for output in tx_body.outputs.iter() { + let val: &Value = match output { + PseudoTransactionOutput::Legacy(output) => &output.amount, + PseudoTransactionOutput::PostAlonzo(output) => &output.value, + }; + if get_lovelace_from_alonzo_val(val) < compute_min_lovelace(val, prot_pps) { + return Err(Babbage(MinLovelaceUnreached)); + } + } + Ok(()) +} + +fn compute_min_lovelace(val: &Value, prot_pps: &BabbageProtParams) -> u64 { + prot_pps.coins_per_utxo_word * (get_val_size_in_words(val) + 160) +} + +// The size of the value in each of the outputs should not be greater than the +// maximum allowed. +fn check_output_val_size( + tx_body: &MintedTransactionBody, + prot_pps: &BabbageProtParams, +) -> ValidationResult { + for output in tx_body.outputs.iter() { + let val: &Value = match output { + PseudoTransactionOutput::Legacy(output) => &output.amount, + PseudoTransactionOutput::PostAlonzo(output) => &output.value, + }; + if get_val_size_in_words(val) > prot_pps.max_val_size { + return Err(Babbage(MaxValSizeExceeded)); + } + } + Ok(()) +} + +fn check_network_id(tx_body: &MintedTransactionBody, network_id: &u8) -> ValidationResult { + check_tx_outs_network_id(tx_body, network_id)?; + check_tx_network_id(tx_body, network_id) +} + +fn check_tx_outs_network_id(tx_body: &MintedTransactionBody, network_id: &u8) -> ValidationResult { + for output in tx_body.outputs.iter() { + let addr_bytes: &Bytes = match output { + PseudoTransactionOutput::Legacy(output) => &output.address, + PseudoTransactionOutput::PostAlonzo(output) => &output.address, + }; + let addr: ShelleyAddress = + get_shelley_address(Bytes::deref(addr_bytes)).ok_or(Babbage(AddressDecoding))?; + if addr.network().value() != *network_id { + return Err(Babbage(OutputWrongNetworkID)); + } + } + Ok(()) +} + +// The network ID of the transaction body is either undefined or equal to the +// global network ID. +fn check_tx_network_id(tx_body: &MintedTransactionBody, network_id: &u8) -> ValidationResult { + if let Some(tx_network_id) = tx_body.network_id { + if get_network_id_value(tx_network_id) != *network_id { + return Err(Babbage(TxWrongNetworkID)); + } + } + Ok(()) +} + +fn check_tx_size(size: &u64, prot_pps: &BabbageProtParams) -> ValidationResult { + if *size > prot_pps.max_tx_size { + return Err(Babbage(MaxTxSizeExceeded)); + } + Ok(()) +} + +fn check_tx_ex_units(mtx: &MintedTx, prot_pps: &BabbageProtParams) -> ValidationResult { + let tx_wits: &MintedWitnessSet = &mtx.transaction_witness_set; + if presence_of_plutus_scripts(mtx) { + match &tx_wits.redeemer { + Some(redeemers_vec) => { + let mut steps: u64 = 0; + let mut mem: u32 = 0; + for Redeemer { ex_units, .. } in redeemers_vec { + mem += ex_units.mem; + steps += ex_units.steps; + } + if mem > prot_pps.max_tx_ex_mem || steps > prot_pps.max_tx_ex_steps { + return Err(Babbage(TxExUnitsExceeded)); + } + } + None => return Err(Babbage(RedeemerMissing)), + } + } + Ok(()) +} + +// Each minted / burned asset is paired with an appropriate native script or +// Plutus script. +fn check_minting(tx_body: &MintedTransactionBody, mtx: &MintedTx) -> ValidationResult { + match &tx_body.mint { + Some(minted_value) => { + let native_script_wits: Vec = + match &mtx.transaction_witness_set.native_script { + None => Vec::new(), + Some(keep_raw_native_script_wits) => keep_raw_native_script_wits + .iter() + .map(|x| x.clone().unwrap()) + .collect(), + }; + let v1_script_wits: Vec = + match &mtx.transaction_witness_set.plutus_v1_script { + None => Vec::new(), + Some(v1_script_wits) => v1_script_wits.clone(), + }; + let v2_script_wits: Vec = + match &mtx.transaction_witness_set.plutus_v2_script { + None => Vec::new(), + Some(v2_script_wits) => v2_script_wits.clone(), + }; + for (policy, _) in minted_value.iter() { + if native_script_wits + .iter() + .all(|script| compute_native_script_hash(script) != *policy) + && v1_script_wits + .iter() + .all(|script| compute_plutus_script_hash(script) != *policy) + && v2_script_wits + .iter() + .all(|script| compute_plutus_v2_script_hash(script) != *policy) + { + return Err(Babbage(MintingLacksPolicy)); + } + } + Ok(()) + } + None => Ok(()), + } +} + +fn check_well_formedness(_tx_body: &MintedTransactionBody, _mtx: &MintedTx) -> ValidationResult { + Ok(()) +} + +fn check_witness_set(mtx: &MintedTx, utxos: &UTxOs) -> ValidationResult { + let tx_hash: &Vec = &Vec::from(mtx.transaction_body.original_hash().as_ref()); + let tx_body: &MintedTransactionBody = &mtx.transaction_body; + let tx_wits: &MintedWitnessSet = &mtx.transaction_witness_set; + let vkey_wits: &Option> = &tx_wits.vkeywitness; + let native_scripts: Vec = match &tx_wits.native_script { + Some(scripts) => scripts + .clone() + .iter() + .map(|raw_script| compute_native_script_hash(raw_script)) + .collect(), + None => Vec::new(), + }; + let plutus_v1_scripts: Vec = match &tx_wits.plutus_v1_script { + Some(scripts) => scripts + .clone() + .iter() + .map(compute_plutus_script_hash) + .collect(), + None => Vec::new(), + }; + let plutus_v2_scripts: Vec = match &tx_wits.plutus_v2_script { + Some(scripts) => scripts + .clone() + .iter() + .map(compute_plutus_v2_script_hash) + .collect(), + None => Vec::new(), + }; + let reference_scripts: Vec = get_reference_script_hashes(tx_body, utxos); + check_needed_scripts( + tx_body, + utxos, + &native_scripts, + &plutus_v1_scripts, + &plutus_v2_scripts, + &reference_scripts, + )?; + check_datums(tx_body, utxos, &tx_wits.plutus_data)?; + check_redeemers( + &plutus_v1_scripts, + &plutus_v2_scripts, + &reference_scripts, + tx_body, + tx_wits, + utxos, + )?; + check_required_signers(&tx_body.required_signers, vkey_wits, tx_hash)?; + check_vkey_input_wits(mtx, &tx_wits.vkeywitness, utxos) +} + +// 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 +fn check_needed_scripts( + tx_body: &MintedTransactionBody, + utxos: &UTxOs, + native_scripts: &[PolicyId], + plutus_v1_scripts: &[PolicyId], + plutus_v2_scripts: &[PolicyId], + reference_scripts: &[PolicyId], +) -> ValidationResult { + let mut filtered_native_scripts: Vec<(bool, PolicyId)> = native_scripts + .iter() + .map(|&script_hash| (false, script_hash)) + .collect(); + filtered_native_scripts.retain(|&(_, native_script_hash)| { + !reference_scripts + .iter() + .any(|&reference_script_hash| reference_script_hash == native_script_hash) + }); + let mut filtered_plutus_v1_scripts: Vec<(bool, PolicyId)> = plutus_v1_scripts + .iter() + .map(|&script_hash| (false, script_hash)) + .collect(); + filtered_plutus_v1_scripts.retain(|&(_, plutus_v1_script_hash)| { + !reference_scripts + .iter() + .any(|&reference_script_hash| reference_script_hash == plutus_v1_script_hash) + }); + let mut filtered_plutus_v2_scripts: Vec<(bool, PolicyId)> = plutus_v2_scripts + .iter() + .map(|&script_hash| (false, script_hash)) + .collect(); + filtered_plutus_v2_scripts.retain(|&(_, plutus_v2_script_hash)| { + !reference_scripts + .iter() + .any(|&reference_script_hash| reference_script_hash == plutus_v2_script_hash) + }); + check_input_scripts( + tx_body, + &mut filtered_native_scripts, + &mut filtered_plutus_v1_scripts, + &mut filtered_plutus_v2_scripts, + reference_scripts, + utxos, + )?; + check_minting_policies( + tx_body, + &mut filtered_native_scripts, + &mut filtered_plutus_v1_scripts, + &mut filtered_plutus_v2_scripts, + reference_scripts, + )?; + for (covered, _) in filtered_native_scripts.iter() { + if !covered { + return Err(Babbage(UnneededNativeScript)); + } + } + for (covered, _) in filtered_plutus_v1_scripts.iter() { + if !covered { + return Err(Babbage(UnneededPlutusV1Script)); + } + } + for (covered, _) in filtered_plutus_v2_scripts.iter() { + if !covered { + return Err(Babbage(UnneededPlutusV2Script)); + } + } + Ok(()) +} + +fn get_reference_script_hashes(tx_body: &MintedTransactionBody, utxos: &UTxOs) -> Vec { + let mut res: Vec = Vec::new(); + if let Some(reference_inputs) = &tx_body.reference_inputs { + for input in reference_inputs.iter() { + if let Some(script_hash) = get_script_hash_from_reference_input(input, utxos) { + res.push(script_hash) + } + } + } + res +} + +fn check_input_scripts( + tx_body: &MintedTransactionBody, + native_scripts: &mut [(bool, PolicyId)], + plutus_v1_scripts: &mut [(bool, PolicyId)], + plutus_v2_scripts: &mut [(bool, PolicyId)], + reference_scripts: &[PolicyId], + utxos: &UTxOs, +) -> ValidationResult { + let mut needed_input_scripts: Vec<(bool, ScriptHash)> = + get_script_hashes_from_inputs(tx_body, utxos); + for (covered, hash) in &mut needed_input_scripts { + for (native_script_covered, native_script_hash) in native_scripts.iter_mut() { + if *hash == *native_script_hash { + *covered = true; + *native_script_covered = true; + } + } + for (plutus_v1_script_covered, plutus_v1_script_hash) in plutus_v1_scripts.iter_mut() { + if *hash == *plutus_v1_script_hash { + *covered = true; + *plutus_v1_script_covered = true; + } + } + for (plutus_v2_script_covered, plutus_v2_script_hash) in plutus_v2_scripts.iter_mut() { + if *hash == *plutus_v2_script_hash { + *covered = true; + *plutus_v2_script_covered = true; + } + } + } + for (covered, hash) in needed_input_scripts { + if !covered + && !reference_scripts + .iter() + .any(|reference_script_hash| *reference_script_hash == hash) + { + return Err(Babbage(ScriptWitnessMissing)); + } + } + Ok(()) +} + +fn get_script_hashes_from_inputs( + tx_body: &MintedTransactionBody, + utxos: &UTxOs, +) -> Vec<(bool, ScriptHash)> { + let mut res: Vec<(bool, ScriptHash)> = Vec::new(); + for input in tx_body.inputs.iter() { + if let Some(script_hash) = get_script_hash_from_input(input, utxos) { + res.push((false, script_hash)) + } + } + res +} + +fn get_script_hash_from_input(input: &TransactionInput, utxos: &UTxOs) -> Option { + match utxos + .get(&MultiEraInput::from_alonzo_compatible(input)) + .and_then(MultiEraOutput::as_babbage) + { + Some(PseudoTransactionOutput::Legacy(output)) => match get_payment_part(&output.address) { + Some(ShelleyPaymentPart::Script(script_hash)) => Some(script_hash), + _ => None, + }, + Some(PseudoTransactionOutput::PostAlonzo(output)) => { + match get_payment_part(&output.address) { + Some(ShelleyPaymentPart::Script(script_hash)) => Some(script_hash), + _ => None, + } + } + None => None, + } +} + +fn get_script_hash_from_reference_input( + ref_input: &TransactionInput, + utxos: &UTxOs, +) -> Option { + match utxos + .get(&MultiEraInput::from_alonzo_compatible(ref_input)) + .and_then(MultiEraOutput::as_babbage) + { + Some(PseudoTransactionOutput::Legacy(_)) => None, + Some(PseudoTransactionOutput::PostAlonzo(output)) => { + if let Some(script_ref_cborwrap) = &output.script_ref { + match script_ref_cborwrap.clone().unwrap() { + PseudoScript::NativeScript(native_script) => { + // First, the NativeScript header. + let mut val_to_hash: Vec = vec![0]; + // Then, the CBOR content. + val_to_hash.extend_from_slice(native_script.raw_cbor()); + return Some(pallas_crypto::hash::Hasher::<224>::hash(&val_to_hash)); + } + PseudoScript::PlutusV1Script(plutus_v1_script) => { + // First, the PlutusV1Script header. + let mut val_to_hash: Vec = vec![1]; + // Then, the CBOR content. + val_to_hash.extend_from_slice(plutus_v1_script.as_ref()); + return Some(pallas_crypto::hash::Hasher::<224>::hash(&val_to_hash)); + } + PseudoScript::PlutusV2Script(plutus_v2_script) => { + // First, the PlutusV2Script header. + let mut val_to_hash: Vec = vec![2]; + // Then, the CBOR content. + val_to_hash.extend_from_slice(plutus_v2_script.as_ref()); + return Some(pallas_crypto::hash::Hasher::<224>::hash(&val_to_hash)); + } + } + } + None + } + _ => None, + } +} + +fn check_minting_policies( + tx_body: &MintedTransactionBody, + native_scripts: &mut [(bool, PolicyId)], + plutus_v1_scripts: &mut [(bool, PolicyId)], + plutus_v2_scripts: &mut [(bool, PolicyId)], + reference_scripts: &[PolicyId], +) -> ValidationResult { + match &tx_body.mint { + None => Ok(()), + Some(minted_value) => { + let mut minting_policies: Vec<(bool, PolicyId)> = + minted_value.iter().map(|(pol, _)| (false, *pol)).collect(); + for (policy_covered, policy) in &mut minting_policies { + for (native_script_covered, native_script_hash) in native_scripts.iter_mut() { + if *policy == *native_script_hash { + *policy_covered = true; + *native_script_covered = true; + } + } + for (plutus_script_covered, plutus_v1_script_hash) in plutus_v1_scripts.iter_mut() { + if *policy == *plutus_v1_script_hash { + *policy_covered = true; + *plutus_script_covered = true; + } + } + for (plutus_script_covered, plutus_v2_script_hash) in plutus_v2_scripts.iter_mut() { + if *policy == *plutus_v2_script_hash { + *policy_covered = true; + *plutus_script_covered = true; + } + } + for reference_script_hash in reference_scripts.iter() { + if *policy == *reference_script_hash { + *policy_covered = true; + } + } + } + for (policy_covered, _) in minting_policies { + if !policy_covered { + return Err(Babbage(MintingLacksPolicy)); + } + } + Ok(()) + } + } +} + +// Each datum hash in a Plutus script input matches the hash of a datum in the +// transaction witness set +fn check_datums( + tx_body: &MintedTransactionBody, + utxos: &UTxOs, + option_plutus_data: &Option>>, +) -> ValidationResult { + let mut plutus_data_hash: Vec<(bool, Hash<32>)> = match option_plutus_data { + Some(plutus_data) => plutus_data + .iter() + .map(|datum| { + ( + false, + pallas_crypto::hash::Hasher::<256>::hash(datum.raw_cbor()), + ) + }) + .collect(), + None => Vec::new(), + }; + check_input_datum_hash_in_witness_set(tx_body, utxos, &mut plutus_data_hash)?; + check_remaining_datums(&plutus_data_hash, tx_body, utxos) +} + +// Each datum hash in a Plutus script input matches the hash of a datum in the +// transaction witness set. +fn check_input_datum_hash_in_witness_set( + tx_body: &MintedTransactionBody, + utxos: &UTxOs, + plutus_data_hash: &mut [(bool, Hash<32>)], +) -> ValidationResult { + for input in &tx_body.inputs { + match utxos + .get(&MultiEraInput::from_alonzo_compatible(input)) + .and_then(MultiEraOutput::as_babbage) + { + Some(output) => { + if let Some(datum_hash) = get_datum_hash(output) { + find_datum_hash(&datum_hash, plutus_data_hash)? + } + } + None => return Err(Babbage(InputNotInUTxO)), + } + } + Ok(()) +} + +// Extract datum hash if one is contained. +fn get_datum_hash(output: &MintedTransactionOutput) -> Option> { + match output { + PseudoTransactionOutput::Legacy(output) => output.datum_hash, + PseudoTransactionOutput::PostAlonzo(output) => match output.datum_option { + Some(PseudoDatumOption::Hash(hash)) => Some(hash), + _ => None, + }, + } +} + +fn find_datum_hash(hash: &Hash<32>, plutus_data_hash: &mut [(bool, Hash<32>)]) -> ValidationResult { + for (found, plutus_datum_hash) in plutus_data_hash { + if hash == plutus_datum_hash { + *found = true; + return Ok(()); + } + } + Err(Babbage(DatumMissing)) +} + +// 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 +fn check_remaining_datums( + plutus_data_hash: &[(bool, Hash<32>)], + tx_body: &MintedTransactionBody, + utxos: &UTxOs, +) -> ValidationResult { + for (found, plutus_datum_hash) in plutus_data_hash { + if !found { + find_datum(plutus_datum_hash, tx_body, utxos)? + } + } + Ok(()) +} + +fn find_datum(hash: &Hash<32>, tx_body: &MintedTransactionBody, utxos: &UTxOs) -> ValidationResult { + // Look for hash in transaction (regular) outputs + for output in tx_body.outputs.iter() { + if let Some(datum_hash) = get_datum_hash(output) { + if *hash == datum_hash { + return Ok(()); + } + } + } + // Look for hash in collateral return output + if let Some(babbage_output) = &tx_body.collateral_return { + match babbage_output { + PseudoTransactionOutput::Legacy(output) => { + if let Some(datum_hash) = &output.datum_hash { + if *hash == *datum_hash { + return Ok(()); + } + } + } + PseudoTransactionOutput::PostAlonzo(output) => { + if let Some(PseudoDatumOption::Hash(datum_hash)) = &output.datum_option { + if *hash == *datum_hash { + return Ok(()); + } + } + } + } + } + // Look for hash in reference input + if let Some(reference_inputs) = &tx_body.reference_inputs { + for reference_input in reference_inputs.iter() { + match utxos + .get(&MultiEraInput::from_alonzo_compatible(reference_input)) + .and_then(MultiEraOutput::as_babbage) + { + Some(PseudoTransactionOutput::Legacy(output)) => { + if let Some(datum_hash) = &output.datum_hash { + if *hash == *datum_hash { + return Ok(()); + } + } + } + Some(PseudoTransactionOutput::PostAlonzo(output)) => { + if let Some(PseudoDatumOption::Hash(datum_hash)) = &output.datum_option { + if *hash == *datum_hash { + return Ok(()); + } + } + } + _ => (), + } + } + } + Err(Babbage(UnneededDatum)) +} + +fn check_redeemers( + plutus_v1_scripts: &[PolicyId], + plutus_v2_scripts: &[PolicyId], + reference_scripts: &[PolicyId], + tx_body: &MintedTransactionBody, + tx_wits: &MintedWitnessSet, + utxos: &UTxOs, +) -> ValidationResult { + let redeemer_pointers: Vec = match &tx_wits.redeemer { + Some(redeemers) => redeemers + .iter() + .map(|x| RedeemerPointer { + tag: x.tag.clone(), + index: x.index, + }) + .collect(), + None => Vec::new(), + }; + let plutus_scripts: Vec = mk_plutus_script_redeemer_pointers( + plutus_v1_scripts, + plutus_v2_scripts, + reference_scripts, + tx_body, + utxos, + ); + redeemer_pointers_coincide(&redeemer_pointers, &plutus_scripts) +} + +// Lexicographical sorting for inputs. +fn sort_inputs(unsorted_inputs: &[TransactionInput]) -> Vec { + let mut res: Vec = unsorted_inputs.to_owned(); + res.sort(); + res +} + +fn mk_plutus_script_redeemer_pointers( + plutus_v1_scripts: &[PolicyId], + plutus_v2_scripts: &[PolicyId], + reference_scripts: &[PolicyId], + tx_body: &MintedTransactionBody, + utxos: &UTxOs, +) -> Vec { + let mut res: Vec = Vec::new(); + let sorted_inputs: &Vec = &sort_inputs(&tx_body.inputs); + for (index, input) in sorted_inputs.iter().enumerate() { + if get_script_hash_from_input(input, utxos).is_some() { + res.push(RedeemerPointer { + tag: RedeemerTag::Spend, + index: index as u32, + }) + } + } + if let Some(mint) = &tx_body.mint { + for (index, policy) in sort_policies(mint).iter().enumerate() { + if is_phase_2_script( + policy, + plutus_v1_scripts, + plutus_v2_scripts, + reference_scripts, + ) { + res.push(RedeemerPointer { + tag: RedeemerTag::Mint, + index: index as u32, + }) + } + } + } + res +} + +// Lexicographical sorting for PolicyID's. +fn sort_policies(mint: &Mint) -> Vec { + let mut res: Vec = mint + .clone() + .to_vec() + .iter() + .map(|(policy_id, _)| *policy_id) + .collect(); + res.sort(); + res +} + +fn is_phase_2_script( + policy: &PolicyId, + plutus_v1_scripts: &[PolicyId], + plutus_v2_scripts: &[PolicyId], + reference_scripts: &[PolicyId], +) -> bool { + plutus_v1_scripts + .iter() + .any(|v1_script| policy == v1_script) + || plutus_v2_scripts + .iter() + .any(|v2_script| policy == v2_script) + || reference_scripts + .iter() + .any(|ref_script| policy == ref_script) +} + +fn redeemer_pointers_coincide( + redeemers: &[RedeemerPointer], + plutus_scripts: &[RedeemerPointer], +) -> ValidationResult { + for redeemer_pointer in redeemers { + if !plutus_scripts.iter().any(|x| x == redeemer_pointer) { + return Err(Babbage(UnneededRedeemer)); + } + } + for ps_redeemer_pointer in plutus_scripts { + if !redeemers.iter().any(|x| x == ps_redeemer_pointer) { + return Err(Babbage(RedeemerMissing)); + } + } + Ok(()) +} + +// All required signers (needed by a Plutus script) have a corresponding match +// in the transaction witness set. +fn check_required_signers( + required_signers: &Option, + vkey_wits: &Option>, + data_to_verify: &[u8], +) -> ValidationResult { + if let Some(req_signers) = &required_signers { + match &vkey_wits { + Some(vkey_wits) => { + for req_signer in req_signers { + find_and_check_req_signer(req_signer, vkey_wits, data_to_verify)? + } + } + None => return Err(Babbage(ReqSignerMissing)), + } + } + Ok(()) +} + +// Try to find the verification key in the witnesses, and verify the signature. +fn find_and_check_req_signer( + vkey_hash: &AddrKeyhash, + vkey_wits: &[VKeyWitness], + data_to_verify: &[u8], +) -> ValidationResult { + for vkey_wit in vkey_wits { + if pallas_crypto::hash::Hasher::<224>::hash(&vkey_wit.vkey.clone()) == *vkey_hash { + if !verify_signature(vkey_wit, data_to_verify) { + return Err(Babbage(ReqSignerWrongSig)); + } else { + return Ok(()); + } + } + } + Err(Babbage(ReqSignerMissing)) +} + +fn check_vkey_input_wits( + mtx: &MintedTx, + vkey_wits: &Option>, + utxos: &UTxOs, +) -> ValidationResult { + let tx_body: &MintedTransactionBody = &mtx.transaction_body; + let vk_wits: &mut Vec<(bool, VKeyWitness)> = + &mut mk_alonzo_vk_wits_check_list(vkey_wits, Babbage(VKWitnessMissing))?; + let tx_hash: &Vec = &Vec::from(mtx.transaction_body.original_hash().as_ref()); + let mut inputs_and_collaterals: Vec = Vec::new(); + inputs_and_collaterals.extend(tx_body.inputs.clone()); + if let Some(collaterals) = &tx_body.collateral { + inputs_and_collaterals.extend(collaterals.clone()) + } + for input in inputs_and_collaterals.iter() { + match utxos.get(&MultiEraInput::from_alonzo_compatible(input)) { + Some(multi_era_output) => { + if let Some(babbage_output) = MultiEraOutput::as_babbage(multi_era_output) { + let address: &Bytes = match babbage_output { + PseudoTransactionOutput::Legacy(output) => &output.address, + PseudoTransactionOutput::PostAlonzo(output) => &output.address, + }; + match get_payment_part(address).ok_or(Babbage(InputDecoding))? { + ShelleyPaymentPart::Key(payment_key_hash) => { + check_vk_wit(&payment_key_hash, vk_wits, tx_hash)? + } + ShelleyPaymentPart::Script(_) => (), + } + } + } + None => return Err(Babbage(InputNotInUTxO)), + } + } + check_remaining_vk_wits(vk_wits, tx_hash) // required for native scripts +} + +fn check_vk_wit( + payment_key_hash: &AddrKeyhash, + wits: &mut [(bool, VKeyWitness)], + data_to_verify: &[u8], +) -> ValidationResult { + for (vkey_wit_covered, vkey_wit) in wits { + if pallas_crypto::hash::Hasher::<224>::hash(&vkey_wit.vkey.clone()) == *payment_key_hash { + if !verify_signature(vkey_wit, data_to_verify) { + return Err(Babbage(VKWrongSignature)); + } else { + *vkey_wit_covered = true; + return Ok(()); + } + } + } + Err(Babbage(VKWitnessMissing)) +} + +fn check_remaining_vk_wits( + wits: &mut [(bool, VKeyWitness)], + data_to_verify: &[u8], +) -> ValidationResult { + for (covered, vkey_wit) in wits { + if !*covered { + if verify_signature(vkey_wit, data_to_verify) { + return Ok(()); + } else { + return Err(Babbage(VKWrongSignature)); + } + } + } + Ok(()) +} + +fn check_languages(mtx: &MintedTx, utxos: &UTxOs, block_slot: &u64) -> ValidationResult { + let available_langs: Vec = available_langs(mtx, utxos, block_slot); + for tx_lang in tx_languages(mtx, utxos).iter() { + if !available_langs + .iter() + .any(|available_lang| *available_lang == *tx_lang) + { + return Err(Babbage(UnsupportedPlutusLanguage)); + } + } + Ok(()) +} + +fn available_langs(mtx: &MintedTx, utxos: &UTxOs, block_slot: &u64) -> Vec { + let block_langs: Vec = block_langs(block_slot); + let allowed_langs: Vec = allowed_langs(mtx, utxos); + block_langs + .iter() + .filter(|&cost_model_language| allowed_langs.contains(cost_model_language)) + .cloned() + .collect::>() +} + +fn block_langs(block_slot: &u64) -> Vec { + if *block_slot >= 72748820 { + vec![Language::PlutusV1, Language::PlutusV2] + } else { + vec![Language::PlutusV1] + } +} + +fn allowed_langs(mtx: &MintedTx, utxos: &UTxOs) -> Vec { + let all_outputs: Vec<&MintedTransactionOutput> = compute_all_outputs(mtx, utxos); + if any_byron_addresses(&all_outputs) { + vec![] + } else if any_datums_or_script_refs(&all_outputs) + || any_reference_inputs(&mtx.transaction_body.reference_inputs) + { + vec![Language::PlutusV2] + } else { + vec![Language::PlutusV1, Language::PlutusV2] + } +} + +fn compute_all_outputs<'a>( + mtx: &'a MintedTx, + utxos: &'a UTxOs, +) -> Vec<&'a MintedTransactionOutput<'a>> { + let mut res: Vec<&MintedTransactionOutput> = Vec::new(); + for input in mtx.transaction_body.inputs.iter() { + if let Some(output) = utxos + .get(&MultiEraInput::from_alonzo_compatible(input)) + .and_then(MultiEraOutput::as_babbage) + { + res.push(output) + } + } + if let Some(reference_inputs) = &mtx.transaction_body.reference_inputs { + for ref_input in reference_inputs.iter() { + if let Some(output) = utxos + .get(&MultiEraInput::from_alonzo_compatible(ref_input)) + .and_then(MultiEraOutput::as_babbage) + { + res.push(output) + } + } + } + for output in mtx.transaction_body.outputs.iter() { + res.push(output) + } + res +} + +fn any_byron_addresses(all_outputs: &[&MintedTransactionOutput]) -> bool { + for output in all_outputs.iter() { + match output { + PseudoTransactionOutput::Legacy(output) => { + if is_byron_address(&output.address) { + return true; + } + } + PseudoTransactionOutput::PostAlonzo(output) => { + if is_byron_address(&output.address) { + return true; + } + } + } + } + false +} + +fn any_datums_or_script_refs(all_outputs: &[&MintedTransactionOutput]) -> bool { + for output in all_outputs.iter() { + match output { + PseudoTransactionOutput::Legacy(_) => (), + PseudoTransactionOutput::PostAlonzo(output) => { + if output.script_ref.is_some() { + return true; + } else if let Some(PseudoDatumOption::Data(_)) = &output.datum_option { + return true; + } + } + } + } + false +} + +fn any_reference_inputs(reference_inputs: &Option>) -> bool { + match reference_inputs { + Some(reference_inputs) => !reference_inputs.is_empty(), + None => false, + } +} + +fn tx_languages(mtx: &MintedTx, utxos: &UTxOs) -> Vec { + let mut v1_scripts: bool = false; + let mut v2_scripts: bool = false; + if let Some(v1_scripts_vec) = &mtx.transaction_witness_set.plutus_v1_script { + if !v1_scripts_vec.is_empty() { + v1_scripts = true + } + } + if let Some(v2_scripts_vec) = &mtx.transaction_witness_set.plutus_v2_script { + if !v2_scripts_vec.is_empty() { + v2_scripts = true; + } + } + if let Some(reference_inputs) = &mtx.transaction_body.reference_inputs { + for ref_input in reference_inputs.iter() { + if let Some(PseudoTransactionOutput::PostAlonzo(output)) = utxos + .get(&MultiEraInput::from_alonzo_compatible(ref_input)) + .and_then(MultiEraOutput::as_babbage) + { + if let Some(script_ref_cborwrap) = &output.script_ref { + match script_ref_cborwrap.clone().unwrap() { + PseudoScript::PlutusV1Script(_) => v1_scripts = true, + PseudoScript::PlutusV2Script(_) => v2_scripts = true, + _ => (), + } + } + } + } + } + if !v1_scripts && !v2_scripts { + vec![] + } else if v1_scripts && !v2_scripts { + vec![Language::PlutusV1] + } else if !v1_scripts && v2_scripts { + vec![Language::PlutusV2] + } else { + vec![Language::PlutusV1, Language::PlutusV2] + } +} + +// The metadata of the transaction is valid. +fn check_auxiliary_data(tx_body: &MintedTransactionBody, mtx: &MintedTx) -> ValidationResult { + match ( + &tx_body.auxiliary_data_hash, + aux_data_from_babbage_minted_tx(mtx), + ) { + (Some(metadata_hash), Some(metadata)) => { + if metadata_hash.as_slice() + == pallas_crypto::hash::Hasher::<256>::hash(metadata).as_ref() + { + Ok(()) + } else { + Err(Babbage(MetadataHash)) + } + } + (None, None) => Ok(()), + _ => Err(Babbage(MetadataHash)), + } +} + +fn check_script_data_hash( + tx_body: &MintedTransactionBody, + mtx: &MintedTx, + utxos: &UTxOs, + block_slot: &u64, +) -> ValidationResult { + match tx_body.script_data_hash { + Some(script_data_hash) => match ( + &mtx.transaction_witness_set.plutus_data, + &mtx.transaction_witness_set.redeemer, + ) { + (Some(plutus_data), Some(redeemer)) => { + let plutus_data: Vec = plutus_data + .iter() + .map(|x| KeepRaw::unwrap(x.clone())) + .collect(); + // The Plutus data part of the script integrity hash may either need to be + // serialized as a indefinite-length array, or a definite-length one. + // TODO: compute only the correct hash, not both of them. + let (indefinite_hash, definite_hash) = compute_script_integrity_hash( + &tx_languages(mtx, utxos), + &plutus_data, + redeemer, + block_slot, + ); + if script_data_hash == indefinite_hash || script_data_hash == definite_hash { + Ok(()) + } else { + Err(Babbage(ScriptIntegrityHash)) + } + } + (_, _) => Err(Babbage(ScriptIntegrityHash)), + }, + None => { + if option_vec_is_empty(&mtx.transaction_witness_set.plutus_data) + && option_vec_is_empty(&mtx.transaction_witness_set.redeemer) + { + Ok(()) + } else { + Err(Babbage(ScriptIntegrityHash)) + } + } + } +} + +// The Plutus data is encoded both as an indefinite-length and a definite-length +// array. Hence, two hashes are computed. +// TODO: compute only the necessary form, which requires knowing the original +// encoding in the MintedWitnessSet. +fn compute_script_integrity_hash( + tx_languages: &[Language], + plutus_data: &[PlutusData], + redeemer: &[Redeemer], + block_slot: &u64, +) -> (Hash<32>, Hash<32>) { + // Indefinite Plutus data serialization + let mut value_to_hash_with_indef: Vec = Vec::new(); + let _ = encode(redeemer, &mut value_to_hash_with_indef); + if !plutus_data.is_empty() { + let mut plutus_data_encoder_indef: Encoder> = Encoder::new(Vec::new()); + let _ = plutus_data_encoder_indef.begin_array(); + for single_plutus_data in plutus_data.iter() { + let _ = plutus_data_encoder_indef.encode(single_plutus_data); + } + let _ = plutus_data_encoder_indef.end(); + value_to_hash_with_indef.extend(plutus_data_encoder_indef.writer().clone()); + } + let cost_model = cost_model_cbor(tx_languages, block_slot); + value_to_hash_with_indef.extend(&cost_model); + // Definite Plutus data serialization + let mut value_to_hash_with_def: Vec = Vec::new(); + let _ = encode(redeemer, &mut value_to_hash_with_def); + if !plutus_data.is_empty() { + let mut plutus_data_encoder_def: Encoder> = Encoder::new(Vec::new()); + let _ = plutus_data_encoder_def.array(plutus_data.len() as u64); + for single_plutus_data in plutus_data.iter() { + let _ = plutus_data_encoder_def.encode(single_plutus_data); + } + value_to_hash_with_def.extend(plutus_data_encoder_def.writer().clone()); + } + value_to_hash_with_def.extend(&cost_model); + ( + pallas_crypto::hash::Hasher::<256>::hash(&value_to_hash_with_indef), + pallas_crypto::hash::Hasher::<256>::hash(&value_to_hash_with_def), + ) +} + +// Precondition: !tx_languages.is_empty() +fn cost_model_cbor(tx_languages: &[Language], block_slot: &u64) -> Vec { + if *block_slot < 72748820 { + hex::decode( + "a141005901d59f1a000302590001011a00060bc719026d00011a000249f01903e800011a000249f018201a0025cea81971f70419744d186419744d186419744d186419744d186419744d186419744d18641864186419744d18641a000249f018201a000249f018201a000249f018201a000249f01903e800011a000249f018201a000249f01903e800081a000242201a00067e2318760001011a000249f01903e800081a000249f01a0001b79818f7011a000249f0192710011a0002155e19052e011903e81a000249f01903e8011a000249f018201a000249f018201a000249f0182001011a000249f0011a000249f0041a000194af18f8011a000194af18f8011a0002377c190556011a0002bdea1901f1011a000249f018201a000249f018201a000249f018201a000249f018201a000249f018201a000249f018201a000242201a00067e23187600010119f04c192bd200011a000249f018201a000242201a00067e2318760001011a000242201a00067e2318760001011a0025cea81971f704001a000141bb041a000249f019138800011a000249f018201a000302590001011a000249f018201a000249f018201a000249f018201a000249f018201a000249f018201a000249f018201a000249f018201a00330da70101ff" + ).unwrap() + } else if *block_slot <= 84844885 { + // Prior to first block in epoch 394 + if tx_languages.contains(&Language::PlutusV1) && !tx_languages.contains(&Language::PlutusV2) + { + hex::decode( + "a141005901b69f1a0003236119032c01011903e819023b00011903e8195e7104011903e818201a0001ca761928eb041959d818641959d818641959d818641959d818641959d818641959d81864186418641959d81864194c5118201a0002acfa182019b551041a000363151901ff00011a00015c3518201a000797751936f404021a0002ff941a0006ea7818dc0001011903e8196ff604021a0003bd081a00034ec5183e011a00102e0f19312a011a00032e801901a5011a0002da781903e819cf06011a00013a34182019a8f118201903e818201a00013aac0119e143041903e80a1a00030219189c011a00030219189c011a0003207c1901d9011a000330001901ff0119ccf3182019fd40182019ffd5182019581e18201940b318201a00012adf18201a0002ff941a0006ea7818dc0001011a00010f92192da7000119eabb18201a0002ff941a0006ea7818dc0001011a0002ff941a0006ea7818dc0001011a000c504e197712041a001d6af61a0001425b041a00040c660004001a00014fab18201a0003236119032c010119a0de18201a00033d7618201979f41820197fb8182019a95d1820197df718201995aa18201a009063b91903fd0aff" + ).unwrap() + } else if !tx_languages.contains(&Language::PlutusV1) + && tx_languages.contains(&Language::PlutusV2) + { + hex::decode( + "a10198af1a0003236119032c01011903e819023b00011903e8195e7104011903e818201a0001ca761928eb041959d818641959d818641959d818641959d818641959d818641959d81864186418641959d81864194c5118201a0002acfa182019b551041a000363151901ff00011a00015c3518201a000797751936f404021a0002ff941a0006ea7818dc0001011903e8196ff604021a0003bd081a00034ec5183e011a00102e0f19312a011a00032e801901a5011a0002da781903e819cf06011a00013a34182019a8f118201903e818201a00013aac0119e143041903e80a1a00030219189c011a00030219189c011a0003207c1901d9011a000330001901ff0119ccf3182019fd40182019ffd5182019581e18201940b318201a00012adf18201a0002ff941a0006ea7818dc0001011a00010f92192da7000119eabb18201a0002ff941a0006ea7818dc0001011a0002ff941a0006ea7818dc0001011a0011b22c1a0005fdde00021a000c504e197712041a001d6af61a0001425b041a00040c660004001a00014fab18201a0003236119032c010119a0de18201a00033d7618201979f41820197fb8182019a95d1820197df718201995aa18201b00000004a817c8001b00000004a817c8001a009063b91903fd0a1b00000004a817c800001b00000004a817c800" + ).unwrap() + } else { + // Precondition allows us to conclude both PlutusV1 and PlutusV2 are required by + // the transaction + hex::decode( + "a241005901b69f1a0003236119032c01011903e819023b00011903e8195e7104011903e818201a0001ca761928eb041959d818641959d818641959d818641959d818641959d818641959d81864186418641959d81864194c5118201a0002acfa182019b551041a000363151901ff00011a00015c3518201a000797751936f404021a0002ff941a0006ea7818dc0001011903e8196ff604021a0003bd081a00034ec5183e011a00102e0f19312a011a00032e801901a5011a0002da781903e819cf06011a00013a34182019a8f118201903e818201a00013aac0119e143041903e80a1a00030219189c011a00030219189c011a0003207c1901d9011a000330001901ff0119ccf3182019fd40182019ffd5182019581e18201940b318201a00012adf18201a0002ff941a0006ea7818dc0001011a00010f92192da7000119eabb18201a0002ff941a0006ea7818dc0001011a0002ff941a0006ea7818dc0001011a000c504e197712041a001d6af61a0001425b041a00040c660004001a00014fab18201a0003236119032c010119a0de18201a00033d7618201979f41820197fb8182019a95d1820197df718201995aa18201a009063b91903fd0aff0198af1a0003236119032c01011903e819023b00011903e8195e7104011903e818201a0001ca761928eb041959d818641959d818641959d818641959d818641959d818641959d81864186418641959d81864194c5118201a0002acfa182019b551041a000363151901ff00011a00015c3518201a000797751936f404021a0002ff941a0006ea7818dc0001011903e8196ff604021a0003bd081a00034ec5183e011a00102e0f19312a011a00032e801901a5011a0002da781903e819cf06011a00013a34182019a8f118201903e818201a00013aac0119e143041903e80a1a00030219189c011a00030219189c011a0003207c1901d9011a000330001901ff0119ccf3182019fd40182019ffd5182019581e18201940b318201a00012adf18201a0002ff941a0006ea7818dc0001011a00010f92192da7000119eabb18201a0002ff941a0006ea7818dc0001011a0002ff941a0006ea7818dc0001011a0011b22c1a0005fdde00021a000c504e197712041a001d6af61a0001425b041a00040c660004001a00014fab18201a0003236119032c010119a0de18201a00033d7618201979f41820197fb8182019a95d1820197df718201995aa18201b00000004a817c8001b00000004a817c8001a009063b91903fd0a1b00000004a817c800001b00000004a817c800" + ).unwrap() + } + } else { + // Starting from first block in epoch 394 + if tx_languages.contains(&Language::PlutusV1) && !tx_languages.contains(&Language::PlutusV2) + { + hex::decode( + "a141005901b69f1a0003236119032c01011903e819023b00011903e8195e7104011903e818201a0001ca761928eb041959d818641959d818641959d818641959d818641959d818641959d81864186418641959d81864194c5118201a0002acfa182019b551041a000363151901ff00011a00015c3518201a000797751936f404021a0002ff941a0006ea7818dc0001011903e8196ff604021a0003bd081a00034ec5183e011a00102e0f19312a011a00032e801901a5011a0002da781903e819cf06011a00013a34182019a8f118201903e818201a00013aac0119e143041903e80a1a00030219189c011a00030219189c011a0003207c1901d9011a000330001901ff0119ccf3182019fd40182019ffd5182019581e18201940b318201a00012adf18201a0002ff941a0006ea7818dc0001011a00010f92192da7000119eabb18201a0002ff941a0006ea7818dc0001011a0002ff941a0006ea7818dc0001011a000c504e197712041a001d6af61a0001425b041a00040c660004001a00014fab18201a0003236119032c010119a0de18201a00033d7618201979f41820197fb8182019a95d1820197df718201995aa18201a0374f693194a1f0aff" + ).unwrap() + } else if !tx_languages.contains(&Language::PlutusV1) + && tx_languages.contains(&Language::PlutusV2) + { + hex::decode( + "a10198af1a0003236119032c01011903e819023b00011903e8195e7104011903e818201a0001ca761928eb041959d818641959d818641959d818641959d818641959d818641959d81864186418641959d81864194c5118201a0002acfa182019b551041a000363151901ff00011a00015c3518201a000797751936f404021a0002ff941a0006ea7818dc0001011903e8196ff604021a0003bd081a00034ec5183e011a00102e0f19312a011a00032e801901a5011a0002da781903e819cf06011a00013a34182019a8f118201903e818201a00013aac0119e143041903e80a1a00030219189c011a00030219189c011a0003207c1901d9011a000330001901ff0119ccf3182019fd40182019ffd5182019581e18201940b318201a00012adf18201a0002ff941a0006ea7818dc0001011a00010f92192da7000119eabb18201a0002ff941a0006ea7818dc0001011a0002ff941a0006ea7818dc0001011a0011b22c1a0005fdde00021a000c504e197712041a001d6af61a0001425b041a00040c660004001a00014fab18201a0003236119032c010119a0de18201a00033d7618201979f41820197fb8182019a95d1820197df718201995aa18201a0223accc0a1a0374f693194a1f0a1a02515e841980b30a" + ).unwrap() + } else { + hex::decode( + "a241005901b69f1a0003236119032c01011903e819023b00011903e8195e7104011903e818201a0001ca761928eb041959d818641959d818641959d818641959d818641959d818641959d81864186418641959d81864194c5118201a0002acfa182019b551041a000363151901ff00011a00015c3518201a000797751936f404021a0002ff941a0006ea7818dc0001011903e8196ff604021a0003bd081a00034ec5183e011a00102e0f19312a011a00032e801901a5011a0002da781903e819cf06011a00013a34182019a8f118201903e818201a00013aac0119e143041903e80a1a00030219189c011a00030219189c011a0003207c1901d9011a000330001901ff0119ccf3182019fd40182019ffd5182019581e18201940b318201a00012adf18201a0002ff941a0006ea7818dc0001011a00010f92192da7000119eabb18201a0002ff941a0006ea7818dc0001011a0002ff941a0006ea7818dc0001011a000c504e197712041a001d6af61a0001425b041a00040c660004001a00014fab18201a0003236119032c010119a0de18201a00033d7618201979f41820197fb8182019a95d1820197df718201995aa18201a0374f693194a1f0aff0198af1a0003236119032c01011903e819023b00011903e8195e7104011903e818201a0001ca761928eb041959d818641959d818641959d818641959d818641959d818641959d81864186418641959d81864194c5118201a0002acfa182019b551041a000363151901ff00011a00015c3518201a000797751936f404021a0002ff941a0006ea7818dc0001011903e8196ff604021a0003bd081a00034ec5183e011a00102e0f19312a011a00032e801901a5011a0002da781903e819cf06011a00013a34182019a8f118201903e818201a00013aac0119e143041903e80a1a00030219189c011a00030219189c011a0003207c1901d9011a000330001901ff0119ccf3182019fd40182019ffd5182019581e18201940b318201a00012adf18201a0002ff941a0006ea7818dc0001011a00010f92192da7000119eabb18201a0002ff941a0006ea7818dc0001011a0002ff941a0006ea7818dc0001011a0011b22c1a0005fdde00021a000c504e197712041a001d6af61a0001425b041a00040c660004001a00014fab18201a0003236119032c010119a0de18201a00033d7618201979f41820197fb8182019a95d1820197df718201995aa18201a0223accc0a1a0374f693194a1f0a1a02515e841980b30a" + ).unwrap() + } + } +} + +fn option_vec_is_empty(option_vec: &Option>) -> bool { + match option_vec { + Some(vec) => vec.is_empty(), + None => true, + } +} diff --git a/pallas-applying/src/lib.rs b/pallas-applying/src/lib.rs index 254d1d0d..b2e615cc 100644 --- a/pallas-applying/src/lib.rs +++ b/pallas-applying/src/lib.rs @@ -1,11 +1,13 @@ //! Logic for validating and applying new blocks and txs to the chain state pub mod alonzo; +pub mod babbage; pub mod byron; pub mod shelley_ma; pub mod utils; use alonzo::validate_alonzo_tx; +use babbage::validate_babbage_tx; use byron::validate_byron_tx; use pallas_traverse::{Era, MultiEraTx}; use shelley_ma::validate_shelley_ma_tx; @@ -40,5 +42,11 @@ pub fn validate(metx: &MultiEraTx, utxos: &UTxOs, env: &Environment) -> Validati } _ => Err(TxAndProtParamsDiffer), }, + MultiEraProtParams::Babbage(bpp) => match metx { + MultiEraTx::Babbage(mtx) => { + validate_babbage_tx(mtx, utxos, bpp, env.block_slot(), env.network_id()) + } + _ => Err(TxAndProtParamsDiffer), + }, } } diff --git a/pallas-applying/src/shelley_ma.rs b/pallas-applying/src/shelley_ma.rs index 33ddc779..5e3b625d 100644 --- a/pallas-applying/src/shelley_ma.rs +++ b/pallas-applying/src/shelley_ma.rs @@ -1,9 +1,10 @@ //! Utilities required for ShelleyMA-era transaction validation. use crate::utils::{ - add_minted_value, add_values, empty_value, extract_auxiliary_data, get_alonzo_comp_tx_size, - get_lovelace_from_alonzo_val, get_payment_part, get_shelley_address, get_val_size_in_words, - mk_alonzo_vk_wits_check_list, values_are_equal, verify_signature, FeePolicy, + add_minted_value, add_values, aux_data_from_alonzo_minted_tx, empty_value, + get_alonzo_comp_tx_size, get_lovelace_from_alonzo_val, get_payment_part, get_shelley_address, + get_val_size_in_words, mk_alonzo_vk_wits_check_list, values_are_equal, verify_signature, + FeePolicy, ShelleyMAError::*, ShelleyProtParams, UTxOs, 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 { - 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)) => { if metadata_hash.as_slice() == pallas_crypto::hash::Hasher::<256>::hash(metadata).as_ref() @@ -222,7 +226,9 @@ fn check_witnesses( match utxos.get(&MultiEraInput::from_alonzo_compatible(input)) { Some(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) => { check_vk_wit(&payment_key_hash, tx_hash, vk_wits)? } diff --git a/pallas-applying/src/utils.rs b/pallas-applying/src/utils.rs index b2dbb444..ccd7625a 100644 --- a/pallas-applying/src/utils.rs +++ b/pallas-applying/src/utils.rs @@ -10,9 +10,12 @@ use pallas_codec::{ utils::{Bytes, KeepRaw, KeyValuePairs}, }; use pallas_crypto::key::ed25519::{PublicKey, Signature}; -use pallas_primitives::alonzo::{ - AssetName, AuxiliaryData, Coin, MintedTx, Multiasset, NetworkId, PolicyId, TransactionBody, - TransactionOutput, VKeyWitness, Value, +use pallas_primitives::{ + alonzo::{ + 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 std::collections::HashMap; @@ -29,6 +32,14 @@ pub fn get_alonzo_comp_tx_size(tx_body: &TransactionBody) -> Option { } } +pub fn get_babbage_tx_size(tx_body: &MintedTransactionBody) -> Option { + let mut buff: Vec = Vec::new(); + match encode(tx_body, &mut buff) { + Ok(()) => Some(buff.len() as u64), + Err(_) => None, + } +} + pub fn empty_value() -> Value { Value::Multiasset(0, Multiasset::::from(Vec::new())) } @@ -52,6 +63,61 @@ pub fn add_values( } } +pub fn lovelace_diff_or_fail( + first: &Value, + second: &Value, + err: &ValidationError, +) -> Result { + 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, sma: &Multiasset) -> 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( base_value: &Value, minted_value: &Multiasset, @@ -155,24 +221,7 @@ pub fn values_are_equal(first: &Value, second: &Value) -> bool { if f != s { false } else { - for (fpolicy, fassets) in fma.iter() { - 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 + multi_assets_are_equal(fma, sma) } } } @@ -235,8 +284,8 @@ pub fn verify_signature(vk_wit: &VKeyWitness, data_to_verify: &[u8]) -> bool { public_key.verify(data_to_verify, &sig) } -pub fn get_payment_part(tx_out: &TransactionOutput) -> Option { - let addr: ShelleyAddress = get_shelley_address(Bytes::deref(&tx_out.address))?; +pub fn get_payment_part(address: &Bytes) -> Option { + let addr: ShelleyAddress = get_shelley_address(Bytes::deref(address))?; Some(addr.payment().clone()) } @@ -247,7 +296,17 @@ pub fn get_shelley_address(address: &[u8]) -> Option { } } -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::>::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::>::from((mtx.auxiliary_data).clone()) .as_ref() .map(KeepRaw::raw_cbor) @@ -258,3 +317,22 @@ pub fn get_val_size_in_words(val: &Value) -> u64 { let _ = encode(val, &mut tx_buf); (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 = 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 = Vec::from(script.as_ref()); + payload.insert(0, 1); + pallas_crypto::hash::Hasher::<224>::hash(&payload) +} diff --git a/pallas-applying/src/utils/environment.rs b/pallas-applying/src/utils/environment.rs index 933e31a2..bd16e151 100644 --- a/pallas-applying/src/utils/environment.rs +++ b/pallas-applying/src/utils/environment.rs @@ -16,6 +16,7 @@ pub enum MultiEraProtParams { Byron(ByronProtParams), Shelley(ShelleyProtParams), Alonzo(AlonzoProtParams), + Babbage(BabbageProtParams), } #[derive(Debug, Clone)] @@ -51,6 +52,20 @@ pub struct AlonzoProtParams { 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 { pub fn prot_params(&self) -> &MultiEraProtParams { &self.prot_params diff --git a/pallas-applying/src/utils/validation.rs b/pallas-applying/src/utils/validation.rs index 11fec087..a7f4bbb4 100644 --- a/pallas-applying/src/utils/validation.rs +++ b/pallas-applying/src/utils/validation.rs @@ -7,6 +7,7 @@ pub enum ValidationError { Byron(ByronError), ShelleyMA(ShelleyMAError), Alonzo(AlonzoError), + Babbage(BabbageError), } #[derive(Debug, Clone)] @@ -91,4 +92,51 @@ pub enum AlonzoError { 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>; diff --git a/pallas-applying/tests/README.md b/pallas-applying/tests/README.md index ac829860..bcbda2de 100644 --- a/pallas-applying/tests/README.md +++ b/pallas-applying/tests/README.md @@ -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. - **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. + +### 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. diff --git a/pallas-applying/tests/alonzo.rs b/pallas-applying/tests/alonzo.rs index 5f96abf0..67dbf47d 100644 --- a/pallas-applying/tests/alonzo.rs +++ b/pallas-applying/tests/alonzo.rs @@ -148,7 +148,7 @@ mod alonzo_tests { ), ], ); - add_collateral( + add_collateral_alonzo( &mtx.transaction_body, &mut utxos, &[( @@ -315,7 +315,7 @@ mod alonzo_tests { } #[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. fn unfound_utxo_input() { let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/alonzo1.tx")); @@ -454,7 +454,7 @@ mod alonzo_tests { #[test] // Same as succesful_mainnet_tx, except that validation is called with an // Environment requesting fees that exceed those paid by the transaction. - fn min_fees_unreached() { + fn min_fee_unreached() { let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/alonzo1.tx")); let mtx: MintedTx = minted_tx_from_cbor(&cbor_bytes); 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, &mut utxos, &[( @@ -698,7 +698,7 @@ mod alonzo_tests { ), ], ); - add_collateral( + add_collateral_alonzo( &mtx.transaction_body, &mut utxos, &[( @@ -954,7 +954,7 @@ mod alonzo_tests { ), ], ); - add_collateral( + add_collateral_alonzo( &mtx.transaction_body, &mut utxos, &[( @@ -1082,7 +1082,7 @@ mod alonzo_tests { ), ], ); - add_collateral( + add_collateral_alonzo( &mtx.transaction_body, &mut utxos, &[( @@ -1122,7 +1122,7 @@ mod alonzo_tests { #[test] // 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() { let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/alonzo1.tx")); let mut mtx: MintedTx = minted_tx_from_cbor(&cbor_bytes); @@ -1162,7 +1162,7 @@ mod alonzo_tests { network_id: 1, }; 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 { Alonzo(AlonzoError::PreservationOfValue) => (), _ => panic!("Unexpected error ({:?})", err), @@ -1232,7 +1232,7 @@ mod alonzo_tests { network_id: 1, }; 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 { Alonzo(AlonzoError::OutputWrongNetworkID) => (), _ => panic!("Unexpected error ({:?})", err), @@ -1282,7 +1282,7 @@ mod alonzo_tests { network_id: 1, }; 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 { Alonzo(AlonzoError::TxWrongNetworkID) => (), _ => panic!("Unexpected error ({:?})", err), @@ -1292,7 +1292,7 @@ mod alonzo_tests { #[test] // 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() { let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/alonzo2.tx")); let mtx: MintedTx = minted_tx_from_cbor(&cbor_bytes); @@ -1369,7 +1369,7 @@ mod alonzo_tests { ), ], ); - add_collateral( + add_collateral_alonzo( &mtx.transaction_body, &mut utxos, &[( @@ -1533,7 +1533,7 @@ mod alonzo_tests { ), ], ); - add_collateral( + add_collateral_alonzo( &mtx.transaction_body, &mut utxos, &[( @@ -1768,7 +1768,7 @@ mod alonzo_tests { ), ], ); - add_collateral( + add_collateral_alonzo( &mtx.transaction_body, &mut utxos, &[( @@ -1891,7 +1891,7 @@ mod alonzo_tests { ), ], ); - add_collateral( + add_collateral_alonzo( &mtx.transaction_body, &mut utxos, &[( @@ -1986,7 +1986,7 @@ mod alonzo_tests { network_id: 1, }; 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 { Alonzo(AlonzoError::MintingLacksPolicy) => (), _ => panic!("Unexpected error ({:?})", err), @@ -2072,7 +2072,7 @@ mod alonzo_tests { ), ], ); - add_collateral( + add_collateral_alonzo( &mtx.transaction_body, &mut utxos, &[( @@ -2195,7 +2195,7 @@ mod alonzo_tests { ), ], ); - add_collateral( + add_collateral_alonzo( &mtx.transaction_body, &mut utxos, &[( @@ -2324,7 +2324,7 @@ mod alonzo_tests { ), ], ); - add_collateral( + add_collateral_alonzo( &mtx.transaction_body, &mut utxos, &[( @@ -2454,7 +2454,7 @@ mod alonzo_tests { ), ], ); - add_collateral( + add_collateral_alonzo( &mtx.transaction_body, &mut utxos, &[( @@ -2536,7 +2536,7 @@ mod alonzo_tests { network_id: 1, }; match validate(&metx, &utxos, &env) { - Ok(()) => panic!("Unneeded redeemer"), + Ok(()) => panic!("Transaction auxiliary data removed"), Err(err) => match err { Alonzo(AlonzoError::MetadataHash) => (), _ => panic!("Unexpected error ({:?})", err), @@ -2545,7 +2545,7 @@ mod alonzo_tests { } #[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. fn min_lovelace_unreached() { let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/alonzo1.tx")); @@ -2573,14 +2573,14 @@ mod alonzo_tests { max_val_size: 5000, collateral_percent: 150, max_collateral_inputs: 3, - coins_per_utxo_word: 10000000, + coins_per_utxo_word: 10000000, // This was 34482 during Alonzo on mainnet. }), prot_magic: 764824073, block_slot: 44237276, network_id: 1, }; match validate(&metx, &utxos, &env) { - Ok(()) => panic!("Unneeded redeemer"), + Ok(()) => panic!("Output minimum lovelace is unreached"), Err(err) => match err { Alonzo(AlonzoError::MinLovelaceUnreached) => (), _ => panic!("Unexpected error ({:?})", err), @@ -2624,7 +2624,7 @@ mod alonzo_tests { network_id: 1, }; match validate(&metx, &utxos, &env) { - Ok(()) => panic!("Unneeded redeemer"), + Ok(()) => panic!("Max value size exceeded"), Err(err) => match err { Alonzo(AlonzoError::MaxValSizeExceeded) => (), _ => panic!("Unexpected error ({:?})", err), @@ -2712,7 +2712,7 @@ mod alonzo_tests { ), ], ); - add_collateral( + add_collateral_alonzo( &mtx.transaction_body, &mut utxos, &[( diff --git a/pallas-applying/tests/babbage.rs b/pallas-applying/tests/babbage.rs new file mode 100644 index 00000000..01d987e0 --- /dev/null +++ b/pallas-applying/tests/babbage.rs @@ -0,0 +1,2400 @@ +pub mod common; + +use common::*; +use hex; +use pallas_addresses::{Address, Network, ShelleyAddress, ShelleyPaymentPart}; +use pallas_applying::{ + utils::{ + BabbageError, BabbageProtParams, Environment, FeePolicy, MultiEraProtParams, + ValidationError::*, + }, + validate, UTxOs, +}; +use pallas_codec::utils::{Bytes, CborWrap, KeepRaw, KeyValuePairs}; +use pallas_codec::{ + minicbor::{ + decode::{Decode, Decoder}, + encode, + }, + utils::Nullable, +}; +use pallas_primitives::babbage::{ + ExUnits, MintedDatumOption, MintedPostAlonzoTransactionOutput, MintedScriptRef, + MintedTransactionBody, MintedTransactionOutput, MintedTx, MintedWitnessSet, NetworkId, + PlutusData, PlutusV2Script, PseudoDatumOption, PseudoScript, PseudoTransactionOutput, Redeemer, + RedeemerTag, Value, +}; +use pallas_traverse::{MultiEraInput, MultiEraOutput, MultiEraTx}; +use std::borrow::Cow; + +#[cfg(test)] +mod babbage_tests { + use super::*; + + #[test] + // Transaction hash: + // b17d685c42e714238c1fb3abcd40e5c6291ebbb420c9c69b641209607bd00c7d + fn successful_mainnet_tx() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/babbage3.tx")); + let mtx: MintedTx = babbage_minted_tx_from_cbor(&cbor_bytes); + let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let tx_outs_info: &[( + String, + Value, + Option, + Option>, + )] = &[( + String::from(include_str!("../../test_data/babbage3.address")), + Value::Coin(103324335), + None, + None, + )]; + let utxos: UTxOs = mk_utxo_for_babbage_tx(&mtx.transaction_body, tx_outs_info); + let env: Environment = Environment { + prot_params: MultiEraProtParams::Babbage(BabbageProtParams { + fee_policy: FeePolicy { + summand: 155381, + multiplier: 44, + }, + max_tx_size: 16384, + max_block_ex_mem: 62000000, + max_block_ex_steps: 40000000000, + max_tx_ex_mem: 14000000, + max_tx_ex_steps: 10000000000, + max_val_size: 5000, + collateral_percent: 150, + max_collateral_inputs: 3, + coins_per_utxo_word: 4310, + }), + prot_magic: 764824073, + block_slot: 72316896, + network_id: 1, + }; + match validate(&metx, &utxos, &env) { + Ok(()) => (), + Err(err) => assert!(false, "Unexpected error ({:?})", err), + } + } + + #[test] + // Transaction hash: + // f33d6f7eb877132af7307e385bb24a7d2c12298c8ac0b1460296748810925ccc + fn successful_mainnet_tx_with_plutus_v1_script() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/babbage4.tx")); + let mtx: MintedTx = babbage_minted_tx_from_cbor(&cbor_bytes); + let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let tx_outs_info: &[( + String, + Value, + Option, + Option>, + )] = &[ + ( + String::from(include_str!("../../test_data/babbage4.0.address")), + Value::Coin(25000000), + Some(PseudoDatumOption::Hash( + hex::decode("3e8c4b1d396bb8132e5097f5a2f012d97900cbc496a3745db4226cea4cb66465") + .unwrap() + .as_slice() + .into(), + )), + None, + ), + ( + String::from(include_str!("../../test_data/babbage4.1.address")), + Value::Multiasset( + 1795660, + KeyValuePairs::from(Vec::from([( + "787f0c946b98153500edc0a753e65457250544da8486b17c85708135" + .parse() + .unwrap(), + KeyValuePairs::from(Vec::from([( + Bytes::from( + hex::decode("506572666563744c6567656e64617279446572705365616c") + .unwrap(), + ), + 1, + )])), + )])), + ), + None, + None, + ), + ]; + let mut utxos: UTxOs = mk_utxo_for_babbage_tx(&mtx.transaction_body, tx_outs_info); + let collateral_info: &[( + String, + Value, + Option, + Option>, + )] = &[( + String::from(include_str!("../../test_data/babbage4.collateral.address")), + Value::Coin(5000000), + None, + None, + )]; + add_collateral_babbage(&mtx.transaction_body, &mut utxos, collateral_info); + let env: Environment = Environment { + prot_params: MultiEraProtParams::Babbage(BabbageProtParams { + fee_policy: FeePolicy { + summand: 155381, + multiplier: 44, + }, + max_tx_size: 16384, + max_block_ex_mem: 62000000, + max_block_ex_steps: 40000000000, + max_tx_ex_mem: 14000000, + max_tx_ex_steps: 10000000000, + max_val_size: 5000, + collateral_percent: 150, + max_collateral_inputs: 3, + coins_per_utxo_word: 4310, + }), + prot_magic: 764824073, + block_slot: 72317003, + network_id: 1, + }; + match validate(&metx, &utxos, &env) { + Ok(()) => (), + Err(err) => assert!(false, "Unexpected error ({:?})", err), + } + } + + #[test] + // Transaction hash: + // ac96a0a2dfdb876b237a8ae674eadab453fd146fb97b221cfd29a1812046fa36 + fn successful_mainnet_tx_with_plutus_v2_script() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/babbage7.tx")); + let mtx: MintedTx = babbage_minted_tx_from_cbor(&cbor_bytes); + let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let tx_outs_info: &[( + String, + Value, + Option, + Option>, + )] = &[ + ( + String::from(include_str!("../../test_data/babbage7.0.address")), + Value::Multiasset( + 1318860, + KeyValuePairs::from(Vec::from([( + "95ab9a125c900c14cf7d39093e3577b0c8e39c9f7548a8301a28ee2d" + .parse() + .unwrap(), + KeyValuePairs::from(Vec::from([( + Bytes::from(hex::decode("4164614964696f7431313235").unwrap()), + 1, + )])), + )])), + ), + Some(PseudoDatumOption::Hash( + hex::decode("d75ad82787a8d45b85c156c97736d2c6525d6b3a09b5d6297d1b45c6a63bccd3") + .unwrap() + .as_slice() + .into(), + )), + None, + ), + ( + String::from(include_str!("../../test_data/babbage7.1.address")), + Value::Coin(231630402), + None, + None, + ), + ]; + let mut utxos: UTxOs = mk_utxo_for_babbage_tx(&mtx.transaction_body, tx_outs_info); + let collateral_info: &[( + String, + Value, + Option, + Option>, + )] = &[( + String::from(include_str!("../../test_data/babbage7.collateral.address")), + Value::Coin(5000000), + None, + None, + )]; + add_collateral_babbage(&mtx.transaction_body, &mut utxos, collateral_info); + let ref_input_info: &[( + String, + Value, + Option, + Option>, + )] = &[( + String::from(include_str!("../../test_data/babbage7.reference.address")), + Value::Coin(40000000), + None, + Some(CborWrap(PseudoScript::PlutusV2Script(PlutusV2Script(Bytes::from(hex::decode("5909fe010000323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232222323232533535533357346064606a0062646464642466002008004a666ae68c0d8c0e00044c848c004008c078d5d0981b8008191baa357426ae88c0d80154ccd5cd1819981b0008991919191919191919191919191919191919191919190919999999999980080b80a8098088078068058048038028018011aba135744004666068eb88004d5d08009aba2002357420026ae88008cc0c9d71aba1001357440046ae84004d5d10011aba1001357440046ae84004d5d10011aba1001357440046ae84004d5d10011981300f1aba1001357440046ae84004d5d1181b001198111192999ab9a30353038001132321233001003002301d357426ae88c0e0008c078d5d0981b8008191baa00135742606a0020606ea8d5d0981a001817911a8011111111111111a80691919299aa99a998149aa99a80109815a481035054380022100203d00303903a03a1533501213302549101350033302330340362350012232333027303803a235001223500122533533302b0440040062153353333026303e040223500222533500321533533303104a0030062153353302b0010031303f3305722533500104c221350022253353305100200a100313304d33047002001300600300215335330370010031303f333302d04b0043370200200600409209008e60720020044266060920102313000333573466e20ccd54c0fc104c0a8cc0f1c024000400266aa608008246a00209600200809208e266ae712410231310004813357389201023132000470023335530360393501b0403501b04233355303603922533535002222253353302200800413038003042213303d001002100103f010333301c303403622350022253353303c00b002100313333020303803a235001222533533302a0210030012133330260220043355303e03f235001223303d002333500120012235002223500322330433370000800466aa608e09046a002446608c004666a0024002e008004ccc0c013400c0048004ccc09c11000c0040084cccc09408400c00800400c0040f140044cc0952410134003330233034036235001223303b00a0025001153353355303403523500122350012222302c533350021303104821001213304e2253350011303404a221350022253353304800200710011300600300c0011302a49010136002213355303603723500122350012222302e533350021303304a2100121330502253350011303604c221350022253353304a00200710011300600300e0033335530310342253353353530283500203f03d203f253353303c001330482253350011302e044221350022253353303000200a135302f001223350022303504b20011300600301003b1302c4901013300133037002001100103a00d1120011533573892010350543500165333573460640020502a666ae68c0c400409c0b8c0ccdd50019baa00133019223355301f020235001223301e002335530220232350012233021002333500137009000380233700002900000099aa980f81011a800911980f001199a800919aa981181211a8009119811001180880080091199806815001000919aa981181211a80091198110011809000800999804012801000812111919807198021a8018139a801013a99a9a80181490a99a8011099a801119a80111980400100091101711119a80210171112999ab9a3370e00c0062a666ae68cdc38028010998068020008158158120a99a80090120121a8008141119a801119a8011198128010009014119a801101411981280100091199ab9a3370e00400204604a44446666aa00866032444600660040024002006002004444466aa603803a46a0024466036004666a0024002052400266600a0080026603c66030006004046444666aa603003603866aa603403646a00244660320046010002666aa6030036446a00444a66a666aa603a03e60106603444a66a00404a200204e46a002446601400400a00c200626604000800604200266aa603403646a00244660320046605e44a66a002260160064426a00444a66a6601800401022444660040140082600c00600800446602644666a0060420040026a00204242444600600842444600200844604e44a66a0020364426a00444a66a6601000400e2602a0022600c0064466aa0046602000603600244a66a004200202e44a66a00202e266ae7000806c8c94ccd5cd180f9811000899190919800801801198079192999ab9a3022302500113232123300100300233301075c464a666ae68c094c0a00044c8cc0514cd4cc028005200110011300e4901022d330033301375c464a66a660180029000080089808249022d3200375a0026ae84d5d118140011bad35742604e0020446ea8004d5d09aba23025002300c35742604800203e6ea8004d5d09aba23022002375c6ae84c084004070dd500091199ab9a3371200400203202e46a002444400844a666ae68cdc79a80100b1a80080b0999ab9a3370e6a0040306a00203002a02e024464a666ae68c06cc0780044c8c8c8c8c8c8c8c848cccc00402401c00c008d5d09aba20045333573466e1d2004001132122230020043574260460042a666ae68c0880044c84888c004010dd71aba1302300215333573460420022244400603c60460026ea8d5d08009aba200233300a75c66014eb9d69aba100135744603c004600a6ae84c074004060dd50009299ab9c001162325333573460326038002264646424660020060046eb4d5d09aba2301d003533357346034603a00226eb8d5d0980e00080b9baa35742603600202c6ea80048c94ccd5cd180c180d80089919191909198008028012999ab9a301b00113232300953335734603c00226464646424466600200c0080066eb4d5d09aba2002375a6ae84004d5d118100019bad35742603e0042a666ae68c0740044c8488c00800cc020d5d0980f80100d180f8009baa35742603a0042a666ae68c070004044060c074004dd51aba135744603600460066ae84c068004054dd5000919192999ab9a30190011321223001003375c6ae84c06800854ccd5cd180c00089909118010019bae35742603400402a60340026ea80048488c00800c888cc06888cccd55cf800900911919807198041803980e8009803180e00098021aba2003357420040166eac0048848cc00400c00888cc05c88cccd55cf800900791980518029aba10023003357440040106eb0004c05088448894cd40044008884cc014008ccd54c01c028014010004c04c88448894cd40044d400c040884ccd4014040c010008ccd54c01c024014010004c0488844894cd4004024884cc020c010008cd54c01801c0100044800488488cc00401000cc03c8894cd40080108854cd4cc02000800c01c4cc01400400c4014400888ccd5cd19b8f0020010030051001220021001220011533573892010350543100164901022d31004901013700370e90001b874800955cf2ab9d2323001001223300330020020011").unwrap()))))), + )]; + add_ref_input_babbage(&mtx.transaction_body, &mut utxos, ref_input_info); + let env: Environment = Environment { + prot_params: MultiEraProtParams::Babbage(BabbageProtParams { + fee_policy: FeePolicy { + summand: 155381, + multiplier: 44, + }, + max_tx_size: 16384, + max_block_ex_mem: 62000000, + max_block_ex_steps: 40000000000, + max_tx_ex_mem: 14000000, + max_tx_ex_steps: 10000000000, + max_val_size: 5000, + collateral_percent: 150, + max_collateral_inputs: 3, + coins_per_utxo_word: 4310, + }), + prot_magic: 764824073, + block_slot: 78797255, + network_id: 1, + }; + match validate(&metx, &utxos, &env) { + Ok(()) => (), + Err(err) => assert!(false, "Unexpected error ({:?})", err), + } + } + + #[test] + // Transaction hash: + // 8702b0a5835c16663101f68295e33e3b3868c487f736d3c8a0a4246242675a15 + fn successful_mainnet_tx_with_minting() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/babbage5.tx")); + let mtx: MintedTx = babbage_minted_tx_from_cbor(&cbor_bytes); + let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let tx_outs_info: &[( + String, + Value, + Option, + Option>, + )] = &[ + ( + String::from(include_str!("../../test_data/babbage5.0.address")), + Value::Multiasset( + 2034438, + KeyValuePairs::from(Vec::from([ + ( + "D195CA7DB29F0F13A00CAC7FCA70426FF60BAD4E1E87D3757FAE8484" + .parse() + .unwrap(), + KeyValuePairs::from(Vec::from([( + Bytes::from( + hex::decode("323738333331333737") + .unwrap(), + ), + 1, + )])), + ), + ( + "E4214B7CCE62AC6FBBA385D164DF48E157EAE5863521B4B67CA71D86" + .parse() + .unwrap(), + KeyValuePairs::from(Vec::from([( + Bytes::from( + hex::decode("39B9B709AC8605FC82116A2EFC308181BA297C11950F0F350001E28F0E50868B") + .unwrap(), + ), + 42555569, + )])), + ), + ])), + ), + Some(PseudoDatumOption::Hash( + hex::decode("BB6F798DF7709327DB5BEB6C7A20BA5F170DE1841DDC38F98E192CD36E857B22") + .unwrap() + .as_slice() + .into(), + )), + None, + ), + ( + String::from(include_str!("../../test_data/babbage5.1.address")), + Value::Multiasset( + 197714998, + KeyValuePairs::from(Vec::from([( + "29D222CE763455E3D7A09A665CE554F00AC89D2E99A1A83D267170C6" + .parse() + .unwrap(), + KeyValuePairs::from(Vec::from([( + Bytes::from( + hex::decode("4D494E") + .unwrap(), + ), + 4913396066, + )])), + )])), + ), + None, + None, + ), + ]; + let mut utxos: UTxOs = mk_utxo_for_babbage_tx(&mtx.transaction_body, tx_outs_info); + let collateral_info: &[( + String, + Value, + Option, + Option>, + )] = &[( + String::from(include_str!("../../test_data/babbage5.collateral.address")), + Value::Coin(5000000), + None, + None, + )]; + add_collateral_babbage(&mtx.transaction_body, &mut utxos, collateral_info); + let env: Environment = Environment { + prot_params: MultiEraProtParams::Babbage(BabbageProtParams { + fee_policy: FeePolicy { + summand: 155381, + multiplier: 44, + }, + max_tx_size: 16384, + max_block_ex_mem: 62000000, + max_block_ex_steps: 40000000000, + max_tx_ex_mem: 14000000, + max_tx_ex_steps: 10000000000, + max_val_size: 5000, + collateral_percent: 150, + max_collateral_inputs: 3, + coins_per_utxo_word: 4310, + }), + prot_magic: 764824073, + block_slot: 72316896, + network_id: 1, + }; + match validate(&metx, &utxos, &env) { + Ok(()) => (), + Err(err) => assert!(false, "Unexpected error ({:?})", err), + } + } + + #[test] + // Transaction hash: + // 7ae8cbe887d5d4cdaa51bce93d296206d4fcc77963e65fad3a64d0e6df672260 + fn successful_mainnet_tx_with_metadata() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/babbage6.tx")); + let mtx: MintedTx = babbage_minted_tx_from_cbor(&cbor_bytes); + let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let tx_outs_info: &[( + String, + Value, + Option, + Option>, + )] = &[ + ( + String::from(include_str!("../../test_data/babbage6.0.address")), + Value::Multiasset( + 1689618, + KeyValuePairs::from(Vec::from([( + "dc8f23301b0e3d71af9ac5d1559a060271aa6cf56ac98bdaeea19e18" + .parse() + .unwrap(), + KeyValuePairs::from(Vec::from([( + Bytes::from(hex::decode("303734").unwrap()), + 1, + )])), + )])), + ), + Some(PseudoDatumOption::Hash( + hex::decode("d5b534d58e737861bac5135b5242297b3465c146cc0ddae0bd52547c52305ee7") + .unwrap() + .as_slice() + .into(), + )), + None, + ), + ( + String::from(include_str!("../../test_data/babbage6.1.address")), + Value::Coin(5000000), + None, + None, + ), + ]; + let mut utxos: UTxOs = mk_utxo_for_babbage_tx(&mtx.transaction_body, tx_outs_info); + let collateral_info: &[( + String, + Value, + Option, + Option>, + )] = &[( + String::from(include_str!("../../test_data/babbage6.collateral.address")), + Value::Coin(5000000), + None, + None, + )]; + add_collateral_babbage(&mtx.transaction_body, &mut utxos, collateral_info); + let env: Environment = Environment { + prot_params: MultiEraProtParams::Babbage(BabbageProtParams { + fee_policy: FeePolicy { + summand: 155381, + multiplier: 44, + }, + max_tx_size: 16384, + max_block_ex_mem: 62000000, + max_block_ex_steps: 40000000000, + max_tx_ex_mem: 14000000, + max_tx_ex_steps: 10000000000, + max_val_size: 5000, + collateral_percent: 150, + max_collateral_inputs: 3, + coins_per_utxo_word: 4310, + }), + prot_magic: 764824073, + block_slot: 72316896, + network_id: 1, + }; + match validate(&metx, &utxos, &env) { + Ok(()) => (), + Err(err) => assert!(false, "Unexpected error ({:?})", err), + } + } + + #[test] + // Same as successful_mainnet_tx, except that all inputs are removed. + fn empty_ins() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/babbage3.tx")); + let mut mtx: MintedTx = babbage_minted_tx_from_cbor(&cbor_bytes); + let tx_outs_info: &[( + String, + Value, + Option, + Option>, + )] = &[( + String::from(include_str!("../../test_data/babbage3.address")), + Value::Coin(103324335), + None, + None, + )]; + let utxos: UTxOs = mk_utxo_for_babbage_tx(&mtx.transaction_body, tx_outs_info); + let mut tx_body: MintedTransactionBody = (*mtx.transaction_body).clone(); + tx_body.inputs = Vec::new(); + let mut tx_buf: Vec = Vec::new(); + let _ = encode(tx_body, &mut tx_buf); + mtx.transaction_body = + Decode::decode(&mut Decoder::new(&tx_buf.as_slice()), &mut ()).unwrap(); + let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let env: Environment = Environment { + prot_params: MultiEraProtParams::Babbage(BabbageProtParams { + fee_policy: FeePolicy { + summand: 155381, + multiplier: 44, + }, + max_tx_size: 16384, + max_block_ex_mem: 62000000, + max_block_ex_steps: 40000000000, + max_tx_ex_mem: 14000000, + max_tx_ex_steps: 10000000000, + max_val_size: 5000, + collateral_percent: 150, + max_collateral_inputs: 3, + coins_per_utxo_word: 4310, + }), + prot_magic: 764824073, + block_slot: 72316896, + network_id: 1, + }; + match validate(&metx, &utxos, &env) { + Ok(()) => assert!(false, "Inputs set should not be empty"), + Err(err) => match err { + Babbage(BabbageError::TxInsEmpty) => (), + _ => assert!(false, "Unexpected error ({:?})", err), + }, + } + } + + #[test] + // Same as successful_mainnet_tx, but validation is called with an empty UTxO + // set. + fn unfound_utxo_input() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/babbage3.tx")); + let mtx: MintedTx = babbage_minted_tx_from_cbor(&cbor_bytes); + let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let utxos: UTxOs = UTxOs::new(); + let env: Environment = Environment { + prot_params: MultiEraProtParams::Babbage(BabbageProtParams { + fee_policy: FeePolicy { + summand: 155381, + multiplier: 44, + }, + max_tx_size: 16384, + max_block_ex_mem: 62000000, + max_block_ex_steps: 40000000000, + max_tx_ex_mem: 14000000, + max_tx_ex_steps: 10000000000, + max_val_size: 5000, + collateral_percent: 150, + max_collateral_inputs: 3, + coins_per_utxo_word: 4310, + }), + prot_magic: 764824073, + block_slot: 72316896, + network_id: 1, + }; + match validate(&metx, &utxos, &env) { + Ok(()) => assert!(false, "All inputs should be within the UTxO set"), + Err(err) => match err { + Babbage(BabbageError::InputNotInUTxO) => (), + _ => assert!(false, "Unexpected error ({:?})", err), + }, + } + } + + #[test] + // Same as successful_mainnet_tx, except that the lower bound of the validity + // interval is greater than the block slot. + fn validity_interval_lower_bound_unreached() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/babbage3.tx")); + let mut mtx: MintedTx = babbage_minted_tx_from_cbor(&cbor_bytes); + let tx_outs_info: &[( + String, + Value, + Option, + Option>, + )] = &[( + String::from(include_str!("../../test_data/babbage3.address")), + Value::Coin(103324335), + None, + None, + )]; + let utxos: UTxOs = mk_utxo_for_babbage_tx(&mtx.transaction_body, tx_outs_info); + let mut tx_body: MintedTransactionBody = (*mtx.transaction_body).clone(); + tx_body.validity_interval_start = Some(72316897); // One slot after the block. + let mut tx_buf: Vec = Vec::new(); + let _ = encode(tx_body, &mut tx_buf); + mtx.transaction_body = + Decode::decode(&mut Decoder::new(&tx_buf.as_slice()), &mut ()).unwrap(); + let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let env: Environment = Environment { + prot_params: MultiEraProtParams::Babbage(BabbageProtParams { + fee_policy: FeePolicy { + summand: 155381, + multiplier: 44, + }, + max_tx_size: 16384, + max_block_ex_mem: 62000000, + max_block_ex_steps: 40000000000, + max_tx_ex_mem: 14000000, + max_tx_ex_steps: 10000000000, + max_val_size: 5000, + collateral_percent: 150, + max_collateral_inputs: 3, + coins_per_utxo_word: 4310, + }), + prot_magic: 764824073, + block_slot: 72316896, + network_id: 1, + }; + match validate(&metx, &utxos, &env) { + Ok(()) => assert!( + false, + "Validity interval lower bound should have been reached" + ), + Err(err) => match err { + Babbage(BabbageError::BlockPrecedesValInt) => (), + _ => assert!(false, "Unexpected error ({:?})", err), + }, + } + } + + #[test] + // Same as successful_mainnet_tx, except that the upper bound of the validity + // interval is lower than the block slot. + fn validity_interval_upper_bound_surpassed() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/babbage3.tx")); + let mut mtx: MintedTx = babbage_minted_tx_from_cbor(&cbor_bytes); + let tx_outs_info: &[( + String, + Value, + Option, + Option>, + )] = &[( + String::from(include_str!("../../test_data/babbage3.address")), + Value::Coin(103324335), + None, + None, + )]; + let utxos: UTxOs = mk_utxo_for_babbage_tx(&mtx.transaction_body, tx_outs_info); + let mut tx_body: MintedTransactionBody = (*mtx.transaction_body).clone(); + tx_body.ttl = Some(72316895); // One slot before the block. + let mut tx_buf: Vec = Vec::new(); + let _ = encode(tx_body, &mut tx_buf); + mtx.transaction_body = + Decode::decode(&mut Decoder::new(&tx_buf.as_slice()), &mut ()).unwrap(); + let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let env: Environment = Environment { + prot_params: MultiEraProtParams::Babbage(BabbageProtParams { + fee_policy: FeePolicy { + summand: 155381, + multiplier: 44, + }, + max_tx_size: 16384, + max_block_ex_mem: 62000000, + max_block_ex_steps: 40000000000, + max_tx_ex_mem: 14000000, + max_tx_ex_steps: 10000000000, + max_val_size: 5000, + collateral_percent: 150, + max_collateral_inputs: 3, + coins_per_utxo_word: 4310, + }), + prot_magic: 764824073, + block_slot: 72316896, + network_id: 1, + }; + match validate(&metx, &utxos, &env) { + Ok(()) => assert!( + false, + "Validity interval upper bound should not have been surpassed" + ), + Err(err) => match err { + Babbage(BabbageError::BlockExceedsValInt) => (), + _ => assert!(false, "Unexpected error ({:?})", err), + }, + } + } + + #[test] + // Same as successful_mainnet_tx, except that validation is called with an + // Environment requesting fees that exceed those paid by the transaction. + fn min_fee_unreached() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/babbage3.tx")); + let mtx: MintedTx = babbage_minted_tx_from_cbor(&cbor_bytes); + let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let tx_outs_info: &[( + String, + Value, + Option, + Option>, + )] = &[( + String::from(include_str!("../../test_data/babbage3.address")), + Value::Coin(103324335), + None, + None, + )]; + let utxos: UTxOs = mk_utxo_for_babbage_tx(&mtx.transaction_body, tx_outs_info); + let env: Environment = Environment { + prot_params: MultiEraProtParams::Babbage(BabbageProtParams { + fee_policy: FeePolicy { + summand: 155381, + multiplier: 76, // This value was 44 during Babbage on mainnet. + }, + max_tx_size: 16384, + max_block_ex_mem: 62000000, + max_block_ex_steps: 40000000000, + max_tx_ex_mem: 14000000, + max_tx_ex_steps: 10000000000, + max_val_size: 5000, + collateral_percent: 150, + max_collateral_inputs: 3, + coins_per_utxo_word: 4310, + }), + prot_magic: 764824073, + block_slot: 72316896, + network_id: 1, + }; + match validate(&metx, &utxos, &env) { + Ok(()) => assert!(false, "Fee should not be below minimum"), + Err(err) => match err { + Babbage(BabbageError::FeeBelowMin) => (), + _ => assert!(false, "Unexpected error ({:?})", err), + }, + } + } + + #[test] + // Same as successful_mainnet_tx_with_plutus_v1_script, except that all + // collaterals are removed before calling validation. + fn no_collateral_inputs() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/babbage4.tx")); + let mut mtx: MintedTx = babbage_minted_tx_from_cbor(&cbor_bytes); + let tx_outs_info: &[( + String, + Value, + Option, + Option>, + )] = &[ + ( + String::from(include_str!("../../test_data/babbage4.0.address")), + Value::Coin(25000000), + Some(PseudoDatumOption::Hash( + hex::decode("3E8C4B1D396BB8132E5097F5A2F012D97900CBC496A3745DB4226CEA4CB66465") + .unwrap() + .as_slice() + .into(), + )), + None, + ), + ( + String::from(include_str!("../../test_data/babbage4.1.address")), + Value::Multiasset( + 1795660, + KeyValuePairs::from(Vec::from([( + "787f0c946b98153500edc0a753e65457250544da8486b17c85708135" + .parse() + .unwrap(), + KeyValuePairs::from(Vec::from([( + Bytes::from( + hex::decode("506572666563744c6567656e64617279446572705365616c") + .unwrap(), + ), + 1, + )])), + )])), + ), + None, + None, + ), + ]; + let mut utxos: UTxOs = mk_utxo_for_babbage_tx(&mtx.transaction_body, tx_outs_info); + let collateral_info: &[( + String, + Value, + Option, + Option>, + )] = &[( + String::from(include_str!("../../test_data/babbage4.collateral.address")), + Value::Coin(5000000), + None, + None, + )]; + add_collateral_babbage(&mtx.transaction_body, &mut utxos, collateral_info); + let mut tx_body: MintedTransactionBody = (*mtx.transaction_body).clone(); + tx_body.collateral = None; + let mut tx_buf: Vec = Vec::new(); + let _ = encode(tx_body, &mut tx_buf); + mtx.transaction_body = + Decode::decode(&mut Decoder::new(&tx_buf.as_slice()), &mut ()).unwrap(); + let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let env: Environment = Environment { + prot_params: MultiEraProtParams::Babbage(BabbageProtParams { + fee_policy: FeePolicy { + summand: 155381, + multiplier: 44, + }, + max_tx_size: 16384, + max_block_ex_mem: 62000000, + max_block_ex_steps: 40000000000, + max_tx_ex_mem: 14000000, + max_tx_ex_steps: 10000000000, + max_val_size: 5000, + collateral_percent: 150, + max_collateral_inputs: 3, + coins_per_utxo_word: 4310, + }), + prot_magic: 764824073, + block_slot: 72316896, + network_id: 1, + }; + match validate(&metx, &utxos, &env) { + Ok(()) => assert!(false, "No collateral inputs"), + Err(err) => match err { + Babbage(BabbageError::CollateralMissing) => (), + _ => assert!(false, "Unexpected error ({:?})", err), + }, + } + } + + #[test] + // Same as successful_mainnet_tx_with_plutus_v1_script, except that validation + // is called on an environment which does not allow enough collateral inputs + // for the transaction to be valid. + fn too_many_collateral_inputs() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/babbage4.tx")); + let mtx: MintedTx = babbage_minted_tx_from_cbor(&cbor_bytes); + let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let tx_outs_info: &[( + String, + Value, + Option, + Option>, + )] = &[ + ( + String::from(include_str!("../../test_data/babbage4.0.address")), + Value::Coin(25000000), + Some(PseudoDatumOption::Hash( + hex::decode("3E8C4B1D396BB8132E5097F5A2F012D97900CBC496A3745DB4226CEA4CB66465") + .unwrap() + .as_slice() + .into(), + )), + None, + ), + ( + String::from(include_str!("../../test_data/babbage4.1.address")), + Value::Multiasset( + 1795660, + KeyValuePairs::from(Vec::from([( + "787f0c946b98153500edc0a753e65457250544da8486b17c85708135" + .parse() + .unwrap(), + KeyValuePairs::from(Vec::from([( + Bytes::from( + hex::decode("506572666563744c6567656e64617279446572705365616c") + .unwrap(), + ), + 1, + )])), + )])), + ), + None, + None, + ), + ]; + let mut utxos: UTxOs = mk_utxo_for_babbage_tx(&mtx.transaction_body, tx_outs_info); + let collateral_info: &[( + String, + Value, + Option, + Option>, + )] = &[( + String::from(include_str!("../../test_data/babbage4.collateral.address")), + Value::Coin(5000000), + None, + None, + )]; + add_collateral_babbage(&mtx.transaction_body, &mut utxos, collateral_info); + let env: Environment = Environment { + prot_params: MultiEraProtParams::Babbage(BabbageProtParams { + fee_policy: FeePolicy { + summand: 155381, + multiplier: 44, + }, + max_tx_size: 16384, + max_block_ex_mem: 62000000, + max_block_ex_steps: 40000000000, + max_tx_ex_mem: 14000000, + max_tx_ex_steps: 10000000000, + max_val_size: 5000, + collateral_percent: 150, + max_collateral_inputs: 0, // no collateral inputs are allowed + coins_per_utxo_word: 4310, + }), + prot_magic: 764824073, + block_slot: 72317003, + network_id: 1, + }; + match validate(&metx, &utxos, &env) { + Ok(()) => assert!(false, "Number of collateral inputs should be within limits"), + Err(err) => match err { + Babbage(BabbageError::TooManyCollaterals) => (), + _ => assert!(false, "Unexpected error ({:?})", err), + }, + } + } + + #[test] + // Same as successful_mainnet_tx_with_plutus_v1_script, except that the address + // of a collateral inputs is altered into a script-locked one. + fn collateral_is_not_verification_key_locked() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/babbage4.tx")); + let mtx: MintedTx = babbage_minted_tx_from_cbor(&cbor_bytes); + let tx_outs_info: &[( + String, + Value, + Option, + Option>, + )] = &[ + ( + String::from(include_str!("../../test_data/babbage4.0.address")), + Value::Coin(25000000), + Some(PseudoDatumOption::Hash( + hex::decode("3E8C4B1D396BB8132E5097F5A2F012D97900CBC496A3745DB4226CEA4CB66465") + .unwrap() + .as_slice() + .into(), + )), + None, + ), + ( + String::from(include_str!("../../test_data/babbage4.1.address")), + Value::Multiasset( + 1795660, + KeyValuePairs::from(Vec::from([( + "787f0c946b98153500edc0a753e65457250544da8486b17c85708135" + .parse() + .unwrap(), + KeyValuePairs::from(Vec::from([( + Bytes::from( + hex::decode("506572666563744c6567656e64617279446572705365616c") + .unwrap(), + ), + 1, + )])), + )])), + ), + None, + None, + ), + ]; + let mut utxos: UTxOs = mk_utxo_for_babbage_tx(&mtx.transaction_body, tx_outs_info); + let old_address: Address = match hex::decode(String::from(include_str!( + "../../test_data/babbage4.collateral.address" + ))) { + Ok(bytes_vec) => Address::from_bytes(bytes_vec.as_slice()).unwrap(), + _ => panic!("Unable to parse collateral input address"), + }; + let old_shelley_address: ShelleyAddress = match old_address { + Address::Shelley(shelley_addr) => shelley_addr, + _ => panic!("Unable to parse collateral input address"), + }; + let altered_address: ShelleyAddress = ShelleyAddress::new( + old_shelley_address.network(), + ShelleyPaymentPart::Script(old_shelley_address.payment().as_hash().clone()), + old_shelley_address.delegation().clone(), + ); + let tx_in = mtx + .transaction_body + .collateral + .clone() + .unwrap() + .pop() + .unwrap(); + let multi_era_in: MultiEraInput = + MultiEraInput::AlonzoCompatible(Box::new(Cow::Owned(tx_in.clone()))); + let multi_era_out: MultiEraOutput = MultiEraOutput::Babbage(Box::new(Cow::Owned( + PseudoTransactionOutput::PostAlonzo(MintedPostAlonzoTransactionOutput { + address: Bytes::try_from(altered_address.to_hex()).unwrap(), + value: Value::Coin(5000000), + datum_option: None, + script_ref: None, + }), + ))); + utxos.insert(multi_era_in, multi_era_out); + let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let env: Environment = Environment { + prot_params: MultiEraProtParams::Babbage(BabbageProtParams { + fee_policy: FeePolicy { + summand: 155381, + multiplier: 44, + }, + max_tx_size: 16384, + max_block_ex_mem: 62000000, + max_block_ex_steps: 40000000000, + max_tx_ex_mem: 14000000, + max_tx_ex_steps: 10000000000, + max_val_size: 5000, + collateral_percent: 150, + max_collateral_inputs: 3, + coins_per_utxo_word: 4310, + }), + prot_magic: 764824073, + block_slot: 72316896, + network_id: 1, + }; + match validate(&metx, &utxos, &env) { + Ok(()) => assert!(false, "Collateral inputs should be verification-key locked"), + Err(err) => match err { + Babbage(BabbageError::CollateralNotVKeyLocked) => (), + _ => assert!(false, "Unexpected error ({:?})", err), + }, + } + } + + #[test] + // Same as successful_mainnet_tx_with_plutus_v1_script, except that the balance + // between assets in collateral inputs and assets in collateral return output + // contains assets other than lovelace. + fn collateral_with_other_assets() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/babbage4.tx")); + let mtx: MintedTx = babbage_minted_tx_from_cbor(&cbor_bytes); + let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let tx_outs_info: &[( + String, + Value, + Option, + Option>, + )] = &[ + ( + String::from(include_str!("../../test_data/babbage4.0.address")), + Value::Coin(25000000), + Some(PseudoDatumOption::Hash( + hex::decode("3E8C4B1D396BB8132E5097F5A2F012D97900CBC496A3745DB4226CEA4CB66465") + .unwrap() + .as_slice() + .into(), + )), + None, + ), + ( + String::from(include_str!("../../test_data/babbage4.1.address")), + Value::Multiasset( + 1795660, + KeyValuePairs::from(Vec::from([( + "787f0c946b98153500edc0a753e65457250544da8486b17c85708135" + .parse() + .unwrap(), + KeyValuePairs::from(Vec::from([( + Bytes::from( + hex::decode("506572666563744c6567656e64617279446572705365616c") + .unwrap(), + ), + 1, + )])), + )])), + ), + None, + None, + ), + ]; + let mut utxos: UTxOs = mk_utxo_for_babbage_tx(&mtx.transaction_body, tx_outs_info); + let collateral_info: &[( + String, + Value, + Option, + Option>, + )] = &[( + String::from(include_str!("../../test_data/babbage4.collateral.address")), + Value::Multiasset( + 5000000, + KeyValuePairs::from(Vec::from([( + "b001076b34a87e7d48ec46703a6f50f93289582ad9bdbeff7f1e3295" + .parse() + .unwrap(), + KeyValuePairs::from(Vec::from([( + Bytes::from(hex::decode("4879706562656173747332343233").unwrap()), + 1000, + )])), + )])), + ), + None, + None, + )]; + add_collateral_babbage(&mtx.transaction_body, &mut utxos, collateral_info); + let env: Environment = Environment { + prot_params: MultiEraProtParams::Babbage(BabbageProtParams { + fee_policy: FeePolicy { + summand: 155381, + multiplier: 44, + }, + max_tx_size: 16384, + max_block_ex_mem: 62000000, + max_block_ex_steps: 40000000000, + max_tx_ex_mem: 14000000, + max_tx_ex_steps: 10000000000, + max_val_size: 5000, + collateral_percent: 150, + max_collateral_inputs: 3, + coins_per_utxo_word: 4310, + }), + prot_magic: 764824073, + block_slot: 72317003, + network_id: 1, + }; + match validate(&metx, &utxos, &env) { + Ok(()) => assert!(false, "Collateral balance should contained only lovelace"), + Err(err) => match err { + Babbage(BabbageError::NonLovelaceCollateral) => (), + _ => assert!(false, "Unexpected error ({:?})", err), + }, + } + } + + #[test] + // Same as successful_mainnet_tx_with_plutus_v1_script, except that the number + // of lovelace in the total collateral balance is insufficient. + fn collateral_min_lovelace() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/babbage4.tx")); + let mtx: MintedTx = babbage_minted_tx_from_cbor(&cbor_bytes); + let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let tx_outs_info: &[( + String, + Value, + Option, + Option>, + )] = &[ + ( + String::from(include_str!("../../test_data/babbage4.0.address")), + Value::Coin(25000000), + Some(PseudoDatumOption::Hash( + hex::decode("3E8C4B1D396BB8132E5097F5A2F012D97900CBC496A3745DB4226CEA4CB66465") + .unwrap() + .as_slice() + .into(), + )), + None, + ), + ( + String::from(include_str!("../../test_data/babbage4.1.address")), + Value::Multiasset( + 1795660, + KeyValuePairs::from(Vec::from([( + "787f0c946b98153500edc0a753e65457250544da8486b17c85708135" + .parse() + .unwrap(), + KeyValuePairs::from(Vec::from([( + Bytes::from( + hex::decode("506572666563744c6567656e64617279446572705365616c") + .unwrap(), + ), + 1, + )])), + )])), + ), + None, + None, + ), + ]; + let mut utxos: UTxOs = mk_utxo_for_babbage_tx(&mtx.transaction_body, tx_outs_info); + let collateral_info: &[( + String, + Value, + Option, + Option>, + )] = &[( + String::from(include_str!("../../test_data/babbage4.collateral.address")), + Value::Coin(5000000), + None, + None, + )]; + add_collateral_babbage(&mtx.transaction_body, &mut utxos, collateral_info); + let env: Environment = Environment { + prot_params: MultiEraProtParams::Babbage(BabbageProtParams { + fee_policy: FeePolicy { + summand: 155381, + multiplier: 44, + }, + max_tx_size: 16384, + max_block_ex_mem: 62000000, + max_block_ex_steps: 40000000000, + max_tx_ex_mem: 14000000, + max_tx_ex_steps: 10000000000, + max_val_size: 5000, + collateral_percent: 728, // This value was 150 during Babbage on mainnet. + max_collateral_inputs: 3, + coins_per_utxo_word: 4310, + }), + prot_magic: 764824073, + block_slot: 72317003, + network_id: 1, + }; + match validate(&metx, &utxos, &env) { + Ok(()) => assert!( + false, + "Collateral balance should contained the minimum lovelace" + ), + Err(err) => match err { + Babbage(BabbageError::CollateralMinLovelace) => (), + _ => assert!(false, "Unexpected error ({:?})", err), + }, + } + } + + #[test] + // Same as successful_mainnet_tx_with_plutus_v1_script, except that the + // annotated collateral is wrong. + fn collateral_annotation() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/babbage4.tx")); + let mut mtx: MintedTx = babbage_minted_tx_from_cbor(&cbor_bytes); + let tx_outs_info: &[( + String, + Value, + Option, + Option>, + )] = &[ + ( + String::from(include_str!("../../test_data/babbage4.0.address")), + Value::Coin(25000000), + Some(PseudoDatumOption::Hash( + hex::decode("3E8C4B1D396BB8132E5097F5A2F012D97900CBC496A3745DB4226CEA4CB66465") + .unwrap() + .as_slice() + .into(), + )), + None, + ), + ( + String::from(include_str!("../../test_data/babbage4.1.address")), + Value::Multiasset( + 1795660, + KeyValuePairs::from(Vec::from([( + "787f0c946b98153500edc0a753e65457250544da8486b17c85708135" + .parse() + .unwrap(), + KeyValuePairs::from(Vec::from([( + Bytes::from( + hex::decode("506572666563744c6567656e64617279446572705365616c") + .unwrap(), + ), + 1, + )])), + )])), + ), + None, + None, + ), + ]; + let mut utxos: UTxOs = mk_utxo_for_babbage_tx(&mtx.transaction_body, tx_outs_info); + let collateral_info: &[( + String, + Value, + Option, + Option>, + )] = &[( + String::from(include_str!("../../test_data/babbage4.collateral.address")), + Value::Coin(5000000), + None, + None, + )]; + add_collateral_babbage(&mtx.transaction_body, &mut utxos, collateral_info); + let mut tx_body: MintedTransactionBody = (*mtx.transaction_body).clone(); + tx_body.total_collateral = Some(5000001); // This is 1 more than the actual paid collateral + let mut tx_buf: Vec = Vec::new(); + let _ = encode(tx_body, &mut tx_buf); + mtx.transaction_body = + Decode::decode(&mut Decoder::new(&tx_buf.as_slice()), &mut ()).unwrap(); + let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let env: Environment = Environment { + prot_params: MultiEraProtParams::Babbage(BabbageProtParams { + fee_policy: FeePolicy { + summand: 155381, + multiplier: 44, + }, + max_tx_size: 16384, + max_block_ex_mem: 62000000, + max_block_ex_steps: 40000000000, + max_tx_ex_mem: 14000000, + max_tx_ex_steps: 10000000000, + max_val_size: 5000, + collateral_percent: 150, + max_collateral_inputs: 3, + coins_per_utxo_word: 4310, + }), + prot_magic: 764824073, + block_slot: 72317003, + network_id: 1, + }; + match validate(&metx, &utxos, &env) { + Ok(()) => assert!(false, "Collateral annotation"), + Err(err) => match err { + Babbage(BabbageError::CollateralAnnotation) => (), + _ => assert!(false, "Unexpected error ({:?})", err), + }, + } + } + + #[test] + // Same as successful_mainnet_tx, except that the fee is reduced by exactly 1, + // and so the "preservation of value" property doesn't hold. + fn preservation_of_value() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/babbage3.tx")); + let mut mtx: MintedTx = babbage_minted_tx_from_cbor(&cbor_bytes); + let tx_outs_info: &[( + String, + Value, + Option, + Option>, + )] = &[( + String::from(include_str!("../../test_data/babbage3.address")), + Value::Coin(103324335), + None, + None, + )]; + let utxos: UTxOs = mk_utxo_for_babbage_tx(&mtx.transaction_body, tx_outs_info); + let mut tx_body: MintedTransactionBody = (*mtx.transaction_body).clone(); + tx_body.fee = tx_body.fee - 1; + let mut tx_buf: Vec = Vec::new(); + let _ = encode(tx_body, &mut tx_buf); + mtx.transaction_body = + Decode::decode(&mut Decoder::new(&tx_buf.as_slice()), &mut ()).unwrap(); + let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let env: Environment = Environment { + prot_params: MultiEraProtParams::Babbage(BabbageProtParams { + fee_policy: FeePolicy { + summand: 155381, + multiplier: 44, + }, + max_tx_size: 16384, + max_block_ex_mem: 62000000, + max_block_ex_steps: 40000000000, + max_tx_ex_mem: 14000000, + max_tx_ex_steps: 10000000000, + max_val_size: 5000, + collateral_percent: 150, + max_collateral_inputs: 3, + coins_per_utxo_word: 4310, + }), + prot_magic: 764824073, + block_slot: 72316896, + network_id: 1, + }; + match validate(&metx, &utxos, &env) { + Ok(()) => assert!(false, "Preservation of value does not hold"), + Err(err) => match err { + Babbage(BabbageError::PreservationOfValue) => (), + _ => assert!(false, "Unexpected error ({:?})", err), + }, + } + } + + #[test] + // Same as successful_mainnet_tx, except that the minimum lovelace in an output + // is unreached. + fn min_lovelace_unreached() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/babbage3.tx")); + let mtx: MintedTx = babbage_minted_tx_from_cbor(&cbor_bytes); + let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let tx_outs_info: &[( + String, + Value, + Option, + Option>, + )] = &[( + String::from(include_str!("../../test_data/babbage3.address")), + Value::Coin(103324335), + None, + None, + )]; + let utxos: UTxOs = mk_utxo_for_babbage_tx(&mtx.transaction_body, tx_outs_info); + let env: Environment = Environment { + prot_params: MultiEraProtParams::Babbage(BabbageProtParams { + fee_policy: FeePolicy { + summand: 155381, + multiplier: 44, + }, + max_tx_size: 16384, + max_block_ex_mem: 62000000, + max_block_ex_steps: 40000000000, + max_tx_ex_mem: 14000000, + max_tx_ex_steps: 10000000000, + max_val_size: 5000, + collateral_percent: 150, + max_collateral_inputs: 3, + coins_per_utxo_word: 10000000, // This was 4310 during Alonzo on mainnet. + }), + prot_magic: 764824073, + block_slot: 72316896, + network_id: 1, + }; + match validate(&metx, &utxos, &env) { + Ok(()) => assert!(false, "Output minimum lovelace is unreached"), + Err(err) => match err { + Babbage(BabbageError::MinLovelaceUnreached) => (), + _ => assert!(false, "Unexpected error ({:?})", err), + }, + } + } + + #[test] + // Same as successful_mainnet_tx, except that the value size exceeds the + // environment parameter. + fn max_val_exceeded() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/babbage3.tx")); + let mtx: MintedTx = babbage_minted_tx_from_cbor(&cbor_bytes); + let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let tx_outs_info: &[( + String, + Value, + Option, + Option>, + )] = &[( + String::from(include_str!("../../test_data/babbage3.address")), + Value::Coin(103324335), + None, + None, + )]; + let utxos: UTxOs = mk_utxo_for_babbage_tx(&mtx.transaction_body, tx_outs_info); + let env: Environment = Environment { + prot_params: MultiEraProtParams::Babbage(BabbageProtParams { + fee_policy: FeePolicy { + summand: 155381, + multiplier: 44, + }, + max_tx_size: 16384, + max_block_ex_mem: 62000000, + max_block_ex_steps: 40000000000, + max_tx_ex_mem: 14000000, + max_tx_ex_steps: 10000000000, + max_val_size: 0, + collateral_percent: 150, + max_collateral_inputs: 3, + coins_per_utxo_word: 4310, + }), + prot_magic: 764824073, + block_slot: 72316896, + network_id: 1, + }; + match validate(&metx, &utxos, &env) { + Ok(()) => assert!(false, "Max value size exceeded"), + Err(err) => match err { + Babbage(BabbageError::MaxValSizeExceeded) => (), + _ => assert!(false, "Unexpected error ({:?})", err), + }, + } + } + + #[test] + // Same as successful_mainnet_tx, except that the first output's transaction + // network ID is altered. + fn output_network_id() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/babbage3.tx")); + let mut mtx: MintedTx = babbage_minted_tx_from_cbor(&cbor_bytes); + let tx_outs_info: &[( + String, + Value, + Option, + Option>, + )] = &[( + String::from(include_str!("../../test_data/babbage3.address")), + Value::Coin(103324335), + None, + None, + )]; + let utxos: UTxOs = mk_utxo_for_babbage_tx(&mtx.transaction_body, tx_outs_info); + let mut tx_body: MintedTransactionBody = (*mtx.transaction_body).clone(); + let (first_output, rest): (&MintedTransactionOutput, &[MintedTransactionOutput]) = + (&tx_body.outputs).split_first().unwrap(); + let (address_bytes, val): (Bytes, Value) = match first_output { + PseudoTransactionOutput::Legacy(output) => { + (output.address.clone(), output.amount.clone()) + } + PseudoTransactionOutput::PostAlonzo(output) => { + (output.address.clone(), output.value.clone()) + } + }; + let address: ShelleyAddress = match Address::from_bytes(&address_bytes) { + Ok(Address::Shelley(sa)) => sa, + _ => panic!("Decoded output address and found the wrong era"), + }; + let altered_address: ShelleyAddress = ShelleyAddress::new( + Network::Testnet, + address.payment().clone(), + address.delegation().clone(), + ); + let altered_output: MintedTransactionOutput = + PseudoTransactionOutput::PostAlonzo(MintedPostAlonzoTransactionOutput { + address: Bytes::from(altered_address.to_vec()), + value: val, + datum_option: None, + script_ref: None, + }); + let mut new_outputs = Vec::from(rest); + new_outputs.insert(0, altered_output); + tx_body.outputs = new_outputs; + let mut tx_buf: Vec = Vec::new(); + let _ = encode(tx_body, &mut tx_buf); + mtx.transaction_body = + Decode::decode(&mut Decoder::new(&tx_buf.as_slice()), &mut ()).unwrap(); + let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let env: Environment = Environment { + prot_params: MultiEraProtParams::Babbage(BabbageProtParams { + fee_policy: FeePolicy { + summand: 155381, + multiplier: 44, + }, + max_tx_size: 16384, + max_block_ex_mem: 62000000, + max_block_ex_steps: 40000000000, + max_tx_ex_mem: 14000000, + max_tx_ex_steps: 10000000000, + max_val_size: 5000, + collateral_percent: 150, + max_collateral_inputs: 3, + coins_per_utxo_word: 4310, + }), + prot_magic: 764824073, + block_slot: 72316896, + network_id: 1, + }; + match validate(&metx, &utxos, &env) { + Ok(()) => assert!( + false, + "Output network ID should match environment network ID" + ), + Err(err) => match err { + Babbage(BabbageError::OutputWrongNetworkID) => (), + _ => assert!(false, "Unexpected error ({:?})", err), + }, + } + } + + #[test] + // Same as successful_mainnet_tx, except that the transaction's network ID is + // altered. + fn tx_network_id() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/babbage3.tx")); + let mut mtx: MintedTx = babbage_minted_tx_from_cbor(&cbor_bytes); + let tx_outs_info: &[( + String, + Value, + Option, + Option>, + )] = &[( + String::from(include_str!("../../test_data/babbage3.address")), + Value::Coin(103324335), + None, + None, + )]; + let utxos: UTxOs = mk_utxo_for_babbage_tx(&mtx.transaction_body, tx_outs_info); + let mut tx_body: MintedTransactionBody = (*mtx.transaction_body).clone(); + tx_body.network_id = Some(NetworkId::Two); + let mut tx_buf: Vec = Vec::new(); + let _ = encode(tx_body, &mut tx_buf); + mtx.transaction_body = + Decode::decode(&mut Decoder::new(&tx_buf.as_slice()), &mut ()).unwrap(); + let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let env: Environment = Environment { + prot_params: MultiEraProtParams::Babbage(BabbageProtParams { + fee_policy: FeePolicy { + summand: 155381, + multiplier: 44, + }, + max_tx_size: 16384, + max_block_ex_mem: 62000000, + max_block_ex_steps: 40000000000, + max_tx_ex_mem: 14000000, + max_tx_ex_steps: 10000000000, + max_val_size: 5000, + collateral_percent: 150, + max_collateral_inputs: 3, + coins_per_utxo_word: 4310, + }), + prot_magic: 764824073, + block_slot: 72316896, + network_id: 1, + }; + match validate(&metx, &utxos, &env) { + Ok(()) => assert!( + false, + "Transaction network ID should match environment network ID" + ), + Err(err) => match err { + Babbage(BabbageError::TxWrongNetworkID) => (), + _ => assert!(false, "Unexpected error ({:?})", err), + }, + } + } + + #[test] + // Same as successful_mainnet_tx_with_plutus_v1_script, except that the + // Environment execution values are below the ones associated with the + // transaction. + fn tx_ex_units_exceeded() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/babbage4.tx")); + let mtx: MintedTx = babbage_minted_tx_from_cbor(&cbor_bytes); + let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let tx_outs_info: &[( + String, + Value, + Option, + Option>, + )] = &[ + ( + String::from(include_str!("../../test_data/babbage4.0.address")), + Value::Coin(25000000), + Some(PseudoDatumOption::Hash( + hex::decode("3E8C4B1D396BB8132E5097F5A2F012D97900CBC496A3745DB4226CEA4CB66465") + .unwrap() + .as_slice() + .into(), + )), + None, + ), + ( + String::from(include_str!("../../test_data/babbage4.1.address")), + Value::Multiasset( + 1795660, + KeyValuePairs::from(Vec::from([( + "787f0c946b98153500edc0a753e65457250544da8486b17c85708135" + .parse() + .unwrap(), + KeyValuePairs::from(Vec::from([( + Bytes::from( + hex::decode("506572666563744c6567656e64617279446572705365616c") + .unwrap(), + ), + 1, + )])), + )])), + ), + None, + None, + ), + ]; + let mut utxos: UTxOs = mk_utxo_for_babbage_tx(&mtx.transaction_body, tx_outs_info); + let collateral_info: &[( + String, + Value, + Option, + Option>, + )] = &[( + String::from(include_str!("../../test_data/babbage4.collateral.address")), + Value::Coin(5000000), + None, + None, + )]; + add_collateral_babbage(&mtx.transaction_body, &mut utxos, collateral_info); + let env: Environment = Environment { + prot_params: MultiEraProtParams::Babbage(BabbageProtParams { + fee_policy: FeePolicy { + summand: 155381, + multiplier: 44, + }, + max_tx_size: 16384, + max_block_ex_mem: 62000000, + max_block_ex_steps: 40000000000, + max_tx_ex_mem: 3678343, // 1 lower than that of the transaction + max_tx_ex_steps: 1304942838, // 1 lower than that of the transaction + max_val_size: 5000, + collateral_percent: 150, + max_collateral_inputs: 3, + coins_per_utxo_word: 4310, + }), + prot_magic: 764824073, + block_slot: 72317003, + network_id: 1, + }; + match validate(&metx, &utxos, &env) { + Ok(()) => assert!(false, "Transaction ex units should be below maximum"), + Err(err) => match err { + Babbage(BabbageError::TxExUnitsExceeded) => (), + _ => assert!(false, "Unexpected error ({:?})", err), + }, + } + } + + #[test] + // Same as successful_mainnet_tx, except that the Environment with which + // validation is called demands the transaction to be smaller than it + // actually is. + fn max_tx_size_exceeded() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/babbage3.tx")); + let mtx: MintedTx = babbage_minted_tx_from_cbor(&cbor_bytes); + let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let tx_outs_info: &[( + String, + Value, + Option, + Option>, + )] = &[( + String::from(include_str!("../../test_data/babbage3.address")), + Value::Coin(103324335), + None, + None, + )]; + let utxos: UTxOs = mk_utxo_for_babbage_tx(&mtx.transaction_body, tx_outs_info); + let env: Environment = Environment { + prot_params: MultiEraProtParams::Babbage(BabbageProtParams { + fee_policy: FeePolicy { + summand: 155381, + multiplier: 44, + }, + max_tx_size: 154, // 1 less than the size of the transaction. + max_block_ex_mem: 62000000, + max_block_ex_steps: 40000000000, + max_tx_ex_mem: 14000000, + max_tx_ex_steps: 10000000000, + max_val_size: 5000, + collateral_percent: 150, + max_collateral_inputs: 3, + coins_per_utxo_word: 4310, + }), + prot_magic: 764824073, + block_slot: 72316896, + network_id: 1, + }; + match validate(&metx, &utxos, &env) { + Ok(()) => assert!( + false, + "Transaction size should not exceed the maximum allowed" + ), + Err(err) => match err { + Babbage(BabbageError::MaxTxSizeExceeded) => (), + _ => assert!(false, "Unexpected error ({:?})", err), + }, + } + } + + #[test] + // Same as successful_mainnet_tx_with_minting, except that minting is not + // supported by the corresponding native script. + fn minting_lacks_policy() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/babbage5.tx")); + let mut mtx: MintedTx = babbage_minted_tx_from_cbor(&cbor_bytes); + let tx_outs_info: &[( + String, + Value, + Option, + Option>, + )] = &[ + ( + String::from(include_str!("../../test_data/babbage5.0.address")), + Value::Multiasset( + 2034438, + KeyValuePairs::from(Vec::from([ + ( + "D195CA7DB29F0F13A00CAC7FCA70426FF60BAD4E1E87D3757FAE8484" + .parse() + .unwrap(), + KeyValuePairs::from(Vec::from([( + Bytes::from( + hex::decode("323738333331333737") + .unwrap(), + ), + 1, + )])), + ), + ( + "E4214B7CCE62AC6FBBA385D164DF48E157EAE5863521B4B67CA71D86" + .parse() + .unwrap(), + KeyValuePairs::from(Vec::from([( + Bytes::from( + hex::decode("39B9B709AC8605FC82116A2EFC308181BA297C11950F0F350001E28F0E50868B") + .unwrap(), + ), + 42555569, + )])), + ), + ])), + ), + Some(PseudoDatumOption::Hash( + hex::decode("BB6F798DF7709327DB5BEB6C7A20BA5F170DE1841DDC38F98E192CD36E857B22") + .unwrap() + .as_slice() + .into(), + )), + None, + ), + ( + String::from(include_str!("../../test_data/babbage5.1.address")), + Value::Multiasset( + 197714998, + KeyValuePairs::from(Vec::from([( + "29D222CE763455E3D7A09A665CE554F00AC89D2E99A1A83D267170C6" + .parse() + .unwrap(), + KeyValuePairs::from(Vec::from([( + Bytes::from( + hex::decode("4D494E") + .unwrap(), + ), + 4913396066, + )])), + )])), + ), + None, + None, + ), + ]; + let mut utxos: UTxOs = mk_utxo_for_babbage_tx(&mtx.transaction_body, tx_outs_info); + let collateral_info: &[( + String, + Value, + Option, + Option>, + )] = &[( + String::from(include_str!("../../test_data/babbage5.collateral.address")), + Value::Coin(5000000), + None, + None, + )]; + add_collateral_babbage(&mtx.transaction_body, &mut utxos, collateral_info); + let mut tx_wits: MintedWitnessSet = mtx.transaction_witness_set.unwrap().clone(); + tx_wits.native_script = Some(Vec::new()); + let mut tx_buf: Vec = Vec::new(); + let _ = encode(tx_wits, &mut tx_buf); + mtx.transaction_witness_set = + Decode::decode(&mut Decoder::new(&tx_buf.as_slice()), &mut ()).unwrap(); + let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let env: Environment = Environment { + prot_params: MultiEraProtParams::Babbage(BabbageProtParams { + fee_policy: FeePolicy { + summand: 155381, + multiplier: 44, + }, + max_tx_size: 16384, + max_block_ex_mem: 62000000, + max_block_ex_steps: 40000000000, + max_tx_ex_mem: 14000000, + max_tx_ex_steps: 10000000000, + max_val_size: 5000, + collateral_percent: 150, + max_collateral_inputs: 3, + coins_per_utxo_word: 4310, + }), + prot_magic: 764824073, + block_slot: 72316896, + network_id: 1, + }; + match validate(&metx, &utxos, &env) { + Ok(()) => assert!( + false, + "Minting policy is not supported by the corresponding native script" + ), + Err(err) => match err { + Babbage(BabbageError::MintingLacksPolicy) => (), + _ => assert!(false, "Unexpected error ({:?})", err), + }, + } + } + + #[test] + // Same as successful_mainnet_tx_with_metadata, except that the AuxiliaryData is + // removed. + fn auxiliary_data_removed() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/babbage6.tx")); + let mut mtx: MintedTx = babbage_minted_tx_from_cbor(&cbor_bytes); + mtx.auxiliary_data = Nullable::Null; + let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let tx_outs_info: &[( + String, + Value, + Option, + Option>, + )] = &[ + ( + String::from(include_str!("../../test_data/babbage6.0.address")), + Value::Multiasset( + 1689618, + KeyValuePairs::from(Vec::from([( + "dc8f23301b0e3d71af9ac5d1559a060271aa6cf56ac98bdaeea19e18" + .parse() + .unwrap(), + KeyValuePairs::from(Vec::from([( + Bytes::from(hex::decode("303734").unwrap()), + 1, + )])), + )])), + ), + Some(PseudoDatumOption::Hash( + hex::decode("d5b534d58e737861bac5135b5242297b3465c146cc0ddae0bd52547c52305ee7") + .unwrap() + .as_slice() + .into(), + )), + None, + ), + ( + String::from(include_str!("../../test_data/babbage6.1.address")), + Value::Coin(5000000), + None, + None, + ), + ]; + let mut utxos: UTxOs = mk_utxo_for_babbage_tx(&mtx.transaction_body, tx_outs_info); + let collateral_info: &[( + String, + Value, + Option, + Option>, + )] = &[( + String::from(include_str!("../../test_data/babbage6.collateral.address")), + Value::Coin(5000000), + None, + None, + )]; + add_collateral_babbage(&mtx.transaction_body, &mut utxos, collateral_info); + let env: Environment = Environment { + prot_params: MultiEraProtParams::Babbage(BabbageProtParams { + fee_policy: FeePolicy { + summand: 155381, + multiplier: 44, + }, + max_tx_size: 16384, + max_block_ex_mem: 62000000, + max_block_ex_steps: 40000000000, + max_tx_ex_mem: 14000000, + max_tx_ex_steps: 10000000000, + max_val_size: 5000, + collateral_percent: 150, + max_collateral_inputs: 3, + coins_per_utxo_word: 4310, + }), + prot_magic: 764824073, + block_slot: 72316896, + network_id: 1, + }; + match validate(&metx, &utxos, &env) { + Ok(()) => assert!(false, "Transaction auxiliary data removed"), + Err(err) => match err { + Babbage(BabbageError::MetadataHash) => (), + _ => assert!(false, "Unexpected error ({:?})", err), + }, + } + } + + #[test] + // Same as sucessful_mainnet_tx_with_plutus_v1_script, except that the script + // hash in the script UTxO cannot be matched to a script in the witness set. + fn script_input_lacks_script() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/babbage4.tx")); + let mut mtx: MintedTx = babbage_minted_tx_from_cbor(&cbor_bytes); + let tx_outs_info: &[( + String, + Value, + Option, + Option>, + )] = &[ + ( + String::from(include_str!("../../test_data/babbage4.0.address")), + Value::Coin(25000000), + Some(PseudoDatumOption::Hash( + hex::decode("3e8c4b1d396bb8132e5097f5a2f012d97900cbc496a3745db4226cea4cb66465") + .unwrap() + .as_slice() + .into(), + )), + None, + ), + ( + String::from(include_str!("../../test_data/babbage4.1.address")), + Value::Multiasset( + 1795660, + KeyValuePairs::from(Vec::from([( + "787f0c946b98153500edc0a753e65457250544da8486b17c85708135" + .parse() + .unwrap(), + KeyValuePairs::from(Vec::from([( + Bytes::from( + hex::decode("506572666563744c6567656e64617279446572705365616c") + .unwrap(), + ), + 1, + )])), + )])), + ), + None, + None, + ), + ]; + let mut utxos: UTxOs = mk_utxo_for_babbage_tx(&mtx.transaction_body, tx_outs_info); + let collateral_info: &[( + String, + Value, + Option, + Option>, + )] = &[( + String::from(include_str!("../../test_data/babbage4.collateral.address")), + Value::Coin(5000000), + None, + None, + )]; + add_collateral_babbage(&mtx.transaction_body, &mut utxos, collateral_info); + let mut tx_wits: MintedWitnessSet = mtx.transaction_witness_set.unwrap().clone(); + tx_wits.plutus_v1_script = Some(Vec::new()); + let mut tx_buf: Vec = Vec::new(); + let _ = encode(tx_wits, &mut tx_buf); + mtx.transaction_witness_set = + Decode::decode(&mut Decoder::new(&tx_buf.as_slice()), &mut ()).unwrap(); + let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let env: Environment = Environment { + prot_params: MultiEraProtParams::Babbage(BabbageProtParams { + fee_policy: FeePolicy { + summand: 155381, + multiplier: 44, + }, + max_tx_size: 16384, + max_block_ex_mem: 62000000, + max_block_ex_steps: 40000000000, + max_tx_ex_mem: 14000000, + max_tx_ex_steps: 10000000000, + max_val_size: 5000, + collateral_percent: 150, + max_collateral_inputs: 3, + coins_per_utxo_word: 4310, + }), + prot_magic: 764824073, + block_slot: 72317003, + network_id: 1, + }; + match validate(&metx, &utxos, &env) { + Ok(()) => assert!( + false, + "Script hash in input is not matched to a script in the witness set" + ), + Err(err) => match err { + Babbage(BabbageError::ScriptWitnessMissing) => (), + _ => assert!(false, "Unexpected error ({:?})", err), + }, + } + } + + #[test] + // Same as successful_mainnet_tx_with_plutus_v1_script, except that the datum of + // the input script UTxO is removed from the MintedWitnessSet. + fn missing_input_datum() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/babbage4.tx")); + let mut mtx: MintedTx = babbage_minted_tx_from_cbor(&cbor_bytes); + let tx_outs_info: &[( + String, + Value, + Option, + Option>, + )] = &[ + ( + String::from(include_str!("../../test_data/babbage4.0.address")), + Value::Coin(25000000), + Some(PseudoDatumOption::Hash( + hex::decode("3e8c4b1d396bb8132e5097f5a2f012d97900cbc496a3745db4226cea4cb66465") + .unwrap() + .as_slice() + .into(), + )), + None, + ), + ( + String::from(include_str!("../../test_data/babbage4.1.address")), + Value::Multiasset( + 1795660, + KeyValuePairs::from(Vec::from([( + "787f0c946b98153500edc0a753e65457250544da8486b17c85708135" + .parse() + .unwrap(), + KeyValuePairs::from(Vec::from([( + Bytes::from( + hex::decode("506572666563744c6567656e64617279446572705365616c") + .unwrap(), + ), + 1, + )])), + )])), + ), + None, + None, + ), + ]; + let mut utxos: UTxOs = mk_utxo_for_babbage_tx(&mtx.transaction_body, tx_outs_info); + let collateral_info: &[( + String, + Value, + Option, + Option>, + )] = &[( + String::from(include_str!("../../test_data/babbage4.collateral.address")), + Value::Coin(5000000), + None, + None, + )]; + add_collateral_babbage(&mtx.transaction_body, &mut utxos, collateral_info); + let mut tx_wits: MintedWitnessSet = mtx.transaction_witness_set.unwrap().clone(); + tx_wits.plutus_data = Some(Vec::new()); + let mut tx_buf: Vec = Vec::new(); + let _ = encode(tx_wits, &mut tx_buf); + mtx.transaction_witness_set = + Decode::decode(&mut Decoder::new(&tx_buf.as_slice()), &mut ()).unwrap(); + let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let env: Environment = Environment { + prot_params: MultiEraProtParams::Babbage(BabbageProtParams { + fee_policy: FeePolicy { + summand: 155381, + multiplier: 44, + }, + max_tx_size: 16384, + max_block_ex_mem: 62000000, + max_block_ex_steps: 40000000000, + max_tx_ex_mem: 14000000, + max_tx_ex_steps: 10000000000, + max_val_size: 5000, + collateral_percent: 150, + max_collateral_inputs: 3, + coins_per_utxo_word: 4310, + }), + prot_magic: 764824073, + block_slot: 72317003, + network_id: 1, + }; + match validate(&metx, &utxos, &env) { + Ok(()) => assert!( + false, + "Datum matching the script input datum hash is missing" + ), + Err(err) => match err { + Babbage(BabbageError::DatumMissing) => (), + _ => assert!(false, "Unexpected error ({:?})", err), + }, + } + } + + #[test] + // Same as successful_mainnet_tx_with_plutus_v1_script, except that the list of + // PlutusData is extended with an unnecessary new element. + fn extra_input_datum() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/babbage4.tx")); + let mut mtx: MintedTx = babbage_minted_tx_from_cbor(&cbor_bytes); + let tx_outs_info: &[( + String, + Value, + Option, + Option>, + )] = &[ + ( + String::from(include_str!("../../test_data/babbage4.0.address")), + Value::Coin(25000000), + Some(PseudoDatumOption::Hash( + hex::decode("3e8c4b1d396bb8132e5097f5a2f012d97900cbc496a3745db4226cea4cb66465") + .unwrap() + .as_slice() + .into(), + )), + None, + ), + ( + String::from(include_str!("../../test_data/babbage4.1.address")), + Value::Multiasset( + 1795660, + KeyValuePairs::from(Vec::from([( + "787f0c946b98153500edc0a753e65457250544da8486b17c85708135" + .parse() + .unwrap(), + KeyValuePairs::from(Vec::from([( + Bytes::from( + hex::decode("506572666563744c6567656e64617279446572705365616c") + .unwrap(), + ), + 1, + )])), + )])), + ), + None, + None, + ), + ]; + let mut utxos: UTxOs = mk_utxo_for_babbage_tx(&mtx.transaction_body, tx_outs_info); + let collateral_info: &[( + String, + Value, + Option, + Option>, + )] = &[( + String::from(include_str!("../../test_data/babbage4.collateral.address")), + Value::Coin(5000000), + None, + None, + )]; + add_collateral_babbage(&mtx.transaction_body, &mut utxos, collateral_info); + let mut tx_wits: MintedWitnessSet = mtx.transaction_witness_set.unwrap().clone(); + let old_datum: KeepRaw = tx_wits.plutus_data.unwrap().pop().unwrap(); + let new_datum: PlutusData = PlutusData::Array(Vec::new()); + let mut new_datum_buf: Vec = Vec::new(); + let _ = encode(new_datum, &mut new_datum_buf); + let keep_raw_new_datum: KeepRaw = + Decode::decode(&mut Decoder::new(&new_datum_buf.as_slice()), &mut ()).unwrap(); + tx_wits.plutus_data = Some(vec![old_datum, keep_raw_new_datum]); + let mut tx_buf: Vec = Vec::new(); + let _ = encode(tx_wits, &mut tx_buf); + mtx.transaction_witness_set = + Decode::decode(&mut Decoder::new(&tx_buf.as_slice()), &mut ()).unwrap(); + let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let env: Environment = Environment { + prot_params: MultiEraProtParams::Babbage(BabbageProtParams { + fee_policy: FeePolicy { + summand: 155381, + multiplier: 44, + }, + max_tx_size: 16384, + max_block_ex_mem: 62000000, + max_block_ex_steps: 40000000000, + max_tx_ex_mem: 14000000, + max_tx_ex_steps: 10000000000, + max_val_size: 5000, + collateral_percent: 150, + max_collateral_inputs: 3, + coins_per_utxo_word: 4310, + }), + prot_magic: 764824073, + block_slot: 72317003, + network_id: 1, + }; + match validate(&metx, &utxos, &env) { + Ok(()) => assert!(false, "Unneeded datum"), + Err(err) => match err { + Babbage(BabbageError::UnneededDatum) => (), + _ => assert!(false, "Unexpected error ({:?})", err), + }, + } + } + + #[test] + // Same as successful_mainnet_tx_with_plutus_v1_script, except that the list of + // Redeemers is extended with an unnecessary new element. + fn extra_redeemer() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/babbage4.tx")); + let mut mtx: MintedTx = babbage_minted_tx_from_cbor(&cbor_bytes); + let tx_outs_info: &[( + String, + Value, + Option, + Option>, + )] = &[ + ( + String::from(include_str!("../../test_data/babbage4.0.address")), + Value::Coin(25000000), + Some(PseudoDatumOption::Hash( + hex::decode("3e8c4b1d396bb8132e5097f5a2f012d97900cbc496a3745db4226cea4cb66465") + .unwrap() + .as_slice() + .into(), + )), + None, + ), + ( + String::from(include_str!("../../test_data/babbage4.1.address")), + Value::Multiasset( + 1795660, + KeyValuePairs::from(Vec::from([( + "787f0c946b98153500edc0a753e65457250544da8486b17c85708135" + .parse() + .unwrap(), + KeyValuePairs::from(Vec::from([( + Bytes::from( + hex::decode("506572666563744c6567656e64617279446572705365616c") + .unwrap(), + ), + 1, + )])), + )])), + ), + None, + None, + ), + ]; + let mut utxos: UTxOs = mk_utxo_for_babbage_tx(&mtx.transaction_body, tx_outs_info); + let collateral_info: &[( + String, + Value, + Option, + Option>, + )] = &[( + String::from(include_str!("../../test_data/babbage4.collateral.address")), + Value::Coin(5000000), + None, + None, + )]; + add_collateral_babbage(&mtx.transaction_body, &mut utxos, collateral_info); + let mut tx_wits: MintedWitnessSet = mtx.transaction_witness_set.unwrap().clone(); + let old_redeemer: Redeemer = tx_wits.redeemer.unwrap().pop().unwrap(); + let new_redeemer: Redeemer = Redeemer { + tag: RedeemerTag::Spend, + index: 15, + data: PlutusData::Array(Vec::new()), + ex_units: ExUnits { mem: 0, steps: 0 }, + }; + tx_wits.redeemer = Some(vec![old_redeemer, new_redeemer]); + let mut tx_buf: Vec = Vec::new(); + let _ = encode(tx_wits, &mut tx_buf); + mtx.transaction_witness_set = + Decode::decode(&mut Decoder::new(&tx_buf.as_slice()), &mut ()).unwrap(); + let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let env: Environment = Environment { + prot_params: MultiEraProtParams::Babbage(BabbageProtParams { + fee_policy: FeePolicy { + summand: 155381, + multiplier: 44, + }, + max_tx_size: 16384, + max_block_ex_mem: 62000000, + max_block_ex_steps: 40000000000, + max_tx_ex_mem: 14000000, + max_tx_ex_steps: 10000000000, + max_val_size: 5000, + collateral_percent: 150, + max_collateral_inputs: 3, + coins_per_utxo_word: 4310, + }), + prot_magic: 764824073, + block_slot: 72317003, + network_id: 1, + }; + match validate(&metx, &utxos, &env) { + Ok(()) => assert!(false, "Unneeded datum"), + Err(err) => match err { + Babbage(BabbageError::UnneededRedeemer) => (), + _ => assert!(false, "Unexpected error ({:?})", err), + }, + } + } + + #[test] + // Same as successful_mainnet_tx_with_plutus_v1_script, except that the + // redeemers list is modified in such a way that all other checks pass, but + // the integrity hash related to script execution no longer matches the one + // contained in the TransactionBody. + fn script_integrity_hash() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/babbage4.tx")); + let mut mtx: MintedTx = babbage_minted_tx_from_cbor(&cbor_bytes); + let tx_outs_info: &[( + String, + Value, + Option, + Option>, + )] = &[ + ( + String::from(include_str!("../../test_data/babbage4.0.address")), + Value::Coin(25000000), + Some(PseudoDatumOption::Hash( + hex::decode("3e8c4b1d396bb8132e5097f5a2f012d97900cbc496a3745db4226cea4cb66465") + .unwrap() + .as_slice() + .into(), + )), + None, + ), + ( + String::from(include_str!("../../test_data/babbage4.1.address")), + Value::Multiasset( + 1795660, + KeyValuePairs::from(Vec::from([( + "787f0c946b98153500edc0a753e65457250544da8486b17c85708135" + .parse() + .unwrap(), + KeyValuePairs::from(Vec::from([( + Bytes::from( + hex::decode("506572666563744c6567656e64617279446572705365616c") + .unwrap(), + ), + 1, + )])), + )])), + ), + None, + None, + ), + ]; + let mut utxos: UTxOs = mk_utxo_for_babbage_tx(&mtx.transaction_body, tx_outs_info); + let collateral_info: &[( + String, + Value, + Option, + Option>, + )] = &[( + String::from(include_str!("../../test_data/babbage4.collateral.address")), + Value::Coin(5000000), + None, + None, + )]; + add_collateral_babbage(&mtx.transaction_body, &mut utxos, collateral_info); + let mut tx_witness_set: MintedWitnessSet = (*mtx.transaction_witness_set).clone(); + let mut redeemer: Redeemer = tx_witness_set.redeemer.unwrap().pop().unwrap(); + redeemer.ex_units = ExUnits { mem: 0, steps: 0 }; + tx_witness_set.redeemer = Some(vec![redeemer]); + let mut tx_witness_set_buf: Vec = Vec::new(); + let _ = encode(tx_witness_set, &mut tx_witness_set_buf); + mtx.transaction_witness_set = + Decode::decode(&mut Decoder::new(&tx_witness_set_buf.as_slice()), &mut ()).unwrap(); + let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let env: Environment = Environment { + prot_params: MultiEraProtParams::Babbage(BabbageProtParams { + fee_policy: FeePolicy { + summand: 155381, + multiplier: 44, + }, + max_tx_size: 16384, + max_block_ex_mem: 62000000, + max_block_ex_steps: 40000000000, + max_tx_ex_mem: 14000000, + max_tx_ex_steps: 10000000000, + max_val_size: 5000, + collateral_percent: 150, + max_collateral_inputs: 3, + coins_per_utxo_word: 4310, + }), + prot_magic: 764824073, + block_slot: 72317003, + network_id: 1, + }; + match validate(&metx, &utxos, &env) { + Ok(()) => assert!(false, "Wrong script integrity hash"), + Err(err) => match err { + Babbage(BabbageError::ScriptIntegrityHash) => (), + _ => assert!(false, "Unexpected error ({:?})", err), + }, + } + } +} diff --git a/pallas-applying/tests/common.rs b/pallas-applying/tests/common.rs index 6f17e567..afc33cc2 100644 --- a/pallas-applying/tests/common.rs +++ b/pallas-applying/tests/common.rs @@ -2,12 +2,17 @@ use pallas_applying::UTxOs; use pallas_codec::{minicbor::bytes::ByteVec, utils::TagWrap}; use pallas_primitives::{ alonzo::{MintedTx, TransactionBody, TransactionOutput, Value}, + babbage::{ + MintedDatumOption, MintedPostAlonzoTransactionOutput, MintedScriptRef, + MintedTransactionBody, MintedTransactionOutput, MintedTx as BabbageMintedTx, + PseudoTransactionOutput, + }, byron::{Address, MintedTxPayload, Tx, TxOut}, }; use pallas_traverse::{MultiEraInput, MultiEraOutput}; use std::{borrow::Cow, iter::zip, vec::Vec}; -use pallas_codec::utils::Bytes; +use pallas_codec::utils::{Bytes, CborWrap}; use pallas_crypto::hash::Hash; pub fn cbor_to_bytes(input: &str) -> Vec { @@ -18,8 +23,12 @@ pub fn minted_tx_from_cbor(tx_cbor: &[u8]) -> MintedTx<'_> { pallas_codec::minicbor::decode::(tx_cbor).unwrap() } -pub fn minted_tx_payload_from_cbor(tx_cbor: &[u8]) -> MintedTxPayload<'_> { - pallas_codec::minicbor::decode::(tx_cbor).unwrap() +pub fn babbage_minted_tx_from_cbor(tx_cbor: &[u8]) -> BabbageMintedTx<'_> { + pallas_codec::minicbor::decode::(&tx_cbor[..]).unwrap() +} + +pub fn minted_tx_payload_from_cbor<'a>(tx_cbor: &'a Vec) -> MintedTxPayload<'a> { + pallas_codec::minicbor::decode::(&tx_cbor[..]).unwrap() } 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>( tx_body: &TransactionBody, - tx_outs_info: &[(String, Value, Option>)], + tx_outs_info: &[( + String, // address in string format + Value, + Option>, + )], ) -> UTxOs<'a> { let mut utxos: UTxOs = UTxOs::new(); 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) { Ok(bytes_vec) => Bytes::from(bytes_vec), _ => panic!("Unable to decode input address"), @@ -58,8 +73,6 @@ pub fn mk_utxo_for_alonzo_compatible_tx<'a>( amount: amount.clone(), datum_hash: *datum_hash, }; - let multi_era_in: MultiEraInput = - MultiEraInput::AlonzoCompatible(Box::new(Cow::Owned(tx_in))); let multi_era_out: MultiEraOutput = MultiEraOutput::AlonzoCompatible(Box::new(Cow::Owned(tx_out))); utxos.insert(multi_era_in, multi_era_out); @@ -67,10 +80,44 @@ pub fn mk_utxo_for_alonzo_compatible_tx<'a>( 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, + Option>, + )], +) -> 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, utxos: &mut UTxOs<'_>, - collateral_info: &[(String, Value, Option>)], + collateral_info: &[( + String, // address in string format + Value, + Option>, + )], ) { match &tx_body.collateral { Some(collaterals) => { @@ -94,3 +141,85 @@ pub fn add_collateral( 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, + Option>, + )], +) { + 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, + Option>, + )], +) { + 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"), + } +} diff --git a/test_data/babbage3.address b/test_data/babbage3.address new file mode 100644 index 00000000..d982f9ed --- /dev/null +++ b/test_data/babbage3.address @@ -0,0 +1 @@ +011be1f490912af2fc39f8e3637a2bade2ecbebefe63e8bfef10989cd6f593309a155b0ebb45ff830747e61f98e5b77feaf7529ce9df351382 \ No newline at end of file diff --git a/test_data/babbage3.tx b/test_data/babbage3.tx new file mode 100644 index 00000000..75c9e526 --- /dev/null +++ b/test_data/babbage3.tx @@ -0,0 +1 @@ +84a40081825820f193aa92b0c401c4ab4694622501b4890330e7a4a7a20533d833a5639b7fc9e601018282581d61775a6cb8a363c28daacd0e535c8914df21533ad07458c23a99f9043a1a007a120082583901e241af5d59f4675250fcbf0f6f50e6c203a268955f92e036a5018243f593309a155b0ebb45ff830747e61f98e5b77feaf7529ce9df3513821a05abfc02021a00028cad031a044fa19ea10081825820f2fc4a141e5d2d2678434f851997a71610bf40ae2cd6b18cb67e8f188d8304ba5840f7b3cefab0d828972e6221e8896c336187ba55f8c79fe83d5a6de9c1a32e6ea788629d7294785c76ef49b9fdb7cd7a932fe32c2761d640c19aa7840a44fc060bf5f6 \ No newline at end of file diff --git a/test_data/babbage4.0.address b/test_data/babbage4.0.address new file mode 100644 index 00000000..75649011 --- /dev/null +++ b/test_data/babbage4.0.address @@ -0,0 +1 @@ +11a55f409501bf65805bb0dc76f6f9ae90b61e19ed870bc0025681360881728e7ed4cf324e1323135e7e6d931f01e30792d9cdf17129cb806d \ No newline at end of file diff --git a/test_data/babbage4.1.address b/test_data/babbage4.1.address new file mode 100644 index 00000000..06e82cdb --- /dev/null +++ b/test_data/babbage4.1.address @@ -0,0 +1 @@ +01f1e126304308006938d2e8571842ff87302fff95a037b3fd838451b8b3c9396d0680d912487139cb7fc85aa279ea70e8cdacee4c6cae40fd \ No newline at end of file diff --git a/test_data/babbage4.collateral.address b/test_data/babbage4.collateral.address new file mode 100644 index 00000000..06e82cdb --- /dev/null +++ b/test_data/babbage4.collateral.address @@ -0,0 +1 @@ +01f1e126304308006938d2e8571842ff87302fff95a037b3fd838451b8b3c9396d0680d912487139cb7fc85aa279ea70e8cdacee4c6cae40fd \ No newline at end of file diff --git a/test_data/babbage4.tx b/test_data/babbage4.tx new file mode 100644 index 00000000..930761da --- /dev/null +++ b/test_data/babbage4.tx @@ -0,0 +1 @@ +84a80082825820459763315cb9af2ecd9003a4236aacae4ec4777df7f4f757b5b0187a32eca90700825820df92937f762ae2f0afcb9829c3ef514635ac0d9975750225519cdb071e1cff9201018582583901f1e126304308006938d2e8571842ff87302fff95a037b3fd838451b8b3c9396d0680d912487139cb7fc85aa279ea70e8cdacee4c6cae40fd1a000f422a82583901c7f913cb1a0d62a1dbd9eb0c5fb4d5b9ff1ed370fbb6b2dbe98c4b82d3e62702c687b1d0d010136220d0a389b590f2e98399502a2bfc4b2b1a002625a08258390170e60f3b5ea7153e0acc7a803e4401d44b8ed1bae1c7baaad1a62a721e78aae7c90cc36d624f7b3bb6d86b52696dc84e490f343eba89005f1a000f424082583901b7469fffd8657fdc71bfcc2368abf070025f8ed3b2d07edf4211383c9e2efeca24440f4ef0d0718ed066b0d0928af76584eb87a3b7fe2549821a00159282a1581c787f0c946b98153500edc0a753e65457250544da8486b17c85708135a15818506572666563744c6567656e64617279446572705365616c0182583901f1e126304308006938d2e8571842ff87302fff95a037b3fd838451b8b3c9396d0680d912487139cb7fc85aa279ea70e8cdacee4c6cae40fd1a013423b4021a000a7e4c031a044f858c081a044f777c0b5820b8f025288ba73aed0fe31fad243c58bef276caf20e70ade9d343bbed62b5fdc00d81825820ff4f36b81327cfb4b17c958b790447c4734fe39c8741dd1f38ace0fd54fcf2fc010e81581cf1e126304308006938d2e8571842ff87302fff95a037b3fd838451b8a400818258205dd8c98f199a00b5a7ce004079f917c7f752bfc1147f0eed545bc877c4c037895840426ac6d06bd7b90a52e4c11a7d56e2b81717d523628f3f96afbdb8b0d0bc1f7363fc48fcb3d069b58e07d1cc0c67f46e2b0d0070b6178e29c3f7b0ee62f2310d0381590fc1590fbe0100003233223232323322333222323233322232333222323332223322323322323332223232332233223232332232323332223322332233223322323232323232323232323232332232323232323232323322323232332232333322223232323232322232232325335305c33223530310012235303500222222222322235304400c23232325335306f333222533530723300300200110731074506e32353047001220023253353505a00113507649010350543800221002302e5002004107115335304a01313301b49101350033355029302f1200123535505e00122323355030335502d30331200123535506200122353550640012253353506f335503533550250490040062153353507033550363335550323039120012235355069002232233225335350770022130020011507800425335350763335502c0500040072153353080013304d0010031335503d5079300400215335308001330630010031335503d507933335502e0510053304900100300215078150773200135508601225335350680011506a2213535506e002225335308201330530020071003133506d33550710020013006003350720010022133026491023130003322333573466e200080041f41f8cc8cd54c0ec48004d40c00048004cd40c40952000335530321200123535506900122001001004133573892010231310007a133573892010231320007900233233553023120013503500135034001335038223355302c120012353550630012233550660023355302f12001235355066001223355069002333535502b0012330264800000488cc09c0080048cc09800520000013355302c1200123535506300122335506600233353550280012335530301200123535506700122335506a00235502f0010012233355502804a0020012335530301200123535506700122335506a00235502d001001333555023045002001505e33233553023120012253353506c3003002213350610010021001505f2353053001222533530763332001504100600313506f0021506e011320013333555028302f1200122353055002225335307333044002500b10031333355502c303312001235305a00122253353506e333550245042003001213333550265043004335530301200123535506700122335506a002333535502c0012001223535506b002223535506d0032233550703302c00400233553039120012353550700012233550730023335355035001200122330310020012001333555030052003001200133355502704900300100213333550255042003002001003001505b500113301b49101340033355029302f1200123530540012233043500a00250011533535058335530401200123320015051320013530460012235305100122253353506b0012321300100d3200135507f2253353506100113507d491022d310022135355067002225335307b3304c00200710011300600313507a49101370050011350744901013600221335530421200123320015053320013530480012235305300122253353506d0012321300100f32001355081012253353506300113507f491022d310022135355069002225335307d3304e00200710011300600313507c4910137005003133355301d12001225335306f335306a303e302d35304600222001207125335307033041001300401010721350764901013300133505a0020011001505900d3200135507622533535058001135074491022d31002213530470022253353072333200150710020071353063303000122335306f00223507b491022d310020011300600315335350520011306d4988854cd4d41500044008884c1c5263333573466e1d40112002203a23333573466e1d40152000203a23263530663357380b80ce0ca0c80c66666ae68cdc39aab9d5002480008cc0c4c8c8c8c8c8c8c8c8c8c8c8cccd5cd19b8735573aa01490001199999999981f99a828919191999ab9a3370e6aae7540092000233045304b35742a00460986ae84d5d1280111931a983a99ab9c06b076074073135573ca00226ea8004d5d0a80519a8288241aba1500935742a0106ae85401cd5d0a8031aba1500535742a00866a0a2eb8d5d0a80199a82899aa82b3ae200135742a0046ae84d5d1280111931a983899ab9c06707207006f135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135573ca00226ea8004d5d0a80119191999ab9a3370e6aae75400520022303a303e357426aae7940088c98d4c1a0cd5ce02f03483383309baa001357426ae8940088c98d4c194cd5ce02d833032031883289931a983219ab9c49010350543500065063135573ca00226ea80044d55ce9baa001223370000400244a66a60ac00220b0266ae7000815c4488c88c008004c8004d5417c894cd4d41040045413c884d4d5411c008894cd4c16ccc02000801c4d41500044c01800c448888c8cd54c03c480048d4d5411800488cd54124008ccd4d5402c0048004880048004ccd554018014008004c8cd40054109410c488cc008cd5411c014010004444888ccd54c0104800540fccd54c030480048d4d5410c00488cd54118008d5402c004ccd54c0104800488d4d54110008894cd4c160ccd54c06048004d4034cd403c894cd4c168008417040041648d4d5411c00488cc028008014018400c4cd410c01000d4100004cd54c030480048d4d5410c00488c8cd5411c00cc004014c8004d54184894cd4d410c0044d5402c00c884d4d54124008894cd4c174cc0300080204cd5404001c0044c01800c008c8004d5416888448894cd4d40fc0044008884cc014008ccd54c01c480040140100044484888c00c01044884888cc0080140104484888c00401044800448cd404888ccd4d401000c88008008004d4d40080048800448848cc00400c00848004c8004d541488844894cd4d40d8004540e0884cd40e4c010008cd54c018480040100044448888cccd54011403c00c0040084488cd54008c8cd403c88ccd4d401800c88008008004d4d401000488004cd401005012800448848cc00400c008480044488c0080048d4c08c00488800cc8004d5412c88c8c94cd4c114cc0a14009200213300c300433530081200150010033004335300d12001500100310031332233706004002a002900209999aa9801890009919a80511199a8040018008011a802800a8039119b800014800800520003200135504a221122253353502f00113500600322133350090053004002333553007120010050040011235350050012200112353500400122002320013550472212253353041333573466e2400920000430421502d153353502b0011502d22133502e002335300612001337020089001000899a801111180198010009000891091980080180109000990009aa821911299a9a81300108009109a980a801111a982180111299a9a8160038a99a9a8160038804110b1109a980d801111a982480111299a9824199ab9a33720010004094092266a0660186601e01601a2a66a6090666ae68cdc8804001024825099a819803198078070028a99a982419809003800899a81980619807805806899a81980319807807002990009aa82111091299a9a8130008a814110a99a981f19804002240002006266a600c240026600e00890010009119b8100200122333573466e240080040e80e4488cc00ccc01cc018008c018004cc894cd4d40c0008854cd4d40c400884cd4c0b80088cd4c0bc0088cc034008004888100888cd4c0c401081008894cd4c104ccd5cd19b87006003043042153353041333573466e1c01400810c1084cc0380100044108410840ec54cd4d40c0004840ec40ecc014008c014004894cd4c0d8008400440dc88ccd5cd19b8700200103703623530240012200123530230012200222335302d0022335302e00223300500200120352335302e002203523300500200122333573466e3c0080040cc0c8c8004d540e08844894cd4d407000454078884cd407cc010008cd54c018480040100048848cc00400c0088004888888888848cccccccccc00402c02802402001c01801401000c00880048848cc00400c0088004848c004008800448800848800480048c8c8cccd5cd19b8735573aa004900011981519191999ab9a3370e6aae75400520002375c6ae84d55cf280111931a981899ab9c02703203002f137540026ae854008dd69aba135744a004464c6a605c66ae700900bc0b40b04d55cf280089baa00123232323333573466e1cd55cea801a4000466600e602c6ae85400cccd5403dd719aa807bae75a6ae854008cd4071d71aba135744a004464c6a605c66ae700900bc0b40b04d5d1280089aab9e5001137540024442466600200800600440022464646666ae68cdc39aab9d5002480008cc88cc024008004dd71aba1500233500a232323333573466e1cd55cea80124000466446601e004002602c6ae854008ccd5403dd719aa809919299a981419805a800a40022a00226a05c921022d33001375a00266aa01eeb88c94cd4c0a0cc02d400520001500113502e491022d32001375a0026ae84d5d1280111931a981719ab9c02402f02d02c135573ca00226ea8004d5d09aba25002232635302a33573804005605205026aae7940044dd500091199ab9a33712004002040042442466002006004400244246600200600440022464460046eb0004c8004d5408c88cccd55cf80092804119a80398021aba1002300335744004048224464460046eac004c8004d5408c88c8cccd55cf80112804919a80419aa80618031aab9d5002300535573ca00460086ae8800c0944d5d0800889100109109119800802001890008891119191999ab9a3370e6aae754009200023355008300635742a004600a6ae84d5d1280111931a981099ab9c01702202001f135573ca00226ea8004448848cc00400c0084480048c8c8cccd5cd19b8735573aa004900011980318071aba1500233500a2323232323333573466e1d40052002233300e375a6ae854010dd69aba15003375a6ae84d5d1280191999ab9a3370ea004900011808180a9aba135573ca00c464c6a604666ae700640900880840804d55cea80189aba25001135573ca00226ea8004d5d09aba25002232635301c33573802403a03603426aae7940044dd5000910919800801801100090911801001911091199800802802001900089119191999ab9a3370ea002900011a80418029aba135573ca00646666ae68cdc3a801240044a010464c6a603066ae7003806405c0580544d55cea80089baa001121223002003112200112001232323333573466e1d4005200223006375c6ae84d55cf280191999ab9a3370ea0049000118041bae357426aae7940108c98d4c04ccd5ce00480a00900880809aab9d50011375400242446004006424460020064002921035054310012253353003333573466e3cd4c01800888008d4c018004880080140104ccd5cd19b873530060022200135300600122001005004100412200212200120012212330010030022001235002490101310012326353003335738002008004930900090008891918008009119801980100100081049fd8799f581cb7469fffd8657fdc71bfcc2368abf070025f8ed3b2d07edf4211383c9fd8799fd8799fd8799f581cc7f913cb1a0d62a1dbd9eb0c5fb4d5b9ff1ed370fbb6b2dbe98c4b82ffd8799fd8799fd8799f581cd3e62702c687b1d0d010136220d0a389b590f2e98399502a2bfc4b2bffffffffa140d8799f00a1401a002625a0ffffd8799fd8799fd8799f581c70e60f3b5ea7153e0acc7a803e4401d44b8ed1bae1c7baaad1a62a72ffd8799fd8799fd8799f581c1e78aae7c90cc36d624f7b3bb6d86b52696dc84e490f343eba89005fffffffffa140d8799f00a1401a000f4240ffffd8799fd8799fd8799f581cb7469fffd8657fdc71bfcc2368abf070025f8ed3b2d07edf4211383cffd8799fd8799fd8799f581c9e2efeca24440f4ef0d0718ed066b0d0928af76584eb87a3b7fe2549ffffffffa1581c787f0c946b98153500edc0a753e65457250544da8486b17c85708135d8799f00a15818506572666563744c6567656e64617279446572705365616c01ffffffffff0581840000d87a80821a003820881a4dc7d8f7f5f6 \ No newline at end of file diff --git a/test_data/babbage5.0.address b/test_data/babbage5.0.address new file mode 100644 index 00000000..f3ca2b90 --- /dev/null +++ b/test_data/babbage5.0.address @@ -0,0 +1 @@ +719b85d5e8611945505f078aeededcbed1d6ca11053f61e3f9d999fe44 \ No newline at end of file diff --git a/test_data/babbage5.1.address b/test_data/babbage5.1.address new file mode 100644 index 00000000..61fee131 --- /dev/null +++ b/test_data/babbage5.1.address @@ -0,0 +1 @@ +0121316dbc84420a5ee7461438483564c41fae876029319b3ee641fe4422339411d2df4c9c7c50b3d8f88db98d475e9d1bccd4244b412fbe5e \ No newline at end of file diff --git a/test_data/babbage5.collateral.address b/test_data/babbage5.collateral.address new file mode 100644 index 00000000..61fee131 --- /dev/null +++ b/test_data/babbage5.collateral.address @@ -0,0 +1 @@ +0121316dbc84420a5ee7461438483564c41fae876029319b3ee641fe4422339411d2df4c9c7c50b3d8f88db98d475e9d1bccd4244b412fbe5e \ No newline at end of file diff --git a/test_data/babbage5.tx b/test_data/babbage5.tx new file mode 100644 index 00000000..f6136f3e --- /dev/null +++ b/test_data/babbage5.tx @@ -0,0 +1 @@ +84a900828258202a60a942cbcb7d40415610dca0fa5e814f5e3222f42a0cf210d16dee6de22e2d008258200e3f2e32c693d0ead0fd38c95f2491cf34aac0bb5fe2c99439ec84f29328a3d002018382583901adb1bf6a51b20ff1b8450726ef3891bb0e153d5bf47783375e2134afbd6a096cbba5e259946798e948403e2d2b3d9ea88a12ee8e7ae94497821a0026913aa1581cd195ca7db29f0f13a00cac7fca70426ff60bad4e1e87d3757fae8484a3456876414441190baf4568764d494e1a00086c0d46687641414441193f408258390121316dbc84420a5ee7461438483564c41fae876029319b3ee641fe4422339411d2df4c9c7c50b3d8f88db98d475e9d1bccd4244b412fbe5e821a001f0b06a1581ce4214b7cce62ac6fbba385d164df48e157eae5863521b4b67ca71d86a1582039b9b709ac8605fc82116a2efc308181ba297c11950f0f350001e28f0e50868b1a028958b18258390121316dbc84420a5ee7461438483564c41fae876029319b3ee641fe4422339411d2df4c9c7c50b3d8f88db98d475e9d1bccd4244b412fbe5e821a0b993027a1581c29d222ce763455e3d7a09a665ce554f00ac89d2e99a1a83d267170c6a1434d494e1b0000000124dc7962021a000922d5031a044fa1ba075820ce6e3db1fe7397d6b3a423ada6ae73cbb5eb080436a61123cfa8b65f81b3b84d09a1581cd195ca7db29f0f13a00cac7fca70426ff60bad4e1e87d3757fae8484a4456876414441190baf4568764d494e1a00086c0d46687641414441193f4049323738333331333737200b58205e005a7d39c61cf611c45b00bc8ba1a19b8a12263a5e0a4f3c153e54344e805d0d81825820a596bd0d02397f058cb89925c9965059660f147b7aa19fdf9e3edfa921164632000e81581c21316dbc84420a5ee7461438483564c41fae876029319b3ee641fe44a500828258205424fa10ba83c95c33714c420479c19183a7274e7c1d4161d173842c245b340c584044225c6e3fe7fa005921213653363d728bede7b110c90bc06b52015784a016b5524d2e5bb778eee85a1d6b8a58db41bed500ad941b62a8c72a05f650f0650704825820386df22fa413a20b6e55b256f4b6d23a321fccae6bd72c916e0d887c8567ecdc584020296d12adceed34361a8819ccfd3cdb3289d073682e4fe0e9def9b9bfee6cec5c7a69ffb1e14d4b6112b5d7e7517b1479046c486c13b86e92e198c170efad0901818200581c4f641455f17911fe2f55ad3ad67fc2e0b2946b59af3352574322e67e03815906f45906f10100003232323232323232323232323232323232323232323232323232323232323232323232232223232323232323553353019003210011622323235301b35301a3500202c01922325335533553335734606800204c2a666ae68c0cc0040540c454cd4ccd54c0b00b4c05804cc0a14040cc08dd71aa80491111110118a99a980419980c3ae5017355335301912233301c22533533018301f002500813300400200110010013302501f35500b034162215335001100222160121300833301875ca02e6a603a6a00a05e02404c04c2a66a666aa605805a602c0266050a02066046eb8d54024888888808c54cd54cd4c064488ccc070894cd4cc060c07c00940204cc0100080044004004cc09407cd5402c0d00548854cd40040a0880644ccd5cd19b87355335300d33301875ca02e6a603a6a00a05e02442a66a0022c4426a00444a66a0062a666ae68c0e40044cc0a80c00085888585888c8cc0cc88cd400520002235002225333573466e3c0080244c01c0044c01800cccc06dd7280d1aa806111111111100398191119a800a4000446a00444a666ae68cdc780100388008980300199b8148001200202902a026026102d1635573a6ea80384c068d4c064d40040ac0614cd4c06c014840045894cd4c014004854cd40040808854cd40040448809007c4c064d40040954ccd5cd18149aab9d0011323301c357420026ae84d5d10009aab9e0010273754006604844a66a0020384426a00444a66a6604400404a260420022600c006604644a66a0020364426a00444a66a66042004048260400022600c006264a66a6a6aa666ae68c090d55ce80089909180080118079aba135573c00204440024466a004034403c420022c6ea8004888008cc048894cd4008010400405c88d400888d400c88c8cd40148cd401094ccd5cd19b8f00200100301b201f2335004201f25333573466e3c00800400c06c54cd400c854cd400884cd40088cd40088cd40088cd40088cc08000800480888cd400880888cc080008004888088888cd401080888894ccd5cd19b8700600315333573466e1c0140084ccd5cd19b87004001023024020020019153350012019007101423301022333500301b0020013500101a13300175ceb488c88c008dd5800980d111999aab9f00120162323301233013300635573a002600a6aae78004c010d5d10019aba1002019233500100c011223018225335001100322133006002300400123004350010022220032325333573460306aae740044c8c8c8c8c848ccc00401400c008d5d09aba2002357420026ae88008d5d08009aab9e0010163754002464a666ae68c05cd55ce80089919191980618031aba1003533357346034002264646020a666ae68c0740044c8c8c8c8c8488ccc00401801000cdd69aba1357440046eb4d5d08009aba2002375a6ae84004d55cf0010a999ab9a301c001130103008357426aae78008068d55ce8009baa357426aae7800854ccd5cd180d80080680c1aab9d00137546ae84d5d10009aab9e001015375400246464a666ae68c0600044c8488c00400cdd71aba135573c0042a666ae68c05c0044c02cdd71aba135573c00402a6aae74004dd5000919191999aa999ab9a3370e90030008990911118020029aba135573c0042a666ae68cdc3a400800226424444600400a6ae84d55cf0010a999ab9a3017001132122223001005357426aae7800854ccd5cd180b0008990911118018029aba135573c004028401c401c401c4666aa60200226601244a66a00442006200201c46464a66aa666ae68cdc79a80100a9a80080a8999ab9a3370e6a00402c6a00202c02002201a2601c00601a600e006600c6a0020266600e464a666ae68c064d55ce800899198061aba1001357426ae88004d55cf00080b9baa00135300635004012222222222200a35573a0026ea8d40040408c94ccd5cd180a1aab9d001132323300853335734602c6aae740044dd71aba135573c0020286eb4d5d09aba200237546ae84004d55cf0008091baa0012325333573460266aae740044c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c848cccccccccc00404c04403c03402c02401c01400c008d5d09aba2002357420026ae88008d5d08009aba2002357420026ae88008d5d08009aba2002357420026ae88008d5d08009aba2002357420026ae88008d5d08009aba2002357420026aae78004044dd5000919118011bac001300e2233335573e002401446600a60086ae84008c00cd5d1001006910911980080200191091980080180108021091180100191199ab9a3371e004002006008200a200a9111cd195ca7db29f0f13a00cac7fca70426ff60bad4e1e87d3757fae848400300422112225335001135003008221333500500930040023335530070080050040011200122002220012323001001223300330020020011533573892010350543100162222222222009370e90001b8748009049fd8799fd8799fd8799f581c21316dbc84420a5ee7461438483564c41fae876029319b3ee641fe44ffd8799fd8799fd8799f581c22339411d2df4c9c7c50b3d8f88db98d475e9d1bccd4244b412fbe5effffffffffff0581840001d87a80821a002dc6c01a77359400f5a11902a2a26965787472614461746180636d736781781b4d696e737761703a205769746864726177206c6971756964697479 \ No newline at end of file diff --git a/test_data/babbage6.0.address b/test_data/babbage6.0.address new file mode 100644 index 00000000..a4265727 --- /dev/null +++ b/test_data/babbage6.0.address @@ -0,0 +1 @@ +11A55F409501BF65805BB0DC76F6F9AE90B61E19ED870BC0025681360881728E7ED4CF324E1323135E7E6D931F01E30792D9CDF17129CB806D \ No newline at end of file diff --git a/test_data/babbage6.1.address b/test_data/babbage6.1.address new file mode 100644 index 00000000..b18940a0 --- /dev/null +++ b/test_data/babbage6.1.address @@ -0,0 +1 @@ +01EDA33318624ADE03D53B7E954713D9E69440891F0D02E823267B610D6018DC6C7989A46EC26822425A3D2BAC60EEC2682A022740361ED957 \ No newline at end of file diff --git a/test_data/babbage6.collateral.address b/test_data/babbage6.collateral.address new file mode 100644 index 00000000..6ed361e6 --- /dev/null +++ b/test_data/babbage6.collateral.address @@ -0,0 +1 @@ +01eda33318624ade03d53b7e954713d9e69440891f0d02e823267b610d6018dc6c7989a46ec26822425a3d2bac60eec2682a022740361ed957 \ No newline at end of file diff --git a/test_data/babbage6.tx b/test_data/babbage6.tx new file mode 100644 index 00000000..30bd6377 --- /dev/null +++ b/test_data/babbage6.tx @@ -0,0 +1 @@ +84a900828258208f5a8125fdd1634501fdf8e8aa52e08b83926df6c9dce53422720cd0e5792f4200825820bffd9367dedec77712dc683b4cfb7d096e20ee54fd3e8789b0666309e102d09704018283583911a55f409501bf65805bb0dc76f6f9ae90b61e19ed870bc0025681360881728e7ed4cf324e1323135e7e6d931f01e30792d9cdf17129cb806d821a0019c812a1581cdc8f23301b0e3d71af9ac5d1559a060271aa6cf56ac98bdaeea19e18a14330373401582072e4aca779e544bc9cdc8540b3500c50c72b1fcdd217ef0baa621085783730e682583901eda33318624ade03d53b7e954713d9e69440891f0d02e823267b610d6018dc6c7989a46ec26822425a3d2bac60eec2682a022740361ed9571a00443d7e021a00080dc2031a044f8183075820415e68a81ff20706192bddc5fbb7d66ddc53bccacb56865d544baee76376c3a8081a044f73730b5820de2ff38ce5a67713531e4422a4071d4a095ef6ddfcd8433fd265a820c14871e40d818258206a85011090a81e6c988801da4577322a014bb6de45e2d19dbca2078e113b66b8000e81581ceda33318624ade03d53b7e954713d9e69440891f0d02e823267b610da4008182582086feab5e547328144ffe660f7a6013ed9a9e1cfb22766161d07b99a969014820584042753afe22628fa6d95531b3766b423ef51e797d057330a42fc503b2bd2585dca8870b6497337ee151c1eb7475ff86bf1f819027678662e397d19057abda3f060381590fc1590fbe0100003233223232323322333222323233322232333222323332223322323322323332223232332233223232332232323332223322332233223322323232323232323232323232332232323232323232323322323232332232333322223232323232322232232325335305c33223530310012235303500222222222322235304400c23232325335306f333222533530723300300200110731074506e32353047001220023253353505a00113507649010350543800221002302e5002004107115335304a01313301b49101350033355029302f1200123535505e00122323355030335502d30331200123535506200122353550640012253353506f335503533550250490040062153353507033550363335550323039120012235355069002232233225335350770022130020011507800425335350763335502c0500040072153353080013304d0010031335503d5079300400215335308001330630010031335503d507933335502e0510053304900100300215078150773200135508601225335350680011506a2213535506e002225335308201330530020071003133506d33550710020013006003350720010022133026491023130003322333573466e200080041f41f8cc8cd54c0ec48004d40c00048004cd40c40952000335530321200123535506900122001001004133573892010231310007a133573892010231320007900233233553023120013503500135034001335038223355302c120012353550630012233550660023355302f12001235355066001223355069002333535502b0012330264800000488cc09c0080048cc09800520000013355302c1200123535506300122335506600233353550280012335530301200123535506700122335506a00235502f0010012233355502804a0020012335530301200123535506700122335506a00235502d001001333555023045002001505e33233553023120012253353506c3003002213350610010021001505f2353053001222533530763332001504100600313506f0021506e011320013333555028302f1200122353055002225335307333044002500b10031333355502c303312001235305a00122253353506e333550245042003001213333550265043004335530301200123535506700122335506a002333535502c0012001223535506b002223535506d0032233550703302c00400233553039120012353550700012233550730023335355035001200122330310020012001333555030052003001200133355502704900300100213333550255042003002001003001505b500113301b49101340033355029302f1200123530540012233043500a00250011533535058335530401200123320015051320013530460012235305100122253353506b0012321300100d3200135507f2253353506100113507d491022d310022135355067002225335307b3304c00200710011300600313507a49101370050011350744901013600221335530421200123320015053320013530480012235305300122253353506d0012321300100f32001355081012253353506300113507f491022d310022135355069002225335307d3304e00200710011300600313507c4910137005003133355301d12001225335306f335306a303e302d35304600222001207125335307033041001300401010721350764901013300133505a0020011001505900d3200135507622533535058001135074491022d31002213530470022253353072333200150710020071353063303000122335306f00223507b491022d310020011300600315335350520011306d4988854cd4d41500044008884c1c5263333573466e1d40112002203a23333573466e1d40152000203a23263530663357380b80ce0ca0c80c66666ae68cdc39aab9d5002480008cc0c4c8c8c8c8c8c8c8c8c8c8c8cccd5cd19b8735573aa01490001199999999981f99a828919191999ab9a3370e6aae7540092000233045304b35742a00460986ae84d5d1280111931a983a99ab9c06b076074073135573ca00226ea8004d5d0a80519a8288241aba1500935742a0106ae85401cd5d0a8031aba1500535742a00866a0a2eb8d5d0a80199a82899aa82b3ae200135742a0046ae84d5d1280111931a983899ab9c06707207006f135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135573ca00226ea8004d5d0a80119191999ab9a3370e6aae75400520022303a303e357426aae7940088c98d4c1a0cd5ce02f03483383309baa001357426ae8940088c98d4c194cd5ce02d833032031883289931a983219ab9c49010350543500065063135573ca00226ea80044d55ce9baa001223370000400244a66a60ac00220b0266ae7000815c4488c88c008004c8004d5417c894cd4d41040045413c884d4d5411c008894cd4c16ccc02000801c4d41500044c01800c448888c8cd54c03c480048d4d5411800488cd54124008ccd4d5402c0048004880048004ccd554018014008004c8cd40054109410c488cc008cd5411c014010004444888ccd54c0104800540fccd54c030480048d4d5410c00488cd54118008d5402c004ccd54c0104800488d4d54110008894cd4c160ccd54c06048004d4034cd403c894cd4c168008417040041648d4d5411c00488cc028008014018400c4cd410c01000d4100004cd54c030480048d4d5410c00488c8cd5411c00cc004014c8004d54184894cd4d410c0044d5402c00c884d4d54124008894cd4c174cc0300080204cd5404001c0044c01800c008c8004d5416888448894cd4d40fc0044008884cc014008ccd54c01c480040140100044484888c00c01044884888cc0080140104484888c00401044800448cd404888ccd4d401000c88008008004d4d40080048800448848cc00400c00848004c8004d541488844894cd4d40d8004540e0884cd40e4c010008cd54c018480040100044448888cccd54011403c00c0040084488cd54008c8cd403c88ccd4d401800c88008008004d4d401000488004cd401005012800448848cc00400c008480044488c0080048d4c08c00488800cc8004d5412c88c8c94cd4c114cc0a14009200213300c300433530081200150010033004335300d12001500100310031332233706004002a002900209999aa9801890009919a80511199a8040018008011a802800a8039119b800014800800520003200135504a221122253353502f00113500600322133350090053004002333553007120010050040011235350050012200112353500400122002320013550472212253353041333573466e2400920000430421502d153353502b0011502d22133502e002335300612001337020089001000899a801111180198010009000891091980080180109000990009aa821911299a9a81300108009109a980a801111a982180111299a9a8160038a99a9a8160038804110b1109a980d801111a982480111299a9824199ab9a33720010004094092266a0660186601e01601a2a66a6090666ae68cdc8804001024825099a819803198078070028a99a982419809003800899a81980619807805806899a81980319807807002990009aa82111091299a9a8130008a814110a99a981f19804002240002006266a600c240026600e00890010009119b8100200122333573466e240080040e80e4488cc00ccc01cc018008c018004cc894cd4d40c0008854cd4d40c400884cd4c0b80088cd4c0bc0088cc034008004888100888cd4c0c401081008894cd4c104ccd5cd19b87006003043042153353041333573466e1c01400810c1084cc0380100044108410840ec54cd4d40c0004840ec40ecc014008c014004894cd4c0d8008400440dc88ccd5cd19b8700200103703623530240012200123530230012200222335302d0022335302e00223300500200120352335302e002203523300500200122333573466e3c0080040cc0c8c8004d540e08844894cd4d407000454078884cd407cc010008cd54c018480040100048848cc00400c0088004888888888848cccccccccc00402c02802402001c01801401000c00880048848cc00400c0088004848c004008800448800848800480048c8c8cccd5cd19b8735573aa004900011981519191999ab9a3370e6aae75400520002375c6ae84d55cf280111931a981899ab9c02703203002f137540026ae854008dd69aba135744a004464c6a605c66ae700900bc0b40b04d55cf280089baa00123232323333573466e1cd55cea801a4000466600e602c6ae85400cccd5403dd719aa807bae75a6ae854008cd4071d71aba135744a004464c6a605c66ae700900bc0b40b04d5d1280089aab9e5001137540024442466600200800600440022464646666ae68cdc39aab9d5002480008cc88cc024008004dd71aba1500233500a232323333573466e1cd55cea80124000466446601e004002602c6ae854008ccd5403dd719aa809919299a981419805a800a40022a00226a05c921022d33001375a00266aa01eeb88c94cd4c0a0cc02d400520001500113502e491022d32001375a0026ae84d5d1280111931a981719ab9c02402f02d02c135573ca00226ea8004d5d09aba25002232635302a33573804005605205026aae7940044dd500091199ab9a33712004002040042442466002006004400244246600200600440022464460046eb0004c8004d5408c88cccd55cf80092804119a80398021aba1002300335744004048224464460046eac004c8004d5408c88c8cccd55cf80112804919a80419aa80618031aab9d5002300535573ca00460086ae8800c0944d5d0800889100109109119800802001890008891119191999ab9a3370e6aae754009200023355008300635742a004600a6ae84d5d1280111931a981099ab9c01702202001f135573ca00226ea8004448848cc00400c0084480048c8c8cccd5cd19b8735573aa004900011980318071aba1500233500a2323232323333573466e1d40052002233300e375a6ae854010dd69aba15003375a6ae84d5d1280191999ab9a3370ea004900011808180a9aba135573ca00c464c6a604666ae700640900880840804d55cea80189aba25001135573ca00226ea8004d5d09aba25002232635301c33573802403a03603426aae7940044dd5000910919800801801100090911801001911091199800802802001900089119191999ab9a3370ea002900011a80418029aba135573ca00646666ae68cdc3a801240044a010464c6a603066ae7003806405c0580544d55cea80089baa001121223002003112200112001232323333573466e1d4005200223006375c6ae84d55cf280191999ab9a3370ea0049000118041bae357426aae7940108c98d4c04ccd5ce00480a00900880809aab9d50011375400242446004006424460020064002921035054310012253353003333573466e3cd4c01800888008d4c018004880080140104ccd5cd19b873530060022200135300600122001005004100412200212200120012212330010030022001235002490101310012326353003335738002008004930900090008891918008009119801980100100081049fd8799f581ceda33318624ade03d53b7e954713d9e69440891f0d02e823267b610d9fd8799fd8799fd8799f581cdf8e55e5c9525c9b0e0c35327881c5942bcdd133c94697e509d5bbdeffd8799fd8799fd8799f581c4d2b542a5a7d191dfc6c8bf5d207a61abf570de04a561b43e1cab7cdffffffffa140d8799f00a1401a007a1200ffffd8799fd8799fd8799f581c70e60f3b5ea7153e0acc7a803e4401d44b8ed1bae1c7baaad1a62a72ffd8799fd8799fd8799f581c1e78aae7c90cc36d624f7b3bb6d86b52696dc84e490f343eba89005fffffffffa140d8799f00a1401a00186a00ffffd8799fd8799fd8799f581ceda33318624ade03d53b7e954713d9e69440891f0d02e823267b610dffd8799fd8799fd8799f581c6018dc6c7989a46ec26822425a3d2bac60eec2682a022740361ed957ffffffffa140d8799f00a1401a04323800ffffffffff0581840000d87980821a0015b7081a23115593f5ac007840643837393966353831636564613333333138363234616465303364353362376539353437313364396536393434303839316630643032653832333236376236310178403064396664383739396664383739396664383739396635383163646638653535653563393532356339623065306333353332373838316335393432626364643102784033336339343639376535303964356262646566666438373939666438373939666438373939663538316334643262353432613561376431393164666336633862037840663564323037613631616266353730646530346135363162343365316361623763646666666666666666613134306438373939663030613134303161303037380478403862363066666666643837393966643837393966643837393966353831633730653630663362356561373135336530616363376138303365343430316434346205784038656431626165316337626161616431613632613732666664383739396664383739396664383739396635383163316537386161653763393063633336643632067840346637623362623664383662353236393664633834653439306633343365626138393030356666666666666666666131343064383739396630306131343031610778403030313831626530666666666438373939666438373939666438373939663538316365646133333331383632346164653033643533623765393534373133643908784065363934343038393166306430326538323332363762363130646666643837393966643837393966643837393966353831633630313864633663373938396134097840366563323638323234323561336432626163363065656332363832613032323734303336316564393537666666666666666661313430643837393966303061310a743430316130343234636138306666666666666666181e6132 \ No newline at end of file diff --git a/test_data/babbage7.0.address b/test_data/babbage7.0.address new file mode 100644 index 00000000..6f157e60 --- /dev/null +++ b/test_data/babbage7.0.address @@ -0,0 +1 @@ +119068A7A3F008803EDAC87AF1619860F2CDCDE40C26987325ACE138AD81728E7ED4CF324E1323135E7E6D931F01E30792D9CDF17129CB806D \ No newline at end of file diff --git a/test_data/babbage7.1.address b/test_data/babbage7.1.address new file mode 100644 index 00000000..f1c415ad --- /dev/null +++ b/test_data/babbage7.1.address @@ -0,0 +1 @@ +01A7D37F1D43D1197A994D95B3CE15D9AF3B4697CC7CDF9BCD1F81688D3499AC08066B36BC6C2D86A21243B940E84DBE5CAC3FAB5F76AB9229 \ No newline at end of file diff --git a/test_data/babbage7.collateral.address b/test_data/babbage7.collateral.address new file mode 100644 index 00000000..4a2ce90a --- /dev/null +++ b/test_data/babbage7.collateral.address @@ -0,0 +1 @@ +01a7d37f1d43d1197a994d95b3ce15d9af3b4697cc7cdf9bcd1f81688d3499ac08066b36bc6c2d86a21243b940e84dbe5cac3fab5f76ab9229 \ No newline at end of file diff --git a/test_data/babbage7.reference.address b/test_data/babbage7.reference.address new file mode 100644 index 00000000..c949983e --- /dev/null +++ b/test_data/babbage7.reference.address @@ -0,0 +1 @@ +119068a7a3f008803edac87af1619860f2cdcde40c26987325ace138ad81728e7ed4cf324e1323135e7e6d931f01e30792d9cdf17129cb806d \ No newline at end of file diff --git a/test_data/babbage7.tx b/test_data/babbage7.tx new file mode 100644 index 00000000..f57f9748 --- /dev/null +++ b/test_data/babbage7.tx @@ -0,0 +1 @@ +84a90082825820806560bebbd9ba3759d091efff97f007ce82fad9bc53d1d8a6d44f8760d6f76100825820a349f9146f6c638505e3dbd2625af1200dbaf4fd17bb8513bb966db70147e30d04018682583901f434b2c9818daa419e631a520e6bb98437add6c658fee52c1e07a20d6e4af7adcff67f1c1e7e5805b7c9155125092dd5dee927dd43ec64301a000f32a08258390170e60f3b5ea7153e0acc7a803e4401d44b8ed1bae1c7baaad1a62a721e78aae7c90cc36d624f7b3bb6d86b52696dc84e490f343eba89005f1a000f32a0825839011a5bd6e94501aa6b9a36a221b4dec2ef7ec007c99dcb7c54416428abbe45266367e628fb1ce04d4dee752dbf2e79ad91d22437b400cec0ae1a0098b5c082583901a7d37f1d43d1197a994d95b3ce15d9af3b4697cc7cdf9bcd1f81688d3499ac08066b36bc6c2d86a21243b940e84dbe5cac3fab5f76ab9229821a0011e360a1581c95ab9a125c900c14cf7d39093e3577b0c8e39c9f7548a8301a28ee2da14c4164614964696f74313132350182583901a7d37f1d43d1197a994d95b3ce15d9af3b4697cc7cdf9bcd1f81688d3499ac08066b36bc6c2d86a21243b940e84dbe5cac3fab5f76ab92291a068cc3d782583901a7d37f1d43d1197a994d95b3ce15d9af3b4697cc7cdf9bcd1f81688d3499ac08066b36bc6c2d86a21243b940e84dbe5cac3fab5f76ab92291a0686852e021a00063ea9031a04b263ec081a04b259600b58200c38855f28468901ccd7d2537b7b9438d84573f1d69fa43271a54b645fbbb4b00d81825820d53715d1b45852588d554b4fb14d78132e0c0022141fd30fe07a305c45f93827000e81581ca7d37f1d43d1197a994d95b3ce15d9af3b4697cc7cdf9bcd1f81688d12818258209a32459bd4ef6bbafdeb8cf3b909d0e3e2ec806e4cc6268529280b0fc1d06f5b00a300818258204bdb6862c2c613165eecf49a460d5abb885c258d95cd5bc3e867693a2f5a862b58405816ff0da3d3c152f5cc8f301b05ac54233f1a3ec9351e267150677523ce1bb55d69b1a17ee3e170fe2d2ea9b142c4190e192c9de3859e610d14359aa137d60c049fd8799f581c1a5bd6e94501aa6b9a36a221b4dec2ef7ec007c99dcb7c54416428ab9fd8799fd8799fd8799f581cf434b2c9818daa419e631a520e6bb98437add6c658fee52c1e07a20dffd8799fd8799fd8799f581c6e4af7adcff67f1c1e7e5805b7c9155125092dd5dee927dd43ec6430ffffffffa140d8799f00a1401a000f32a0ffffd8799fd8799fd8799f581c70e60f3b5ea7153e0acc7a803e4401d44b8ed1bae1c7baaad1a62a72ffd8799fd8799fd8799f581c1e78aae7c90cc36d624f7b3bb6d86b52696dc84e490f343eba89005fffffffffa140d8799f00a1401a000f32a0ffffd8799fd8799fd8799f581c1a5bd6e94501aa6b9a36a221b4dec2ef7ec007c99dcb7c54416428abffd8799fd8799fd8799f581cbe45266367e628fb1ce04d4dee752dbf2e79ad91d22437b400cec0aeffffffffa140d8799f00a1401a0098b5c0ffffffffff0581840000d87a80821a0027e6981a2bc017a9f5f6 \ No newline at end of file