diff --git a/pallas-applying/src/alonzo.rs b/pallas-applying/src/alonzo.rs index 8de22ddfc..a3da8bdcd 100644 --- a/pallas-applying/src/alonzo.rs +++ b/pallas-applying/src/alonzo.rs @@ -242,32 +242,37 @@ fn check_collaterals_assets( prot_pps: &AlonzoProtParams, ) -> ValidationResult { let fee_percentage: u64 = tx_body.fee * prot_pps.collateral_percent; - for input in tx_body.inputs.iter() { - match utxos.get(&MultiEraInput::from_alonzo_compatible(input)) { - Some(multi_era_output) => match MultiEraOutput::as_alonzo(multi_era_output) { - Some(TransactionOutput { - amount: Value::Coin(n), - .. - }) => { - if *n * 100 < fee_percentage { - return Err(Alonzo(CollateralMinLovelace)); - } - } - Some(TransactionOutput { - amount: Value::Multiasset(n, multi_assets), - .. - }) => { - if *n * 100 < fee_percentage { - return Err(Alonzo(CollateralMinLovelace)); - } - if !multi_assets.is_empty() { - return Err(Alonzo(NonLovelaceCollateral)); - } + match &tx_body.collateral { + Some(collaterals) => { + for collateral in collaterals { + match utxos.get(&MultiEraInput::from_alonzo_compatible(collateral)) { + Some(multi_era_output) => match MultiEraOutput::as_alonzo(multi_era_output) { + Some(TransactionOutput { + amount: Value::Coin(n), + .. + }) => { + if *n * 100 < fee_percentage { + return Err(Alonzo(CollateralMinLovelace)); + } + } + Some(TransactionOutput { + amount: Value::Multiasset(n, multi_assets), + .. + }) => { + if *n * 100 < fee_percentage { + return Err(Alonzo(CollateralMinLovelace)); + } + if !multi_assets.is_empty() { + return Err(Alonzo(NonLovelaceCollateral)); + } + } + None => (), + }, + None => return Err(Alonzo(CollateralNotInUTxO)), } - None => (), - }, - None => return Err(Alonzo(CollateralNotInUTxO)), + } } + None => return Err(Alonzo(CollateralMissing)), } Ok(()) } diff --git a/pallas-applying/tests/alonzo.rs b/pallas-applying/tests/alonzo.rs index 951bbe8d5..ec63bb7be 100644 --- a/pallas-applying/tests/alonzo.rs +++ b/pallas-applying/tests/alonzo.rs @@ -1,7 +1,7 @@ pub mod common; use common::*; -use pallas_addresses::{Address, Network, ShelleyAddress}; +use pallas_addresses::{Address, Network, ShelleyAddress, ShelleyPaymentPart}; use pallas_applying::{ utils::{ AlonzoError::*, AlonzoProtParams, Environment, FeePolicy, Language, MultiEraProtParams, @@ -14,10 +14,12 @@ use pallas_codec::{ decode::{Decode, Decoder}, encode, }, - utils::Bytes, + utils::{Bytes, KeyValuePairs}, }; +use pallas_crypto::hash::Hash; use pallas_primitives::alonzo::{MintedTx, NetworkId, TransactionBody, TransactionOutput, Value}; -use pallas_traverse::{Era, MultiEraTx}; +use pallas_traverse::{Era, MultiEraInput, MultiEraOutput, MultiEraTx}; +use std::borrow::Cow; #[cfg(test)] mod alonzo_tests { @@ -76,36 +78,62 @@ mod alonzo_tests { &mtx.transaction_body, &[ ( + // (tx hash, tx output index): + // (117325a52d60be3a1e4072af39d9e630bf61ce59d315d6c1bf4c4d140f8066ea, 0) String::from(include_str!("../../test_data/alonzo2.0.address")), - Value::Coin(1724100), + Value::Multiasset( + 1724100, + KeyValuePairs::from(Vec::from([( + Hash::<28>::new([ + 176, 1, 7, 107, 52, 168, 126, 125, 72, 236, 70, 112, 58, 111, 80, + 249, 50, 137, 88, 42, 217, 189, 190, 255, 127, 30, 50, 149, + ]), + KeyValuePairs::from(Vec::from([( + Bytes::from(hex::decode("4879706562656173747332343233").unwrap()), + 1, + )])), + )])), + ), None, ), ( + // (tx hash, tx output index): + // (d2f9764fa93ae5bcabbb65c7a2f97d1e31188064ae3d2ba1462114453928dd99, 0) String::from(include_str!("../../test_data/alonzo2.1.address")), Value::Coin(20292207), None, ), ( + // (tx hash, tx output index): + // (9fab354c2825376a943e505d13a3861e4d9ad3e177028d7bb2bbabce5453fa11, 0) String::from(include_str!("../../test_data/alonzo2.2.address")), Value::Coin(20292207), None, ), ( + // (tx hash, tx output index): + // (3077a999b1d22cb1a4e5ee485adbde6a4596704a96384fbc9727028b8b28ba47, 0) String::from(include_str!("../../test_data/alonzo2.3.address")), Value::Coin(29792207), None, ), ( + // (tx hash, tx output index): + // (b231aca45a38add7378d2ed7a0822626fee3396821e8791a5af5926807db962d, 0) String::from(include_str!("../../test_data/alonzo2.4.address")), Value::Coin(29792207), None, ), ( + // (tx hash, tx output index): + // (11579a841b3c7a64aa057c9adf993ef42520570450499b0a724c7ef706b2a435, 0) String::from(include_str!("../../test_data/alonzo2.5.address")), Value::Coin(61233231), None, ), ( + // (tx hash, tx output index): + // (b857f98162b753d117464c499d53bbbfec5aa38b94bd624e295a7e3fddc77130, 0) String::from(include_str!("../../test_data/alonzo2.6.address")), Value::Coin(20292207), None, @@ -398,6 +426,613 @@ mod alonzo_tests { } } + #[test] + // Same as successful_mainnet_tx_with_plutus_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/alonzo2.tx")); + let mut mtx: MintedTx = minted_tx_from_cbor(&cbor_bytes); + let mut utxos: UTxOs = mk_utxo_for_alonzo_compatible_tx( + &mtx.transaction_body, + &[ + ( + // (tx hash, tx output index): + // (117325a52d60be3a1e4072af39d9e630bf61ce59d315d6c1bf4c4d140f8066ea, 0) + String::from(include_str!("../../test_data/alonzo2.0.address")), + Value::Multiasset( + 1724100, + KeyValuePairs::from(Vec::from([( + Hash::<28>::new([ + 176, 1, 7, 107, 52, 168, 126, 125, 72, 236, 70, 112, 58, 111, 80, + 249, 50, 137, 88, 42, 217, 189, 190, 255, 127, 30, 50, 149, + ]), + KeyValuePairs::from(Vec::from([( + Bytes::from(hex::decode("4879706562656173747332343233").unwrap()), + 1, + )])), + )])), + ), + None, + ), + ( + // (tx hash, tx output index): + // (d2f9764fa93ae5bcabbb65c7a2f97d1e31188064ae3d2ba1462114453928dd99, 0) + String::from(include_str!("../../test_data/alonzo2.1.address")), + Value::Coin(20292207), + None, + ), + ( + // (tx hash, tx output index): + // (9fab354c2825376a943e505d13a3861e4d9ad3e177028d7bb2bbabce5453fa11, 0) + String::from(include_str!("../../test_data/alonzo2.2.address")), + Value::Coin(20292207), + None, + ), + ( + // (tx hash, tx output index): + // (3077a999b1d22cb1a4e5ee485adbde6a4596704a96384fbc9727028b8b28ba47, 0) + String::from(include_str!("../../test_data/alonzo2.3.address")), + Value::Coin(29792207), + None, + ), + ( + // (tx hash, tx output index): + // (b231aca45a38add7378d2ed7a0822626fee3396821e8791a5af5926807db962d, 0) + String::from(include_str!("../../test_data/alonzo2.4.address")), + Value::Coin(29792207), + None, + ), + ( + // (tx hash, tx output index): + // (11579a841b3c7a64aa057c9adf993ef42520570450499b0a724c7ef706b2a435, 0) + String::from(include_str!("../../test_data/alonzo2.5.address")), + Value::Coin(61233231), + None, + ), + ( + // (tx hash, tx output index): + // (b857f98162b753d117464c499d53bbbfec5aa38b94bd624e295a7e3fddc77130, 0) + String::from(include_str!("../../test_data/alonzo2.6.address")), + Value::Coin(20292207), + None, + ), + ], + ); + add_collateral( + &mtx.transaction_body, + &mut utxos, + &[( + String::from(include_str!("../../test_data/alonzo2.collateral.address")), + Value::Coin(5000000), + None, + )], + ); + let mut tx_body: TransactionBody = (*mtx.transaction_body).clone(); + tx_body.collateral = None; + let mut tx_buf: Vec = Vec::new(); + match encode(tx_body, &mut tx_buf) { + Ok(_) => (), + Err(err) => assert!(false, "Unable to encode Tx ({:?})", err), + }; + mtx.transaction_body = + Decode::decode(&mut Decoder::new(&tx_buf.as_slice()), &mut ()).unwrap(); + let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Alonzo); + let env: Environment = Environment { + prot_params: MultiEraProtParams::Alonzo(AlonzoProtParams { + fee_policy: FeePolicy { + summand: 155381, + multiplier: 44, + }, + max_tx_size: 16384, + languages: vec![Language::PlutusV1, Language::PlutusV2], + max_block_ex_mem: 50000000, + max_block_ex_steps: 40000000000, + max_tx_ex_mem: 10000000, + max_tx_ex_steps: 10000000000, + max_val_size: 5000, + collateral_percent: 150, + max_collateral_inputs: 3, + coints_per_utxo_word: 34482, + }), + prot_magic: 764824073, + block_slot: 58924928, + network_id: 1, + }; + match validate(&metx, &utxos, &env) { + Ok(()) => assert!(false, "No collateral inputs"), + Err(err) => match err { + Alonzo(CollateralMissing) => (), + _ => assert!(false, "Unexpected error ({:?})", err), + }, + } + } + + #[test] + // Same as successful_mainnet_tx_with_plutus_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/alonzo2.tx")); + let mtx: MintedTx = minted_tx_from_cbor(&cbor_bytes); + let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Alonzo); + let mut utxos: UTxOs = mk_utxo_for_alonzo_compatible_tx( + &mtx.transaction_body, + &[ + ( + // (tx hash, tx output index): + // (117325a52d60be3a1e4072af39d9e630bf61ce59d315d6c1bf4c4d140f8066ea, 0) + String::from(include_str!("../../test_data/alonzo2.0.address")), + Value::Multiasset( + 1724100, + KeyValuePairs::from(Vec::from([( + Hash::<28>::new([ + 176, 1, 7, 107, 52, 168, 126, 125, 72, 236, 70, 112, 58, 111, 80, + 249, 50, 137, 88, 42, 217, 189, 190, 255, 127, 30, 50, 149, + ]), + KeyValuePairs::from(Vec::from([( + Bytes::from(hex::decode("4879706562656173747332343233").unwrap()), + 1, + )])), + )])), + ), + None, + ), + ( + // (tx hash, tx output index): + // (d2f9764fa93ae5bcabbb65c7a2f97d1e31188064ae3d2ba1462114453928dd99, 0) + String::from(include_str!("../../test_data/alonzo2.1.address")), + Value::Coin(20292207), + None, + ), + ( + // (tx hash, tx output index): + // (9fab354c2825376a943e505d13a3861e4d9ad3e177028d7bb2bbabce5453fa11, 0) + String::from(include_str!("../../test_data/alonzo2.2.address")), + Value::Coin(20292207), + None, + ), + ( + // (tx hash, tx output index): + // (3077a999b1d22cb1a4e5ee485adbde6a4596704a96384fbc9727028b8b28ba47, 0) + String::from(include_str!("../../test_data/alonzo2.3.address")), + Value::Coin(29792207), + None, + ), + ( + // (tx hash, tx output index): + // (b231aca45a38add7378d2ed7a0822626fee3396821e8791a5af5926807db962d, 0) + String::from(include_str!("../../test_data/alonzo2.4.address")), + Value::Coin(29792207), + None, + ), + ( + // (tx hash, tx output index): + // (11579a841b3c7a64aa057c9adf993ef42520570450499b0a724c7ef706b2a435, 0) + String::from(include_str!("../../test_data/alonzo2.5.address")), + Value::Coin(61233231), + None, + ), + ( + // (tx hash, tx output index): + // (b857f98162b753d117464c499d53bbbfec5aa38b94bd624e295a7e3fddc77130, 0) + String::from(include_str!("../../test_data/alonzo2.6.address")), + Value::Coin(20292207), + None, + ), + ], + ); + add_collateral( + &mtx.transaction_body, + &mut utxos, + &[( + String::from(include_str!("../../test_data/alonzo2.collateral.address")), + Value::Coin(5000000), + None, + )], + ); + let env: Environment = Environment { + prot_params: MultiEraProtParams::Alonzo(AlonzoProtParams { + fee_policy: FeePolicy { + summand: 155381, + multiplier: 44, + }, + max_tx_size: 16384, + languages: vec![Language::PlutusV1, Language::PlutusV2], + max_block_ex_mem: 50000000, + max_block_ex_steps: 40000000000, + max_tx_ex_mem: 10000000, + max_tx_ex_steps: 10000000000, + max_val_size: 5000, + collateral_percent: 150, + max_collateral_inputs: 0, // no collateral inputs are allowed + coints_per_utxo_word: 34482, + }), + prot_magic: 764824073, + block_slot: 58924928, + network_id: 1, + }; + match validate(&metx, &utxos, &env) { + Ok(()) => assert!(false, "Number of collateral inputs should be within limits"), + Err(err) => match err { + Alonzo(TooManyCollaterals) => (), + _ => assert!(false, "Unexpected error ({:?})", err), + }, + } + } + + #[test] + // Same as successful_mainnet_tx_with_plutus_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/alonzo2.tx")); + let mtx: MintedTx = minted_tx_from_cbor(&cbor_bytes); + let mut utxos: UTxOs = mk_utxo_for_alonzo_compatible_tx( + &mtx.transaction_body, + &[ + ( + // (tx hash, tx output index): + // (117325a52d60be3a1e4072af39d9e630bf61ce59d315d6c1bf4c4d140f8066ea, 0) + String::from(include_str!("../../test_data/alonzo2.0.address")), + Value::Multiasset( + 1724100, + KeyValuePairs::from(Vec::from([( + Hash::<28>::new([ + 176, 1, 7, 107, 52, 168, 126, 125, 72, 236, 70, 112, 58, 111, 80, + 249, 50, 137, 88, 42, 217, 189, 190, 255, 127, 30, 50, 149, + ]), + KeyValuePairs::from(Vec::from([( + Bytes::from(hex::decode("4879706562656173747332343233").unwrap()), + 1, + )])), + )])), + ), + None, + ), + ( + // (tx hash, tx output index): + // (d2f9764fa93ae5bcabbb65c7a2f97d1e31188064ae3d2ba1462114453928dd99, 0) + String::from(include_str!("../../test_data/alonzo2.1.address")), + Value::Coin(20292207), + None, + ), + ( + // (tx hash, tx output index): + // (9fab354c2825376a943e505d13a3861e4d9ad3e177028d7bb2bbabce5453fa11, 0) + String::from(include_str!("../../test_data/alonzo2.2.address")), + Value::Coin(20292207), + None, + ), + ( + // (tx hash, tx output index): + // (3077a999b1d22cb1a4e5ee485adbde6a4596704a96384fbc9727028b8b28ba47, 0) + String::from(include_str!("../../test_data/alonzo2.3.address")), + Value::Coin(29792207), + None, + ), + ( + // (tx hash, tx output index): + // (b231aca45a38add7378d2ed7a0822626fee3396821e8791a5af5926807db962d, 0) + String::from(include_str!("../../test_data/alonzo2.4.address")), + Value::Coin(29792207), + None, + ), + ( + // (tx hash, tx output index): + // (11579a841b3c7a64aa057c9adf993ef42520570450499b0a724c7ef706b2a435, 0) + String::from(include_str!("../../test_data/alonzo2.5.address")), + Value::Coin(61233231), + None, + ), + ( + // (tx hash, tx output index): + // (b857f98162b753d117464c499d53bbbfec5aa38b94bd624e295a7e3fddc77130, 0) + String::from(include_str!("../../test_data/alonzo2.6.address")), + Value::Coin(20292207), + None, + ), + ], + ); + let old_address: Address = match hex::decode(String::from(include_str!( + "../../test_data/alonzo2.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::AlonzoCompatible(Box::new(Cow::Owned(TransactionOutput { + address: Bytes::try_from(altered_address.to_hex()).unwrap(), + amount: Value::Coin(5000000), + datum_hash: None, + }))); + utxos.insert(multi_era_in, multi_era_out); + let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Alonzo); + let env: Environment = Environment { + prot_params: MultiEraProtParams::Alonzo(AlonzoProtParams { + fee_policy: FeePolicy { + summand: 155381, + multiplier: 44, + }, + max_tx_size: 16384, + languages: vec![Language::PlutusV1, Language::PlutusV2], + max_block_ex_mem: 50000000, + max_block_ex_steps: 40000000000, + max_tx_ex_mem: 10000000, + max_tx_ex_steps: 10000000000, + max_val_size: 5000, + collateral_percent: 150, + max_collateral_inputs: 3, + coints_per_utxo_word: 34482, + }), + prot_magic: 764824073, + block_slot: 58924928, + network_id: 1, + }; + match validate(&metx, &utxos, &env) { + Ok(()) => assert!(false, "Collateral inputs should be verification-key locked"), + Err(err) => match err { + Alonzo(CollateralNotVKeyLocked) => (), + _ => assert!(false, "Unexpected error ({:?})", err), + }, + } + } + + #[test] + // Same as sucessful_mainnet_tx_with_plutus_script, except that the output + // associated to the collateral input contains assets other than lovelace. + fn collateral_with_other_assets() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/alonzo2.tx")); + let mtx: MintedTx = minted_tx_from_cbor(&cbor_bytes); + let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Alonzo); + let mut utxos: UTxOs = mk_utxo_for_alonzo_compatible_tx( + &mtx.transaction_body, + &[ + ( + // (tx hash, tx output index): + // (117325a52d60be3a1e4072af39d9e630bf61ce59d315d6c1bf4c4d140f8066ea, 0) + String::from(include_str!("../../test_data/alonzo2.0.address")), + Value::Multiasset( + 1724100, + KeyValuePairs::from(Vec::from([( + Hash::<28>::new([ + 176, 1, 7, 107, 52, 168, 126, 125, 72, 236, 70, 112, 58, 111, 80, + 249, 50, 137, 88, 42, 217, 189, 190, 255, 127, 30, 50, 149, + ]), + KeyValuePairs::from(Vec::from([( + Bytes::from(hex::decode("4879706562656173747332343233").unwrap()), + 1, + )])), + )])), + ), + None, + ), + ( + // (tx hash, tx output index): + // (d2f9764fa93ae5bcabbb65c7a2f97d1e31188064ae3d2ba1462114453928dd99, 0) + String::from(include_str!("../../test_data/alonzo2.1.address")), + Value::Coin(20292207), + None, + ), + ( + // (tx hash, tx output index): + // (9fab354c2825376a943e505d13a3861e4d9ad3e177028d7bb2bbabce5453fa11, 0) + String::from(include_str!("../../test_data/alonzo2.2.address")), + Value::Coin(20292207), + None, + ), + ( + // (tx hash, tx output index): + // (3077a999b1d22cb1a4e5ee485adbde6a4596704a96384fbc9727028b8b28ba47, 0) + String::from(include_str!("../../test_data/alonzo2.3.address")), + Value::Coin(29792207), + None, + ), + ( + // (tx hash, tx output index): + // (b231aca45a38add7378d2ed7a0822626fee3396821e8791a5af5926807db962d, 0) + String::from(include_str!("../../test_data/alonzo2.4.address")), + Value::Coin(29792207), + None, + ), + ( + // (tx hash, tx output index): + // (11579a841b3c7a64aa057c9adf993ef42520570450499b0a724c7ef706b2a435, 0) + String::from(include_str!("../../test_data/alonzo2.5.address")), + Value::Coin(61233231), + None, + ), + ( + // (tx hash, tx output index): + // (b857f98162b753d117464c499d53bbbfec5aa38b94bd624e295a7e3fddc77130, 0) + String::from(include_str!("../../test_data/alonzo2.6.address")), + Value::Coin(20292207), + None, + ), + ], + ); + add_collateral( + &mtx.transaction_body, + &mut utxos, + &[( + String::from(include_str!("../../test_data/alonzo2.collateral.address")), + Value::Multiasset( + 5000000, + KeyValuePairs::from(Vec::from([( + Hash::<28>::new([ + 176, 1, 7, 107, 52, 168, 126, 125, 72, 236, 70, 112, 58, 111, 80, 249, + 50, 137, 88, 42, 217, 189, 190, 255, 127, 30, 50, 149, + ]), + KeyValuePairs::from(Vec::from([( + Bytes::from(hex::decode("4879706562656173747332343233").unwrap()), + 1000, + )])), + )])), + ), + None, + )], + ); + let env: Environment = Environment { + prot_params: MultiEraProtParams::Alonzo(AlonzoProtParams { + fee_policy: FeePolicy { + summand: 155381, + multiplier: 44, + }, + max_tx_size: 16384, + languages: vec![Language::PlutusV1, Language::PlutusV2], + max_block_ex_mem: 50000000, + max_block_ex_steps: 40000000000, + max_tx_ex_mem: 10000000, + max_tx_ex_steps: 10000000000, + max_val_size: 5000, + collateral_percent: 150, + max_collateral_inputs: 3, + coints_per_utxo_word: 34482, + }), + prot_magic: 764824073, + block_slot: 58924928, + network_id: 1, + }; + match validate(&metx, &utxos, &env) { + Ok(()) => assert!(false, "Collateral inputs should contain only lovelace"), + Err(err) => match err { + Alonzo(NonLovelaceCollateral) => (), + _ => assert!(false, "Unexpected error ({:?})", err), + }, + } + } + + #[test] + // Same as successful_mainnet_tx_with_plutus_script, except that the lovelace in + // the collateral input is insufficient. + fn collateral_without_min_lovelace() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/alonzo2.tx")); + let mtx: MintedTx = minted_tx_from_cbor(&cbor_bytes); + let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Alonzo); + let mut utxos: UTxOs = mk_utxo_for_alonzo_compatible_tx( + &mtx.transaction_body, + &[ + ( + // (tx hash, tx output index): + // (117325a52d60be3a1e4072af39d9e630bf61ce59d315d6c1bf4c4d140f8066ea, 0) + String::from(include_str!("../../test_data/alonzo2.0.address")), + Value::Multiasset( + 1724100, + KeyValuePairs::from(Vec::from([( + Hash::<28>::new([ + 176, 1, 7, 107, 52, 168, 126, 125, 72, 236, 70, 112, 58, 111, 80, + 249, 50, 137, 88, 42, 217, 189, 190, 255, 127, 30, 50, 149, + ]), + KeyValuePairs::from(Vec::from([( + Bytes::from(hex::decode("4879706562656173747332343233").unwrap()), + 1, + )])), + )])), + ), + None, + ), + ( + // (tx hash, tx output index): + // (d2f9764fa93ae5bcabbb65c7a2f97d1e31188064ae3d2ba1462114453928dd99, 0) + String::from(include_str!("../../test_data/alonzo2.1.address")), + Value::Coin(20292207), + None, + ), + ( + // (tx hash, tx output index): + // (9fab354c2825376a943e505d13a3861e4d9ad3e177028d7bb2bbabce5453fa11, 0) + String::from(include_str!("../../test_data/alonzo2.2.address")), + Value::Coin(20292207), + None, + ), + ( + // (tx hash, tx output index): + // (3077a999b1d22cb1a4e5ee485adbde6a4596704a96384fbc9727028b8b28ba47, 0) + String::from(include_str!("../../test_data/alonzo2.3.address")), + Value::Coin(29792207), + None, + ), + ( + // (tx hash, tx output index): + // (b231aca45a38add7378d2ed7a0822626fee3396821e8791a5af5926807db962d, 0) + String::from(include_str!("../../test_data/alonzo2.4.address")), + Value::Coin(29792207), + None, + ), + ( + // (tx hash, tx output index): + // (11579a841b3c7a64aa057c9adf993ef42520570450499b0a724c7ef706b2a435, 0) + String::from(include_str!("../../test_data/alonzo2.5.address")), + Value::Coin(61233231), + None, + ), + ( + // (tx hash, tx output index): + // (b857f98162b753d117464c499d53bbbfec5aa38b94bd624e295a7e3fddc77130, 0) + String::from(include_str!("../../test_data/alonzo2.6.address")), + Value::Coin(20292207), + None, + ), + ], + ); + add_collateral( + &mtx.transaction_body, + &mut utxos, + &[( + String::from(include_str!("../../test_data/alonzo2.collateral.address")), + Value::Coin(5000000), + None, + )], + ); + let env: Environment = Environment { + prot_params: MultiEraProtParams::Alonzo(AlonzoProtParams { + fee_policy: FeePolicy { + summand: 155381, + multiplier: 44, + }, + max_tx_size: 16384, + languages: vec![Language::PlutusV1, Language::PlutusV2], + max_block_ex_mem: 50000000, + max_block_ex_steps: 40000000000, + max_tx_ex_mem: 10000000, + max_tx_ex_steps: 10000000000, + max_val_size: 5000, + collateral_percent: 700, + max_collateral_inputs: 3, + coints_per_utxo_word: 34482, + }), + prot_magic: 764824073, + block_slot: 58924928, + network_id: 1, + }; + match validate(&metx, &utxos, &env) { + Ok(()) => assert!( + false, + "Collateral inputs should contain the minimum lovelace" + ), + Err(err) => match err { + Alonzo(CollateralMinLovelace) => (), + _ => assert!(false, "Unexpected error ({:?})", err), + }, + } + } + #[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.