From e7cf70dbdb1809673ce63b817a9aeee97fe11d79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20S=C3=A1nchez=20Terraf?= Date: Fri, 23 Aug 2024 11:55:35 -0300 Subject: [PATCH 1/8] (feat) check certificates and native scripts --- pallas-applying/src/shelley_ma.rs | 378 +++++++++++++++++++++- pallas-applying/src/utils.rs | 74 ++++- pallas-applying/src/utils/validation.rs | 13 + pallas-applying/tests/README.md | 17 +- pallas-applying/tests/shelley_ma.rs | 401 ++++++++++++++++++++++++ pallas-primitives/src/alonzo/model.rs | 2 +- test_data/allegra1.tx | 1 + test_data/mary2.tx | 1 + test_data/mary3.tx | 1 + test_data/shelley4.tx | 1 + 10 files changed, 869 insertions(+), 20 deletions(-) create mode 100644 test_data/allegra1.tx create mode 100644 test_data/mary2.tx create mode 100644 test_data/mary3.tx create mode 100644 test_data/shelley4.tx diff --git a/pallas-applying/src/shelley_ma.rs b/pallas-applying/src/shelley_ma.rs index dbae94e0..0764fe9f 100644 --- a/pallas-applying/src/shelley_ma.rs +++ b/pallas-applying/src/shelley_ma.rs @@ -8,18 +8,27 @@ use crate::utils::{ ShelleyProtParams, UTxOs, ValidationError::{self, *}, ValidationResult, + CertPointer, DState, PState, PoolParam, CertState, AccountState }; use pallas_addresses::{PaymentKeyHash, ScriptHash, ShelleyAddress, ShelleyPaymentPart}; use pallas_codec::minicbor::encode; use pallas_primitives::{ alonzo::{ MintedTx, MintedWitnessSet, NativeScript, PolicyId, TransactionBody, TransactionOutput, - VKeyWitness, Value, + VKeyWitness, Value, StakeCredential::{self, *}, PoolKeyhash, Epoch, Coin, TransactionIndex, + Certificate::{self, *}, Genesishash, GenesisDelegateHash, VrfKeyhash, + MoveInstantaneousReward, InstantaneousRewardSource::*, InstantaneousRewardTarget::*, }, byron::TxOut, }; -use pallas_traverse::{ComputeHash, Era, MultiEraInput, MultiEraOutput}; -use std::{cmp::max, ops::Deref}; +use pallas_traverse::{ + ComputeHash, Era, MultiEraInput, MultiEraOutput, time::Slot, + wellknown::GenesisValues, +}; +use std::{cmp::max, ops::Deref, collections::HashMap}; +use pallas_crypto::hash::Hasher as PallasHasher; +use pallas_crypto::hash::Hash; // TODO: remove when fixed missing args +use std::str::FromStr; // TODO: remove when fixed missing args pub fn validate_shelley_ma_tx( mtx: &MintedTx, @@ -32,12 +41,27 @@ pub fn validate_shelley_ma_tx( let tx_body: &TransactionBody = &mtx.transaction_body; let tx_wits: &MintedWitnessSet = &mtx.transaction_witness_set; let size: u32 = get_alonzo_comp_tx_size(mtx); + let stk_dep_count: &mut u64 = &mut 0; // count of key registrations (for deposits) + let stk_refund_count: &mut u64 = &mut 0; // count of key deregs (for refunds) + let pool_count: &mut u64 = &mut 0; // count of pool regs (for deposits) + + // FIXME: This section is entirely made up + let stab_win = 129600; // FIXME: Found as "1.5 days" in unreliable sources. + let tx_ix: TransactionIndex = 0; // should be an argument + let acnt = AccountState { treasury: 261_254_564_000_000, reserves: 0 }; // should be an argument + let mut cert_state: CertState = CertState::default(); // should be an argument + let hash = Hash::from_str("FB2B631DB76384F64DD94B47F97FC8C2A206764C17A1DE7DA2F70E83").unwrap(); + cert_state.dstate.rewards.insert(AddrKeyhash(hash),0); + check_ins_not_empty(tx_body)?; check_ins_in_utxos(tx_body, utxos)?; check_ttl(tx_body, block_slot)?; check_tx_size(&size, prot_pps)?; check_min_lovelace(tx_body, prot_pps, era)?; - check_preservation_of_value(tx_body, utxos, era)?; + check_certificates(&tx_body.certificates, tx_ix, &mut cert_state, stk_dep_count, + stk_refund_count, pool_count, &acnt, block_slot, &stab_win, prot_pps)?; + check_preservation_of_value(tx_body, utxos, stk_dep_count, stk_refund_count, + pool_count, era, prot_pps)?; check_fees(tx_body, &size, prot_pps)?; check_network_id(tx_body, network_id)?; check_metadata(tx_body, mtx)?; @@ -115,25 +139,27 @@ fn compute_min_lovelace(output: &TransactionOutput, prot_pps: &ShelleyProtParams fn check_preservation_of_value( tx_body: &TransactionBody, utxos: &UTxOs, + stk_dep_count: &u64, + stk_refund_count: &u64, + pool_count: &u64, era: &Era, + prot_pps: &ShelleyProtParams, ) -> ValidationResult { - let neg_val_err: ValidationError = ShelleyMA(NegativeValue); - let input: Value = get_consumed(tx_body, utxos, era)?; - let produced: Value = get_produced(tx_body, era)?; - let output: Value = add_values(&produced, &Value::Coin(tx_body.fee), &neg_val_err)?; - if let Some(m) = &tx_body.mint { - add_minted_value(&output, m, &neg_val_err)?; + let consumed: Value = get_consumed(tx_body, utxos, stk_refund_count, era, prot_pps)?; + let produced: Value = get_produced(tx_body, stk_dep_count, pool_count, era, prot_pps)?; + if !values_are_equal(&consumed, &produced) { + Err(ShelleyMA(PreservationOfValue)) + } else { + Ok(()) } - if !values_are_equal(&input, &output) { - return Err(ShelleyMA(PreservationOfValue)); - } - Ok(()) } fn get_consumed( tx_body: &TransactionBody, utxos: &UTxOs, + stk_refund_count: &u64, era: &Era, + prot_pps: &ShelleyProtParams, ) -> Result { let neg_val_err: ValidationError = ShelleyMA(NegativeValue); let mut res: Value = empty_value(); @@ -155,10 +181,22 @@ fn get_consumed( }, } } + // TODO: Set right error message below. + // Adding key refunds and minted assets + res = add_values(&res, &Value::Coin(prot_pps.key_deposit * *stk_refund_count), &neg_val_err)?; + if let Some(m) = &tx_body.mint { + res = add_minted_value(&res, m, &neg_val_err)?; + } Ok(res) } -fn get_produced(tx_body: &TransactionBody, era: &Era) -> Result { +fn get_produced( + tx_body: &TransactionBody, + stk_dep_count: &u64, + pool_count: &u64, + era: &Era, + prot_pps: &ShelleyProtParams, +) -> Result { let neg_val_err: ValidationError = ShelleyMA(NegativeValue); let mut res: Value = empty_value(); for TransactionOutput { amount, .. } in tx_body.outputs.iter() { @@ -168,6 +206,12 @@ fn get_produced(tx_body: &TransactionBody, era: &Era) -> Result res = add_values(&res, amount, &neg_val_err)?, } } + // TODO: Set right error message below. + // Adding fees + res = add_values(&res, &Value::Coin(tx_body.fee), &neg_val_err)?; + // Pool reg deposits and staking key registrations + let total_deposits = prot_pps.pool_deposit * (*pool_count + *stk_dep_count); + res = add_values(&res, &Value::Coin(total_deposits), &neg_val_err)?; Ok(res) } @@ -220,6 +264,10 @@ fn check_witnesses( let vk_wits: &mut Vec<(bool, VKeyWitness)> = &mut mk_alonzo_vk_wits_check_list(&tx_wits.vkeywitness, ShelleyMA(MissingVKWitness))?; let tx_hash: &Vec = &Vec::from(tx_body.compute_hash().as_ref()); + let native_scripts: Vec = match &tx_wits.native_script { + Some(scripts) => scripts.iter().map(|x| x.clone().unwrap()).collect(), + None => Vec::new(), + }; for input in tx_body.inputs.iter() { match utxos.get(&MultiEraInput::from_alonzo_compatible(input)) { Some(multi_era_output) => { @@ -243,6 +291,9 @@ fn check_witnesses( None => return Err(ShelleyMA(InputNotInUTxO)), } } + let vkey_wits = &vk_wits.iter().map(|bv| bv.clone().1).collect(); + check_native_scripts(vkey_wits, &native_scripts, + &tx_body.validity_interval_start, &tx_body.ttl)?; check_remaining_vk_wits(vk_wits, tx_hash) } @@ -330,3 +381,300 @@ fn compute_script_hash(script: &NativeScript) -> PolicyId { payload.insert(0, 0); pallas_crypto::hash::Hasher::<224>::hash(&payload) } + +// Checks all certificates in order, and counts the relevant ones for computing deposits. +fn check_certificates( + cert_opt: &Option>, + tx_ix: TransactionIndex, + cert_state: &mut CertState, + stk_dep_count: &mut u64, + stk_refund_count: &mut u64, + pool_count: &mut u64, + acnt: &AccountState, + slot: &Slot, + stab_win: &Slot, + prot_pps: &ShelleyProtParams, +) -> ValidationResult { + if let Some(certs) = cert_opt { + let genesis = &GenesisValues::mainnet(); + let cepoch: Epoch = to_epoch(genesis, slot); + let mpc: Coin = prot_pps.min_pool_cost; + let mut ptr = CertPointer { slot: *slot, tx_ix, cert_ix: 0, }; + for (ix, cert) in certs.iter().enumerate() { + match cert { + StakeRegistration(stc) => { + *stk_dep_count += 1; + check_stake_registration(stc, &ptr, &mut cert_state.dstate)?; + }, + StakeDeregistration(stc) => { + check_stake_deregistration(stc, &mut cert_state.dstate)?; + *stk_refund_count += 1; + }, + StakeDelegation(stc, pk) => { + check_stake_delegation(stc, pk, &mut cert_state.dstate, &cert_state.pstate)?; + }, + PoolRegistration { operator, vrf_keyhash, pledge, cost, margin, reward_account, + pool_owners, relays, pool_metadata } => + { + if !cert_state.pstate.pool_params.contains_key(&operator) { + *pool_count += 1; + } + let pool_param = PoolParam { vrf_keyhash: *vrf_keyhash, pledge: *pledge, + cost: *cost, margin: margin.clone(), + reward_account: reward_account.clone(), + pool_owners: pool_owners.clone(), + relays: relays.clone(), + pool_metadata: pool_metadata.clone() + }; + check_pool_reg_or_update(operator, &pool_param, + &mpc, &mut cert_state.pstate)?; + }, + PoolRetirement(pk, repoch) => { + check_pool_retirement(pk, repoch, &cepoch, &prot_pps.maximum_epoch, + &mut cert_state.pstate)?; + }, + GenesisKeyDelegation(gkh, dkh, vrf) => { + check_genesis_key_delegation(gkh, dkh, vrf, slot, stab_win, + &mut cert_state.dstate)?; + }, + MoveInstantaneousRewardsCert(mir) => { + check_mir(mir, slot, stab_win, &mut cert_state.dstate, acnt)?; + } + } + ptr.cert_ix = ix as u32; // FIXME: Careful here, `ix` is `usize` + } + Ok(()) + } else { + Ok(()) + } +} + + +fn check_stake_registration( + stc: &StakeCredential, + ptr: &CertPointer, + ds: &mut DState, +) -> ValidationResult { + insert_or_err(&mut ds.rewards, stc, &0_u64, ShelleyMA(KeyAlreadyRegistered))?; + insert_or_err(&mut ds.ptrs, ptr, stc, ShelleyMA(PointerInUse)) +} + +fn check_stake_deregistration( + stc: &StakeCredential, + ds: &mut DState, +) -> ValidationResult { + match ds.rewards.get(stc) { + None => Err(ShelleyMA(KeyNotRegistered)), + Some(0) => { + ds.ptrs.retain(|_, v| v != stc); + ds.delegations.remove(stc); + ds.rewards.remove(stc); + Ok(()) + }, + Some(_) => Err(ShelleyMA(RewardsNotNull)), + } +} + +fn check_stake_delegation( + stc: &StakeCredential, + pk: &PoolKeyhash, + ds: &mut DState, + ps: &PState, +) -> ValidationResult { + if !ps.pool_params.contains_key(pk) { + Err(ShelleyMA(PoolNotRegistered)) + } else if ds.rewards.contains_key(stc) { + ds.delegations.insert(stc.clone(), *pk); + Ok(()) + } else { + Err(ShelleyMA(KeyNotRegistered)) + } +} + +// Inserts a key-value pair if the key is not already in use, otherwise return +// the provided error. +fn insert_or_err( + map: &mut HashMap, + key: &K, + value: &V, + error: E, +) -> Result<(), E> where K: Eq, K: std::hash::Hash, K: Clone, V: Clone { + if map.contains_key(key) { + return Err(error); + } else { + map.insert(key.clone(), value.clone()); + Ok(()) + } +} + +fn check_pool_reg_or_update( + pool_hash: &PoolKeyhash, + pool_param: &PoolParam, + min_pool_cost: &Coin, + ps: &mut PState, +) -> ValidationResult { + if pool_param.cost < *min_pool_cost { + Err(ShelleyMA(PoolCostBelowMin)) + } else if ps.pool_params.contains_key(pool_hash) { + // Updating + ps.fut_pool_params.insert(pool_hash.clone(), (*pool_param).clone()); + ps.retiring.remove(&pool_hash); + Ok(()) + } else { + // Registering + ps.pool_params.insert(pool_hash.clone(), (*pool_param).clone()); + Ok(()) + } +} + +fn check_pool_retirement( + pool_hash: &PoolKeyhash, + repoch: &Epoch, + cepoch: &Epoch, + emax: &u32, + ps: &mut PState, +) -> ValidationResult { + if !ps.pool_params.contains_key(&pool_hash) { + return Err(ShelleyMA(PoolNotRegistered)); + } + if (*cepoch < *repoch) & (*repoch <= *cepoch + *emax as u64) { + ps.retiring.insert(pool_hash.clone(), *repoch); + Ok(()) + } else { + Err(ShelleyMA(PoolNotRegistered)) + } +} + +fn check_genesis_key_delegation( + gkh: &Genesishash, + dkh: &GenesisDelegateHash, // called `vkh` in specs + vrf: &VrfKeyhash, + slot: &Slot, + stab_win: &Slot, + ds: &mut DState, +) -> ValidationResult { + let cod = ds.gen_delegs + .iter().filter(|kv| kv.0 != gkh) + .map(|kv| kv.1).collect::>(); + let fod = ds.fut_gen_delegs + .iter().filter(|kv| kv.0.1 != *gkh) + .map(|kv| kv.1).collect::>(); + let curr_keyhashes = cod.iter().map(|v| v.0.clone()).collect::>(); + let curr_vrfs = cod.iter().map(|v| v.1).collect::>(); + let fut_keyhashes = fod.iter().map(|v| v.0.clone()).collect::>(); + let fut_vrfs = fod.iter().map(|v| v.1).collect::>(); + if curr_keyhashes.contains(dkh) | fut_keyhashes.contains(dkh) + | curr_vrfs.contains(vrf) | fut_vrfs.contains(vrf) { + Err(ShelleyMA(DuplicateGenesisDelegate)) + } else if !ds.gen_delegs.contains_key(gkh) { + Err(ShelleyMA(GenesisKeyNotInMapping)) + } else { + let gen_slot: Slot = *slot + *stab_win; + ds.fut_gen_delegs.insert((gen_slot, gkh.clone()), (dkh.clone(), vrf.clone())); + Ok(()) + } +} + +fn check_mir( + mir: &MoveInstantaneousReward, + slot: &Slot, + stab_win: &Slot, + ds: &mut DState, + acnt: &AccountState, +) -> ValidationResult { + let genesis = &GenesisValues::mainnet(); + if !(*slot < first_slot(genesis, &(to_epoch(genesis, slot)+1)) - *stab_win) { + Err(ShelleyMA(MIRCertificateTooLateinEpoch)) + } else { + let (ir_reserves, ir_treasury) = ds.inst_rewards.clone(); + let (pot, ir_pot) = match mir.source { + Reserves => (acnt.reserves, ir_reserves.clone()), + Treasury => (acnt.treasury, ir_treasury.clone()), + }; + let mut combined: HashMap = HashMap::new(); + match &mir.target { + StakeCredentials(kvp) => { + let mut kvv: Vec<(StakeCredential, u64)> = // TODO: Err if the value is negative + kvp.iter().map(|kv| (kv.clone().0, kv.clone().1 as u64)).collect(); + kvv.extend(ir_pot); + for (key, value) in kvv { + combined.insert(key, value); + } + }, + _ => (), + } + if combined.iter().map(|kv| kv.1).sum::() > pot { + return Err(ShelleyMA(InsufficientForInstantaneousRewards)); + } else { + ds.inst_rewards = match mir.source { + Reserves => (combined, ir_reserves), + Treasury => (ir_treasury, combined), + } + }; + Ok(()) + } +} + +#[inline] +// Called just `epoch` in specs +fn to_epoch(genesis: &GenesisValues, slot: &Slot) -> Epoch { + genesis.absolute_slot_to_relative(*slot).0 +} + +#[inline] +// CamelCase in specs +fn first_slot(genesis: &GenesisValues, epoch: &Epoch) -> Slot { + genesis.relative_slot_to_absolute(*epoch, 0) +} + +fn check_native_scripts( + vkey_wits: &Vec, // changed from alonzo + native_scripts: &Vec, + low_bnd: &Option, + upp_bnd: &Option, +) -> ValidationResult { + for native_script in native_scripts { + if !eval_native_script(vkey_wits, native_script, low_bnd, upp_bnd) { + return Err(ShelleyMA(ScriptDenial)); + } + } + Ok(()) +} + +fn eval_native_script( + vkey_wits: &Vec, // changed from alonzo + native_script: &NativeScript, + low_bnd: &Option, + upp_bnd: &Option, +) -> bool { + match native_script { + NativeScript::ScriptAll(scripts) => + scripts.iter() + .all(|scr| eval_native_script(vkey_wits, scr, low_bnd, upp_bnd)), + NativeScript::ScriptAny(scripts) => + scripts.iter() + .any(|scr| eval_native_script(vkey_wits, scr, low_bnd, upp_bnd)), + NativeScript::ScriptPubkey(hash) => { + vkey_wits.iter() + .any(|vkey_wit| PallasHasher::<224>::hash(&vkey_wit.vkey.clone()) == *hash) + }, + NativeScript::ScriptNOfK(val, scripts) => { + let count = scripts.iter() + .map(|scr| eval_native_script(vkey_wits, scr, low_bnd, upp_bnd)) + .fold(0, |x, y| x + y as u32); + count >= *val + }, + NativeScript::InvalidBefore(val) => { + match low_bnd { + Some(time) => val >= time, + None => false, // as per mary-ledger.pdf, p.20 + } + }, + NativeScript::InvalidHereafter(val) => { + match upp_bnd { + Some(time) => val <= time, + None => false, // as per mary-ledger.pdf, p.20 + } + }, + } +} diff --git a/pallas-applying/src/utils.rs b/pallas-applying/src/utils.rs index 69390a4d..a4e7946f 100644 --- a/pallas-applying/src/utils.rs +++ b/pallas-applying/src/utils.rs @@ -13,11 +13,13 @@ use pallas_crypto::key::ed25519::{PublicKey, Signature}; use pallas_primitives::{ alonzo::{ AssetName, AuxiliaryData, Coin, MintedTx as AlonzoMintedTx, Multiasset, NativeScript, - NetworkId, PlutusScript, PolicyId, VKeyWitness, Value, + NetworkId, PlutusScript, PolicyId, VKeyWitness, Value, StakeCredential, PoolKeyhash, + Epoch, VrfKeyhash, Relay, UnitInterval, RewardAccount, PoolMetadata, AddrKeyhash, + TransactionIndex, Genesishash, GenesisDelegateHash, }, babbage::{MintedTx as BabbageMintedTx, PlutusV2Script}, }; -use pallas_traverse::{MultiEraInput, MultiEraOutput}; +use pallas_traverse::{MultiEraInput, MultiEraOutput, time::Slot}; use std::collections::HashMap; use std::ops::Deref; pub use validation::*; @@ -101,6 +103,10 @@ pub fn lovelace_diff_or_fail( } pub fn multi_assets_are_equal(fma: &Multiasset, sma: &Multiasset) -> bool { + multi_asset_included(fma, sma) && multi_asset_included(sma, fma) +} + +pub fn multi_asset_included(fma: &Multiasset, sma: &Multiasset) -> bool { for (fpolicy, fassets) in fma.iter() { match find_policy(sma, fpolicy) { Some(sassets) => { @@ -158,7 +164,7 @@ fn coerce_to_coin( err: &ValidationError, ) -> Result, ValidationError> { let mut res: Vec<(PolicyId, KeyValuePairs)> = Vec::new(); - for (policy, assets) in value.clone().to_vec().iter() { + for (policy, assets) in value.iter() { let mut aa: Vec<(AssetName, Coin)> = Vec::new(); for (asset_name, amount) in assets.clone().to_vec().iter() { if *amount < 0 { @@ -342,3 +348,65 @@ pub fn compute_plutus_v2_script_hash(script: &PlutusV2Script) -> PolicyId { payload.insert(0, 2); pallas_crypto::hash::Hasher::<224>::hash(&payload) } + +// Move to an appropriate place (primitives?) +// pub type RewardAccounts = HashMap; +// pub type Delegations = HashMap; + +pub type CertificateIndex = u32; + +#[derive(PartialEq, Eq, Hash, Clone)] +pub struct CertPointer { + pub slot: Slot, + pub tx_ix: TransactionIndex, + pub cert_ix: CertificateIndex, +} + +pub type GenesisDelegation = HashMap; +pub type FutGenesisDelegation = HashMap<(Slot, Genesishash), (GenesisDelegateHash, VrfKeyhash)>; +pub type InstantaneousRewards = (HashMap, HashMap); + +#[derive(Default)] // for testing +pub struct DState { + pub rewards: HashMap, + pub delegations: HashMap, + pub ptrs: HashMap, + pub fut_gen_delegs: FutGenesisDelegation, + pub gen_delegs: GenesisDelegation, + pub inst_rewards: InstantaneousRewards, +} + +// Essentially part of the `PoolRegistration` component of `Certificate` at alonzo/src/model.rs +#[derive(Clone)] +pub struct PoolParam { + pub vrf_keyhash: VrfKeyhash, + pub pledge: Coin, + pub cost: Coin, + pub margin: UnitInterval, + pub reward_account: RewardAccount, // FIXME: Should be a `StakeCredential`, or `Hash<_>`??? + pub pool_owners: Vec, + pub relays: Vec, + pub pool_metadata: Nullable, +} + +#[derive(Default)] // for testing +pub struct PState { + pub pool_params: HashMap, + pub fut_pool_params: HashMap, + pub retiring: HashMap, +} + +// Originally `DPState` in ShelleyMA specs, then updated to +// `CertState` in Haskell sources at Intersect (#3369). +#[non_exhaustive] +#[derive(Default)] // for testing +pub struct CertState { + pub pstate: PState, + pub dstate: DState, +} + +#[derive(Default)] // for testing +pub struct AccountState { + pub treasury: Coin, + pub reserves: Coin, +} diff --git a/pallas-applying/src/utils/validation.rs b/pallas-applying/src/utils/validation.rs index 5b560b40..c05b55f4 100644 --- a/pallas-applying/src/utils/validation.rs +++ b/pallas-applying/src/utils/validation.rs @@ -49,6 +49,19 @@ pub enum ShelleyMAError { MissingScriptWitness, WrongSignature, MintingLacksPolicy, + KeyAlreadyRegistered, + KeyNotRegistered, + PointerInUse, + RewardsNotNull, + PoolAlreadyRegistered, + PoolNotRegistered, + PoolCostBelowMin, + DuplicateGenesisDelegate, + DuplicateGenesisVRF, + GenesisKeyNotInMapping, + InsufficientForInstantaneousRewards, + MIRCertificateTooLateinEpoch, + ScriptDenial, } #[derive(Debug, Clone)] diff --git a/pallas-applying/tests/README.md b/pallas-applying/tests/README.md index bcbda2de..c5ba3596 100644 --- a/pallas-applying/tests/README.md +++ b/pallas-applying/tests/README.md @@ -28,9 +28,19 @@ Note that, since phase-1 validations do not include the execution of native scri List of positive unit tests: - **successful_mainnet_shelley_tx** ([here](https://cexplorer.io/tx/50eba65e73c8c5f7b09f4ea28cf15dce169f3d1c322ca3deff03725f51518bb2) to see on Cardano explorer) is a simple Shelley transaction, with no native scripts or metadata. - **successful_mainnet_shelley_tx_with_script** ([here](https://cexplorer.io/tx/4a3f86762383f1d228542d383ae7ac89cf75cf7ff84dec8148558ea92b0b92d0) to see on Cardano explorer) is a Shelley transaction with a native script and no metadata. +- **successful_mainnet_shelley_tx_with_changed_script** is the same as the + previous transaction but the script is modified from requiring all signatures + to requiring only one of them, and with one key-witness pair removed. - **successful_mainnet_shelley_tx_with_metadata** ([here](https://cexplorer.io/tx/c220e20cc480df9ce7cd871df491d7390c6a004b9252cf20f45fc3c968535b4a) to see on Cardano Explorer) is a Shelley transaction with metadata and no native scripts. - **successful_mainnet_mary_tx_with_minting** ([here](https://cexplorer.io/tx/b7b1046d1787ac6917f5bb5841e73b3f4bef8f0a6bf692d05ef18e1db9c3f519) to see on Cardano Explorer) is a Mary transaction that mints assets and has, therefore, a native script. It has no metadata. - +- **successful_mainnet_mary_tx_with_pool_reg** + ([here](https://cexplorer.io/tx/ce8ba608357e31695ce7be1a4a9875f43b3fd264f106e455e870714f149af925) + to see on Cardano explorer) is a Mary transaction with a pool registration. +- **successful_mainnet_allegra_tx_with_mir** + ([here](https://cexplorer.io/tx/99f621beaacefc14ad8912b777422600e707f75bf619b2af20e918b0fe53f882) + to see on Cardano explorer) is a Mary transaction moving instantaneous + rewards, drawn from the Treasury. + List of negative unit tests: - **empty_ins** takes successful_mainnet_shelley_tx and removes its input. - **unfound_utxo** takes successful_mainnet_shelley_tx and calls validation on it without a proper UTxO set containing the transaction input information. @@ -45,6 +55,11 @@ List of negative unit tests: - **missing_vk_witness** takes successful_mainnet_shelley_tx and removes the verification-key witness associated to one of its inputs. - **vk_witness_changed** takes successful_mainnet_shelley_tx and modifies the verification-key witness associated to one of its inputs. - **missing_native_script_witness** takes successful_mainnet_shelley_tx_with_script and removes the native script associated to one of its inputs. +- **missing_signature_native_script** takes successful_mainnet_shelley_tx but + one verification-key witness is removed (the same one of + successful_mainnet_shelley_tx_with_changed_script). +- **too_late_for_mir** takes successful_mainnet_allegra_tx_with_mir but the slot + is advanced to a later moment. ### Alonzo *pallas-applying/tests/alonzo.rs* contains multiple unit tests for validation in the Alonzo era. diff --git a/pallas-applying/tests/shelley_ma.rs b/pallas-applying/tests/shelley_ma.rs index baaf7931..08ea3ff1 100644 --- a/pallas-applying/tests/shelley_ma.rs +++ b/pallas-applying/tests/shelley_ma.rs @@ -152,6 +152,80 @@ mod shelley_ma_tests { } } + #[test] + // Same as successful_mainnet_shelley_tx_with_script, but changing "All" to "any" and + // deleting one key-witness pair + fn successful_mainnet_shelley_tx_with_changed_script() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/shelley4.tx")); + let mut mtx: MintedTx = minted_tx_from_cbor(&cbor_bytes); + // Delete one VKey witness. + let mut tx_wits: MintedWitnessSet = mtx.transaction_witness_set.unwrap().clone(); + let wit: VKeyWitness = tx_wits.vkeywitness.unwrap().remove(1); + tx_wits.vkeywitness = Some(Vec::from([wit])); + let mut tx_buf: Vec = Vec::new(); + match encode(tx_wits, &mut tx_buf) { + Ok(_) => (), + Err(err) => panic!("Unable to encode Tx ({:?})", err), + }; + mtx.transaction_witness_set = + Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap(); + let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Shelley); + let utxos: UTxOs = mk_utxo_for_alonzo_compatible_tx( + &mtx.transaction_body, + &[( + String::from("711245ed0e86bc58578e4b06958d5b0ef856ed42e5ee8fa811e0745aba"), + Value::Coin(2000000), + None, + )], + ); + let env: Environment = Environment { + prot_params: MultiEraProtocolParameters::Shelley(ShelleyProtParams { + minfee_b: 155381, + minfee_a: 44, + max_block_body_size: 65536, + max_transaction_size: 4096, + max_block_header_size: 1100, + key_deposit: 2000000, + pool_deposit: 500000000, + maximum_epoch: 18, + desired_number_of_stake_pools: 150, + pool_pledge_influence: RationalNumber { + // FIX: this is a made-up value. + numerator: 1, + denominator: 1, + }, + expansion_rate: RationalNumber { + // FIX: this is a made-up value. + numerator: 1, + denominator: 1, + }, + treasury_growth_rate: RationalNumber { + // FIX: this is a made-up value. + numerator: 1, + denominator: 1, + }, + decentralization_constant: RationalNumber { + numerator: 1, + denominator: 1, + }, + extra_entropy: Nonce { + variant: NonceVariant::NeutralNonce, + hash: None, + }, + protocol_version: (0, 2), + min_utxo_value: 1000000, + min_pool_cost: 340000000, + }), + prot_magic: 764824073, + block_slot: 17584925, + network_id: 1, + }; + match validate(&metx, &utxos, &env) { + Ok(()) => (), + Err(err) => panic!("Unexpected error ({:?})", err), + } + } + #[test] // Transaction hash: // c220e20cc480df9ce7cd871df491d7390c6a004b9252cf20f45fc3c968535b4a @@ -278,6 +352,192 @@ mod shelley_ma_tests { } } + // #[test] + // // Transaction hash: + // // 1dd22b2976374f9b8e6aa045ded141742fa5adc5184a505410fb9f343d14e407 + // fn successful_mainnet_mary_tx_with_stk_reg() { + // let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/mary3.tx")); + // let mtx: MintedTx = minted_tx_from_cbor(&cbor_bytes); + // let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Mary); + // let utxos: UTxOs = mk_utxo_for_alonzo_compatible_tx( + // &mtx.transaction_body, + // &[( + // String::from("018e8f7a7073b8a95a4c1f1cf412b1042fca4945b89eb11754b3481b29fb2b631db76384f64dd94b47f97fc8c2a206764c17a1de7da2f70e83"), + // Value::Coin(1_501_000_000), + // None, + // ), + // ( + // String::from("018e8f7a7073b8a95a4c1f1cf412b1042fca4945b89eb11754b3481b29fb2b631db76384f64dd94b47f97fc8c2a206764c17a1de7da2f70e83"), + // Value::Coin(9_000_000), + // None, + // )], + // ); + // let env: Environment = Environment { + // prot_params: MultiEraProtocolParameters::Shelley(ShelleyProtParams { + // minfee_b: 155381, + // minfee_a: 44, + // max_block_body_size: 65536, + // max_transaction_size: 16384, + // max_block_header_size: 1100, + // key_deposit: 2_000_000, + // pool_deposit: 500_000_000, + // maximum_epoch: 18, + // desired_number_of_stake_pools: 500, + // pool_pledge_influence: RationalNumber { + // numerator: 3, + // denominator: 10, + // }, + // expansion_rate: RationalNumber { + // numerator: 3, + // denominator: 1000, + // }, + // treasury_growth_rate: RationalNumber { + // numerator: 2, + // denominator: 10, + // }, + // decentralization_constant: RationalNumber { + // numerator: 0, + // denominator: 1, + // }, + // extra_entropy: Nonce { + // variant: NonceVariant::NeutralNonce, + // hash: None, + // }, + // protocol_version: (4, 0), + // min_utxo_value: 1_000_000, + // min_pool_cost: 340_000_000, + // }), + // prot_magic: 764824073, + // block_slot: 26342233, + // network_id: 1, + // }; + // match validate(&metx, &utxos, &env) { + // Ok(()) => (), + // Err(err) => panic!("Unexpected error ({:?})", err), + // } + // } + + #[test] + // Transaction hash: + // ce8ba608357e31695ce7be1a4a9875f43b3fd264f106e455e870714f149af925 + fn successful_mainnet_mary_tx_with_pool_reg() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/mary2.tx")); + let mtx: MintedTx = minted_tx_from_cbor(&cbor_bytes); + let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Mary); + let utxos: UTxOs = mk_utxo_for_alonzo_compatible_tx( + &mtx.transaction_body, + &[( + String::from("018e8f7a7073b8a95a4c1f1cf412b1042fca4945b89eb11754b3481b29fb2b631db76384f64dd94b47f97fc8c2a206764c17a1de7da2f70e83"), + Value::Coin(1_507_817_955), + None, + )], + ); + let env: Environment = Environment { + prot_params: MultiEraProtocolParameters::Shelley(ShelleyProtParams { + minfee_b: 155381, + minfee_a: 44, + max_block_body_size: 65536, + max_transaction_size: 16384, + max_block_header_size: 1100, + key_deposit: 2_000_000, + pool_deposit: 500_000_000, + maximum_epoch: 18, + desired_number_of_stake_pools: 500, + pool_pledge_influence: RationalNumber { + numerator: 3, + denominator: 10, + }, + expansion_rate: RationalNumber { + numerator: 3, + denominator: 1000, + }, + treasury_growth_rate: RationalNumber { + numerator: 2, + denominator: 10, + }, + decentralization_constant: RationalNumber { + numerator: 0, + denominator: 1, + }, + extra_entropy: Nonce { + variant: NonceVariant::NeutralNonce, + hash: None, + }, + protocol_version: (4, 0), + min_utxo_value: 1_000_000, + min_pool_cost: 340_000_000, + }), + prot_magic: 764824073, + block_slot: 26342415, + network_id: 1, + }; + match validate(&metx, &utxos, &env) { + Ok(()) => (), + Err(err) => panic!("Unexpected error ({:?})", err), + } + } + + #[test] + // Transaction hash: + // 99f621beaacefc14ad8912b777422600e707f75bf619b2af20e918b0fe53f882 + // A total of 10_797_095_002 lovelace is drawn from the Treasury. + fn successful_mainnet_allegra_tx_with_mir() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/allegra1.tx")); + let mtx: MintedTx = minted_tx_from_cbor(&cbor_bytes); + let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Mary); + let utxos: UTxOs = mk_utxo_for_alonzo_compatible_tx( + &mtx.transaction_body, + &[( + String::from("61b651c2062463499961b9cd594da399a5ec910fceb5c63f9eb55a224a"), + Value::Coin(96_400_000), + None, + )], + ); + let env: Environment = Environment { + prot_params: MultiEraProtocolParameters::Shelley(ShelleyProtParams { + minfee_b: 155381, + minfee_a: 44, + max_block_body_size: 65536, + max_transaction_size: 16384, + max_block_header_size: 1100, + key_deposit: 2_000_000, + pool_deposit: 500_000_000, + maximum_epoch: 18, + desired_number_of_stake_pools: 500, + pool_pledge_influence: RationalNumber { + numerator: 3, + denominator: 10, + }, + expansion_rate: RationalNumber { + numerator: 3, + denominator: 1000, + }, + treasury_growth_rate: RationalNumber { + numerator: 2, + denominator: 10, + }, + decentralization_constant: RationalNumber { + numerator: 3, + denominator: 10, + }, + extra_entropy: Nonce { + variant: NonceVariant::NeutralNonce, + hash: None, + }, + protocol_version: (3, 0), + min_utxo_value: 1_000_000, + min_pool_cost: 340_000_000, + }), + prot_magic: 764824073, + block_slot: 19282133, + network_id: 1, + }; + match validate(&metx, &utxos, &env) { + Ok(()) => (), + Err(err) => panic!("Unexpected error ({:?})", err), + } + } + #[test] // All inputs are removed. fn empty_ins() { @@ -1220,4 +1480,145 @@ mod shelley_ma_tests { }, } } + + #[test] + // Like successful_mainnet_shelley_tx (hash: + // 50eba65e73c8c5f7b09f4ea28cf15dce169f3d1c322ca3deff03725f51518bb2), but one + // verification-key witness is removed + // (the same one of successful_mainnet_shelley_tx_with_changed_script). + fn missing_signature_native_script() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/shelley2.tx")); + let mut mtx: MintedTx = minted_tx_from_cbor(&cbor_bytes); + // Delete one VKey witness. + let mut tx_wits: MintedWitnessSet = mtx.transaction_witness_set.unwrap().clone(); + let wit: VKeyWitness = tx_wits.vkeywitness.unwrap().remove(1); + tx_wits.vkeywitness = Some(Vec::from([wit])); + let mut tx_buf: Vec = Vec::new(); + match encode(tx_wits, &mut tx_buf) { + Ok(_) => (), + Err(err) => panic!("Unable to encode Tx ({:?})", err), + }; + mtx.transaction_witness_set = + Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap(); + let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Shelley); + let env: Environment = Environment { + prot_params: MultiEraProtocolParameters::Shelley(ShelleyProtParams { + minfee_b: 155381, + minfee_a: 44, + max_block_body_size: 65536, + max_transaction_size: 4096, + max_block_header_size: 1100, + key_deposit: 2000000, + pool_deposit: 500000000, + maximum_epoch: 18, + desired_number_of_stake_pools: 150, + pool_pledge_influence: RationalNumber { + // FIX: this is a made-up value. + numerator: 1, + denominator: 1, + }, + expansion_rate: RationalNumber { + // FIX: this is a made-up value. + numerator: 1, + denominator: 1, + }, + treasury_growth_rate: RationalNumber { + // FIX: this is a made-up value. + numerator: 1, + denominator: 1, + }, + decentralization_constant: RationalNumber { + numerator: 1, + denominator: 1, + }, + extra_entropy: Nonce { + variant: NonceVariant::NeutralNonce, + hash: None, + }, + protocol_version: (0, 2), + min_utxo_value: 1000000, + min_pool_cost: 340000000, + }), + prot_magic: 764824073, + block_slot: 5281340, + network_id: 1, + }; + let utxos: UTxOs = mk_utxo_for_alonzo_compatible_tx( + &mtx.transaction_body, + &[( + String::from("7165c197d565e88a20885e535f93755682444d3c02fd44dd70883fe89e"), + Value::Coin(2000000), + None, + )], + ); + match validate(&metx, &utxos, &env) { + Ok(()) => panic!("The script is not satisfied"), + Err(err) => match err { + ShelleyMA(ShelleyMAError::ScriptDenial) => (), + _ => panic!("Unexpected error ({:?})", err), + }, + } + } + #[test] + // Same as successful_mainnet_allegra_tx_with_mir(), + // but the the slot is advanced to a later moment. + fn too_late_for_mir() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/allegra1.tx")); + let mtx: MintedTx = minted_tx_from_cbor(&cbor_bytes); + let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Mary); + let utxos: UTxOs = mk_utxo_for_alonzo_compatible_tx( + &mtx.transaction_body, + &[( + String::from("61b651c2062463499961b9cd594da399a5ec910fceb5c63f9eb55a224a"), + Value::Coin(96_400_000), + None, + )], + ); + let env: Environment = Environment { + prot_params: MultiEraProtocolParameters::Shelley(ShelleyProtParams { + minfee_b: 155381, + minfee_a: 44, + max_block_body_size: 65536, + max_transaction_size: 16384, + max_block_header_size: 1100, + key_deposit: 2_000_000, + pool_deposit: 500_000_000, + maximum_epoch: 18, + desired_number_of_stake_pools: 500, + pool_pledge_influence: RationalNumber { + numerator: 3, + denominator: 10, + }, + expansion_rate: RationalNumber { + numerator: 3, + denominator: 1000, + }, + treasury_growth_rate: RationalNumber { + numerator: 2, + denominator: 10, + }, + decentralization_constant: RationalNumber { + numerator: 3, + denominator: 10, + }, + extra_entropy: Nonce { + variant: NonceVariant::NeutralNonce, + hash: None, + }, + protocol_version: (3, 0), + min_utxo_value: 1_000_000, + min_pool_cost: 340_000_000, + }), + prot_magic: 764824073, + block_slot: 19483200, + network_id: 1, + }; + match validate(&metx, &utxos, &env) { + Ok(()) => panic!("MIR after the stability window"), + Err(err) => match err { + ShelleyMA(ShelleyMAError::MIRCertificateTooLateinEpoch) => (), + _ => panic!("Unexpected error ({:?})", err), + }, + } + } } diff --git a/pallas-primitives/src/alonzo/model.rs b/pallas-primitives/src/alonzo/model.rs index c39567e8..3846b11b 100644 --- a/pallas-primitives/src/alonzo/model.rs +++ b/pallas-primitives/src/alonzo/model.rs @@ -413,7 +413,7 @@ pub type UnitInterval = RationalNumber; pub type PositiveInterval = RationalNumber; -#[derive(Serialize, Deserialize, Debug, PartialEq, PartialOrd, Eq, Ord, Clone)] +#[derive(Serialize, Deserialize, Debug, PartialEq, PartialOrd, Eq, Ord, Clone, Hash)] pub enum StakeCredential { AddrKeyhash(AddrKeyhash), Scripthash(Scripthash), diff --git a/test_data/allegra1.tx b/test_data/allegra1.tx new file mode 100644 index 00000000..22322c78 --- /dev/null +++ b/test_data/allegra1.tx @@ -0,0 +1 @@ +84a50081825820167b908450685d217f1373503ab95d25e25a009467cb5416bd4f75cc50f6006f00018182581d61b651c2062463499961b9cd594da399a5ec910fceb5c63f9eb55a224a1a05b136e0021a000dbba0031a013a1a4a048182068201bf8200581c028feeb3074fc7fe233ac85244ed150b0b236f301c9677d14c141b9e1a02a6dd158200581c0358bba0857398c5a8a80d7cd64d4b32f960abd6bcdfa5cd9dee39681a00dfc8898200581c03a6539a5570f39319582af64c80e4b3874369ddc8a41065d833d4401a034a6bb68200581c04f9892507af0775bbe286ed379ba369a67cbaf8637df65a1201c5451a004516cb8200581c05bbbaf2ffa842578451130201a9016b8e2a5f95a73af307314faf761a005449938200581c05f69cdc4ee8f21a306efb26b80f6d314cdcc76bed5d9c7c5f0546171a0152a9fb8200581c0844e73f236d4c64def219258a10634ae18fff26d1b67edd2cf03e361a05a7d79d8200581c0b53f775a01f627cb5f430f39b9f61a3d311779c6eebf3838ddba3891a0a2cb2c78200581c107782f197766073c53c1580d1176dd5c947eac8726599898a2d7d7d1a008b28f58200581c11cece1c95677fb25ecb2aebf6ad83215895e9c94d797e0e8c48301d1a016130f58200581c144304bed922b5e10e8869e42b55058dee601f6f56b24ff27ac6c79a1a04be24d08200581c159f19778c52002bfca782b363b9d864b9cc894247e29d763df565751a015dbe998200581c16382f8b172921b5de403efeb5596256a6c4fe84d93dbd855227f8f41a015100838200581c1729f991ec021e80b9ee91f66a1ee21ae7f9693e177526889347a2341a09b5a4a58200581c17504fba64010db02c45bbac3f923491099f99687fc8cc0765e0ee5e1a0032c2a78200581c178a6f74ff792130b0e691e6ea02dd0d039514a8c40a8b509fd8fcfb1a06ed220f8200581c1ae995cf9feefd4bff99bc0b206d242d72dfd6efd53a36517d11a0e91a0021c0e68200581c1b2409bf74fcf6154dcfef0612c61283635da1b0a415645f2963cea31a068895fa8200581c1d2c2853e013dcb951ce21a86e067fc82e93e315f5a5ecf0c9a186ba1a0265e5418200581c245fe0cfeab2f174373ec4a6a4292bf3b1bec9e26b4fa9c0cb33c8aa1a08bfacf78200581c24b25df075bff89dd14765443df2302a20fd40470c65c74cc7f0f2941a010826ae8200581c255cbba8038b7b88996e91df70bd54fd8810c9bee18b5494361a5d721a044810f18200581c25ec83dc286f28c8b38fe696afbee66f391cf4c25dd61ff58bb8cce01a01e68d3d8200581c2a5457a12f0a61dcb24f458f0b0cdbaead6b17f65079ae4f012ca8351a0d38c9448200581c2a99707b39c1b84f2b42b1a3c5b921286427ac2f214f3db13c2a78f91a0027d2828200581c2b6a7d2c2eff1898f9dba6ae690d158af95efb317ffa0ec4bb476dc31a0154e2468200581c2c12e91b15646fcee1c4e5d2438a743c7c3d1a91bf2e446f2560aff01a002108788200581c2c3e28a4692d8e4c5cde42ff66ce6acb804e012a8425476a3914339b1a00a879ea8200581c2de39242b5fe6bcf85fcf9216691d7a78297ebcbbf81e3f6927a53e21a0694d76c8200581c2fa24fdbc2dbf52d7e7351d3cf6a545a14746f511b4d23fbd9c83be11a00a88e8f8200581c2fadcbe9b493bdc646e778b2614ba4599ac4bfa8eedc726c77f65ede1a008e49578200581c2fd89f927bd9b1b314f1e234fc0d1f4bf1cc23a1b89d4059013528a81a07ea64b58200581c3197ecb62f401a142ffcbfc8008f5f418d54b53693b1b2a1af36aed01a002564f78200581c31ce7d59502a1be31145780603c31f7830fe6357eff14684d4d15ba31a00da3bb68200581c3320b12522d3fbdb4e3bc7665285632fcadabe24127ea5d61230e1b91a01b659498200581c33e72c2312934f73cce410c2921a6253e6d912ce695f558302fff8af1a0021b2e68200581c3604cd8f59c5b945ca12ca0ba0a7f8991e9c297458144ffa5a7c2a931a00bdc1ae8200581c36cede966ecc0d1c81b787aaed3a3f344b4324db95e7f2b627c6d20f1a01512cf78200581c36d67151e2d942af08977832bea1ab6674234a40b83264487fd8bb011a03cf26af8200581c3a042ffe55bf9e837e2b35fd0bb81074cfc58f81227499228999cc351a0161d0a08200581c3d4b6a709d9b84a3dbfaaafc3f4b5eb04c812da1080ff0019b5285a91a00699e668200581c3e98567d6eefe2fd4be43bbdf51f06b4ce698475a4c827e1f213152c1a035788d58200581c3ff5d4efc15d6122a88b8822eae91fcdb7bf336862df3e766b55a3291a00a884928200581c40a49c0b0e83078f829dffbfc3e2978022b7b5ea0545d1f452d422f01a00a01b428200581c4335139c6f43cfe161c32592f0f18ce50aac67af7a5711d3e80ab3d61a00a8cfa18200581c4418ef4c2dba7ec4b90a457ab9297b040395ccd46fc09f5be2ef9d3b1a00e3f0b68200581c4d09fc072b1990dd311449cbd964f5c1428bee1c1165d3d62807a6941a00ab7c4a8200581c4d536c7865bae42bb01f0878808d8ad100f9313194cabb61752c110f1a017f77b08200581c4ef155c7fdd79751aeec9e9d68371c7ce07d119423b11ce5080d7cf61a0693fa888200581c4efaf99926839e3b5548625223a0f86c09156dd4e4bd07cf17eb8bfd1a011575b98200581c4fdeb76961881ffbacf6003849dbf516d3239c2d289cbaeedf9ec7dc1a00460f928200581c50cd26b89b3dad56f61afa0900d87e71b986204c4b46fe5d56f7d5fd1a01e9de628200581c5163218374fc1f1c3d65141c577a4a08233471f10252321e79cf59201a0021b23c8200581c51752f0e4eae3b1bf609c3e93f01de7583bd529616b56c7efa22a5381a0d7ead1a8200581c51e4c85eda929c7e6f8a6d09327272e7489cd587680465a011932d821a001afe0d8200581c536c33773746f495940ba2784ca4a5a483d6c8cad4b291c64ad27cf51a003605908200581c57d4b5b5cd916d93832b39a25a45e0d1e77404c170cdf55e89bfae2b1a00ae4d0a8200581c57f06b13f7a8b20323ea8f004c41680a00963c349a6d0d4c55628ca41a001acfa68200581c5ae49dc993d2d79b46f51083077ade1ae1949e66a22998b406463be01a0226bf118200581c5b8297340e90123649721def9c4ffab3fce481384726a6099eb952b31a0021b1ef8200581c5ccfc6e1a5194f8b02634ac66eaed18aa7005bf9f2f492d4b223b4ee1a0174119a8200581c5d6bfb289db580670a4b1f8a0ae3aab016c1461dbbcbdb15a27eb3551a001af4f58200581c5d77965401dff34a90013e33890bcabdedc4afdd8761aa58f17175531a009063048200581c5eebe87f753eb6b947a2f7971a4f8a163c85f12a23c4a65c84f5d0951a0515f0cb8200581c5f5f436634708a6330029b8df20311f789d415d4cb9ab1bdbe07ec6e1a02aa0f438200581c607f6dd428bd8eed812c5d88633de1ff32eb14f3b0836729b01663b31a07a09cb78200581c61a53e5d3fa5100bd09dc1f54d874609ed24e1690aa4f6237651c6ad1a06a3a2c88200581c62393dbf2e9acad56e6f501aa99d4ca40a613efe7466e029be3e38081a03f2e7a78200581c63cedd4a6f1be2a5877f42458a2be4e312907b5a85e3020089b5ab581a1845c72d8200581c6426c1c8b9093452709c98e283b1c81ed1d250dd710879c735b6a2611a00329bc68200581c6661aa7adc62bcd640f31f354576500c8d2e6002483035ea9063eea11a0253fd198200581c672a795037e4b50d520d8415988e2d6faab106a5e4adca664cae7c021a036edac88200581c673744fd43c851363cd1c964a20da28efc43bfc2de5ed04e5750515c1a01dd41b18200581c69c2b5b63dfa2f8caa34a890b3f98fef327f19288fed1dfc1f3f11471a0062b0998200581c6a53d43f6ad9ac8f8c28117d112ec545ae6723c2c84d9d660a196df91a001b02268200581c6b5eaec28c267d46ce603226c16fd11bd4b441021551ffd975ca79011a0046bc708200581c6b62cfab15484c11d29523086f7e3124644eb43f096357fb40fc0b371a001b108f8200581c6ba7ae1c3e32004f4e16a801c8916083d7aed2e4932ee7ed9e591e171a00609c128200581c6d43728f6d4201428cf20b35461c0695a7dfe6babee2b543f8b4b3991a00a6196a8200581c6e2c34aac1bbe994316ce4ded4ad23116a8043d3e0e90c4a1523a5681a002fc0f28200581c6e61812a946e567dc816203c1d6026aba5f14c33f6ae90e1d024a5bf1a00cb04478200581c6ec00e8261d8f8c70ee12dc34c0dc7550a7b9c3fe2cce7cf9a5b38d91a1c7567a78200581c6f7d707223d1449ea32e9fe7b457f9098b537105b300ffd2962a545a1a004c6b6c8200581c7090f3779ac5de2f7b96772733914a53a39433da46577c19cd2ad6071a01cfff758200581c7112ee147412ec34336431c89b5de6028f62290a4589c16848f931a71a06c242c88200581c732ef9a10fc2cbe29f7e982ab452dd2c637027dc6ad4f2abec409c2a1a01cd1acc8200581c746ce0615e630100b9b827c0380945365e8de16eb9a43caa24956d261a01e3623d8200581c75359c04a5d9530cab6bf6e338efe0202ec9cc5f36ea052945ce16531a01c759ed8200581c7564eb11453b9ba731b293556e310a4df8fb5ce60899c99428cae6fe1a0f9940f28200581c7844f55b003d50f93bb170cc68484e8a7c0acdcc8bb3c3b6fe34a40e1a0d2ead8b8200581c79f3ce2708307282062aa911bfbfb3651326669e39412066d9784b271a003604288200581c7a647f2437ae4611b861c04fb79905768f25017573cffa609773eb041a00484c9c8200581c7b4731cbbed138fa58de3ae59cf4a4591f6a8c339f2cba732041e1ff1a040110078200581c7c828e5d80fb7aa20075022bce2617c314a96358ccb17c683f22aba11a0591e7378200581c7cb5ace66ebdc8e3e0da3fa8017eba8a5b72c2629ae1fed9bbd9941b1a08db14778200581c7e2905f26356971732d5afa1d9f727df35635f6eacf685c5932702271a001c595b8200581c800730d7e24b8b7a1a6662f11efc1e3170a06cc0b633fd111a1830061a03d9ef288200581c8421d040e7c9e34b62d76c98b97c0577ad16aae01c28aa44fcc4bb5a1a002610108200581c871d9f6e95759d4bdfec64c532e737df42d21f7e80f58edb570564801a0a8e77c88200581c872f0985fd1a37dab8739bb879c28dc1fa5fc03558019d7017bba2d51a0048483b8200581c87f8918af6fdba985505f8886e5c63ea93e9fab39b3bb712ade6d7211a03621e148200581c8a0a0cfef663ef87813141f44c5ec062aea7b16ee74382139097e6161a188e80e28200581c8b1a26b6ea72fc8c47d1b955979f0abc59dbbd58899a5dbc47d837c81a001fe5b48200581c8e19e09e4799121c9296094ac269b864f7c923cf2c6c051e4b5e6ccd1a00a3fbee8200581c9012f06d61d4f3ad5b5d76ec36ec54f58dc0e9be19197c3a8757d7f41a00a8fc988200581c90b38b01f744eb9b7e6845889e589e7d8b8272887d11d70365f837d01a0162fabd8200581c9235b775ef7395bc0a251ee0483e273f97d876bb4b09a4ee0c9433971a002db8748200581c93332f17e73be3045ea73b0750a77403ee9a108ff3d5eb560696ec401a01b7dc218200581c946cd25b9b241ba01566d1aa13eb3a41cdde4ecacfa2d0fd800c806b1a02051a3e8200581c948db5b6d1e79b48cc261dc720387448bb5543751b15205ac8a756261a008482758200581c94e703349ebcbc23d0fbf643c38e931f548b896ad9837a383a7b45341a01514bf98200581c95f2b14f74173cf489a646687dba5fdc887e88e8bfae4c0b415dd6931a04dedc588200581c9727b11e831606806402c56d9b31c92615fe608998585ff5539a8e281a02c9fd158200581c9840e3d26cf4f1ed741bc0063b3412130cb32ef2eb511693e58bf6ce1a0043bba48200581c98539b0d51659e8d49f6f575cc02602bee6168a8bda6e762a1fb99f31a001b49598200581c98c9408127a85ad0a03031dbce26ee1963c6ce78df66dfaf31f403071a054ea0ca8200581c9a1cb5a99fb5396f8d5b4f2fc8d8d66d27267c465230dcb42ae2d1041a019c356e8200581c9d46e1c8552b9c2b52ed818b0ceb69283520d1f7b2a17d05e0e165561a019e568e8200581c9da823b1820c4d3492385d99827efaf61daede440e663f4f20287c2d1a0110ef818200581c9e66ce87b115ff04290c2de8f0fbe0bde80904176f237e40ed4590ca1a002bafd88200581c9e67d5672b7d87464d6576d2f4a80b29727aa5d0f873c614010716281a007e4be58200581ca0fd74055edf9249fcc9725221a56a6b9ac494c794c870c5355b908b1a061eab5d8200581ca1465a386e345651b1c5fd76e8d44a67cc9cd7b30b5186ede3ef97511a0543e7b18200581ca205b708f4c1a289ae5b3abf64684e9960d1df83963b55c396ddde0b1a02f53e8c8200581ca2b944d901e886f18689bc4adef9eef2b1cd87f81fe324fb391a25271a027653ea8200581ca406aa772be0dab75a34159775aeae975349b9bb8f2e209720c9e2e41a010220178200581ca46e8d668f5f7d1cfe3c696c80011821bc71b09abb68184863772a291a009000ed8200581ca4e6a5f3bb2be41ccaa29d940b1d528cfe98e4a7c63a2f54f1dfc9591a00aa8a5d8200581ca517e5750d3e52ca962ba17ee20f4e4b0840535fee66d995624639a01a0094e6418200581ca58ac211e775a2b1f1846a4626b75fc42a7ddca0ff4d5bddb1f7c33e1a0021b05c8200581ca5d12efa1b863be5ec3afc7d2ba581c36de8141f11c05f9abda2013d1a00dd1b3f8200581ca60825001905eff4592d3551dddeb14f3e9dee197c8a3595094877d31a00bdef228200581ca685464833386816ef42429d720c378e69d00b77c945bc59e2583f621a0206ec358200581ca89e0cf89076922829ede91462ef3157981949236e6f4a76dcd1c9261a00ade1928200581ca9aa1a007eb9e8b074c32b93dc63d6e8ab2e64130797abd4a8c958ea1a003aa1be8200581ca9c464908e2b70886f2cfb48e374e2d4c2d9ce043c7bd29eab89d7181a0b6c391f8200581cab77a97e090259983df8242559087f0968419ce3c3e8e4ceaf6c5b6c1a016b25138200581cac301e5a6205236d7c16edf7f7463891f80ea684dd91770c2811c6461a01cd4ee08200581cae28c077a1eb58ec562107e0a210ed5afab4e2c5e8a354e7af26a9411a00c5154a8200581cae4b2a53df7cb9f328913bf903387209af52dd25cfc766e979d48de21a0272a3f08200581caea2e5de838202a2df5491fe17524af5b730fd033060565232e5c54e1a02eee5668200581cb22d6124ab954e72adab67628cd231edc78a83968e80f3a3b8ad26701a014a30558200581cb4290f1a0daef856cdaba189563c1d9e5d463867869f570e1393d4451a02c94e0d8200581cb483eaecdac5ae7596c99cac8d6503e4f9d4f17fc821dd6bad388cca1a0021b5708200581cb7e45176af370fe74d783adc74161c647639043e62bd1dfb2ebd261d1a0048b7c08200581cb99f23cf2d465f72b6a14e1b0e6c92fdc863b2a42045578b19ecc1811a0d5d0cea8200581cb9c276a32083c28113b6301f6355ba09ff88b899d204f27ee51966ac1a002d54de8200581cbbdabf7b83c3f8ef583db3bad38a58d9b2e418306d1684ad8742ff2e1a0021b0368200581cbc82aa560090694433cd010c43397abc87e0f0972d22bcfba037d2411a001dfe1f8200581cbd591ba1630ad1d9544b73f880a8eab6923b376f2187f25c51af458a1a015513278200581cbd7ce09cd7393e8d3600d568fbfd73def40ce0f9eeeee20335740f111a010b4c7d8200581cbf81a71179afb2ac0b3ef8f1bc36fbc5b804c0d884088f40f389a6551a080b49948200581cbf9684ab82180dda805457e836bcefa1c54be36b42ff1c0fc8b2d71b1a006b58d38200581cc18e9205a0453701ddf5fe7417719e28861823df863f2f1feb45558d1a032af0bf8200581cc886141806b33ddc5637cc81d7c8277caf7c2df5111a08a9a3ebe1461a020d74668200581ccbc626d446f0aa3d19a6b2d8bb99141833f587a6a25e0073613f81ae1a01563b3a8200581cce1ae61745193cc354cb631ea2b76cb367f9c89ae953a2327f3147ca1a015109238200581ccfccd3198492a3aeb0b0bb8844d95e0a15f322b665dfd9828e3d20c71a010939768200581cd02bb1d7563c83787e07871665116ec07c079684fc9de776a2afbf781a03ef91a18200581cd4b90d8b9bfb979637ccf35d1616b635c4ac0f3978f5502c2233fe001a008060ae8200581cd5c0e8d6df3947f7dcbdc9039257e4235afa9cfd3375a6013f3507841a01998aa58200581cd6e749fec010193ba45d43c744619f9a797cbec890f1177e73d06e3f1a0086c8a88200581cd998e7ae422b8cc4827bf13a8e50b81653741023d4b4ebc8abc1371d1a00d1e2c68200581cd9c5dffafc7bf47a60cf7bc38e239d3f4e8d4cd3a0e5500778cdad571a0021b8d48200581cda743b6e6fa4477b1d68a525deb9f928cf2498585735db976c9517c91a005b95398200581cdc0b1b4d4f7c7e91d32e80e82cfc71a427a0ab0e6841ac634baf89a81a025f5d518200581cddd1ddce5b6ee103b1ee12b08ca5e14ec73648cae7c6b229bb62e22d1a009d9c5d8200581cdf74d77e7e5942cfc74924c7ef261713062265edb0c702ef8d90cd241a0694d76c8200581cdfa4fb47b728430ce4b883a46cfa38bc7caef72995960af36b3369601a00db864d8200581ce0360c94453390cafb372bde0744397b318be8b4a6e02f796693c17d1a003b44e68200581ce0f82ea7337f2f0b325f03c3f4aeec2780c0b74fc729b5e7077da24b1a002200e68200581ce1c2a57a62fb44931ab84f22f20419102c25ff60dea50b5bc00f43411a01ab62118200581ce1df6fe022d19d609702a970b2c929819d8787d3e4665b7a73abef461a02000d8b8200581ce3aeec9d70010360137465fb4327253db4991fd89551ac478f4acf6f1a053c518c8200581ce4e7fe289867b5a7ae139681e932b55fd0fe288697a8afd848b3c0851a0fcba5378200581ce5cfe2cf616a5d6f6c0098d77b6775ac973ffb32982475f2769fdfbe1a00bb702d8200581ce6154246359a00491e5e46707994ab05bb2e5e82c8932decf04ab9221a0da749878200581ce7e8ebb446cbefc87a91da516e642bc6f1a9821aa3a1bf9f7b0d89d21a00b899df8200581ce85ca1b26610a9c81a72560b25de8dc343f44dd52d27d96368ca7ba51a0cfaed328200581ce952ec599969e7b0ea0beae634bafbb644566627553a002ab0652b1d1a02a5d3f08200581ceadce0e9de04cdf33dbf4ef6017b5548c7161e769c184245c24fa7441a00238adc8200581ceb19d174824b76179440c7e0b70cf2b34914f0ba19dbd978745cc0a71a04c491888200581cec340e54731f6f375e2089f2da25fd497cc5f2ac7d1d730e4c80129e1a475d5fc18200581ced4b6be2e32a275cdcef23e7acae3ac11d5ec285158b608d0df60bec1a001af4f58200581cedeceb491625a29f52c36fe80b490486d8bacdfb4519d8172f7eba3e1a001af3158200581cee932c2f9ad3aab7003a37dbdd2882e29b4801cb983a4e2d9757e0481a01f5737b8200581cef71c8245bcc9ce6680dc4d15b0488caa3e9c7bd7c948c839a4ba84b1a007f49e58200581cf19e0b2c2da12d5c8d2607a7bff5d1e87f0dbefd8c2d479e98894ae61a012d317c8200581cf2f9b8d0d08d23a8a9684cc0682ca43e8286959d32ec3b9f4f216b861a0043737e8200581cf32f9446e2ae68d691718d70903ba3d0bcac855cf1745b8d45df609e1a00a3f8208200581cf4d86c120809825c6c4f68b4a3e9fdac63d2fb424e5b8ad3b5c4214f1a005379ff8200581cf51d4b716275ab751b0a4ca911667134f92645425c9786a8f6b397e61a05c5a33a8200581cf637bf3c940317e6c2286f42fde44c7309f5dbcd8c0ee5354f358b521a0025fa6e8200581cf6ba2018fd80f83b6c118bf602b243a13314977c5e4199c077ffc7ec1a0023a86a8200581cf794c4a3f0517abf42fbf4889038282f88d4350b746bd44c4fa6c7dc1a001b20178200581cf87b44574b32708f32104d4272d9aae0fdfeb0cfd09f5b8e91fcbe611a010d925a8200581cf905af69d4efd4c07c7223d5bca600120ae38445cc205e56564333ed1a00f774bc8200581cfa17748e9fa0925be088e9da0e19d9fcb794acd216741a54af615f5b1a09231c718200581cfa6fa77d80e23868abfa361dc40e80a04ef729dcd85dca1f2f7148e31a05a5b8578200581cfb0a91e22a4a84db6e40048c1a71d329aadff23496ee112342dc16861a0603b149ffa1008882582061261a95b7613ee6bf2067dad77b70349729b0c50d57bc1cf30de0db4a1e73a858408d818db913e2a818b7fd87d96e9029d9c73c7dc3b08cc9337e36c1f31355c0a906a75785c14909de24bdbbb8f7b9a4b47b788cdf320c38080d2e617115bde4058258209180d818e69cd997e34663c418a648c076f2e19cd4194e486e159d8580bc6cda58402e4faa7c9181d50bbb497e2c7c40ee7a005031fcf6c1dfd3e2b3eff90064ab0b7adb02e3715935c26b3de2b5e5db43e06966333958dc7a386e7ae04a7e596f0882582089c29f8c4af27b7accbe589747820134ebbaa1caf3ce949270a3d0c7dcfd541b58404aaf6b3f641f4434d0a84cdbdc53cb3fe507bbad4b1845b9d722d8e9ae0a30efef822200e32e658d9d6363c77844ceddb0b7aa85d50b60cd8cbdb0d8ea333e0b825820f14f712dc600d793052d4842d50cefa4e65884ea6cf83707079eb8ce302efc85584017f1c23f43e9a9ab49f782965939908baaceefb2f81ed117dc83421cea2c4ef5abe938bf569c6feab0099348c3fd5060e9da458359d4189802cab2ec9c21d5018258208b53207629f9a30e4b2015044f337c01735abe67243c19470c9dae8c7b7327985840cdb442f4d71f4f94a958b9a10b6bf6b7e595627d0ca3292b8f07976ad4f524926fcf4e62dfef6674fe1b40567a850edb50a343c55ae2fe371a0f2cb7b54494008258205fddeedade2714d6db2f9e1104743d2d8d818ecddc306e176108db14caadd4415840dd56fb774e1bdaabb10ebd7e6ae6976f349a990d242b75baa9d592179a0e847dcf323072c52d4ac6c2540aef087cdea5b790d3e0d1746dde4f8f5edb3546b2048258204c0232db0800fe53ce90a8d3842cc0d3964e675701f45ead680042e4049465b15840f47782aeab379e392f9cb09b24cc030b63235b68d49275eae09faedcf1101c909557b97365f90d97802cee86f21f028b088537c0938b1115dc90144fb3c3e00a825820e8c03a03c0b2ddbea4195caf39f41e669f7d251ecf221fbb2f275c0a5d7e05d158406f8e58accb86e8b56cfa9f004439d64ba3df0c1e6aee5854b0786221d4559513ccee90356a7d76ab4ab17fa703769c03bba9b75a03b1ab8cddae95b1475e2002f5f6 \ No newline at end of file diff --git a/test_data/mary2.tx b/test_data/mary2.tx new file mode 100644 index 00000000..cdb9949b --- /dev/null +++ b/test_data/mary2.tx @@ -0,0 +1 @@ +84a500818258201dd22b2976374f9b8e6aa045ded141742fa5adc5184a505410fb9f343d14e407000181825839018e8f7a7073b8a95a4c1f1cf412b1042fca4945b89eb11754b3481b29fb2b631db76384f64dd94b47f97fc8c2a206764c17a1de7da2f70e831a3c0f17fa021a0002fce9031a0191f62f04828a03581c59ebe72ae96462018fbe04633100f90b3066688d85f00f3bd254707f58201efb798f239b9b02deb4636a3ab1962af43512595fcb82276e11971e684e49b71a3b9aca001a1443fd00d81e82011864581de1fb2b631db76384f64dd94b47f97fc8c2a206764c17a1de7da2f70e8381581cfb2b631db76384f64dd94b47f97fc8c2a206764c17a1de7da2f70e83818400190bb944c22614bbf682781c68747470733a2f2f6361726461706f6f6c2e636f6d2f612e6a736f6e582001f708549816c9a075ff96e9682c11a5f5c7f4e147862a663bdeece0716ab76e83028200581cfb2b631db76384f64dd94b47f97fc8c2a206764c17a1de7da2f70e83581c59ebe72ae96462018fbe04633100f90b3066688d85f00f3bd254707fa100838258205ac72fb72ac603eb4aa1be5b98af76f193e24924b4a7c877f9e9c401fb9cda385840bdbd394d90106cd033224caffbba57fc071a0e00087bc26aa07174ec42d26060259cb865cbd8ce0981897c1fb0ebc38678420b52731827e9cb00f5209649850b825820af5d32c73a976fcd7abed9fa9c85e128862bd870c8ebc51f602a053343fab8dc584032bb6013443435846521d42e6cf076f0769766ed366661d286fda0788622f603339afa20649123a04cc4266581e328a5f02f9eca866fa44e971a6d8ab7bb5c04825820bc08576470a74179757177311dd8bdae9e56a637d58ed5d50a737d2b03dd9e685840cf586f9bc2ef1684c396e047d9923ab94fa616aed013d5f7c7d3c730ec75f0cd6021a51bd9bdb6f91711d92e332b21fef2221809659b56d538debaaf0bc02b0ef5f6 \ No newline at end of file diff --git a/test_data/mary3.tx b/test_data/mary3.tx new file mode 100644 index 00000000..e790f2f1 --- /dev/null +++ b/test_data/mary3.tx @@ -0,0 +1 @@ +84a500828258204c89f16dd1e38ce56ab8292ff9e840bda3f66ac14eec444aa7d111ad14d6c02a0a8258208f30562f0e45329904dcb1b8d5b24c4e0bd9f9bb11e4c42540330269596cbb30000181825839018e8f7a7073b8a95a4c1f1cf412b1042fca4945b89eb11754b3481b29fb2b631db76384f64dd94b47f97fc8c2a206764c17a1de7da2f70e831a59df79e3021a0002c71d031a0191f5ab048182008200581cfb2b631db76384f64dd94b47f97fc8c2a206764c17a1de7da2f70e83a10082825820af5d32c73a976fcd7abed9fa9c85e128862bd870c8ebc51f602a053343fab8dc5840e5f4046fd411e43bc027519572441b73b5c96b253da8e7358665efed189419d381cf67edbe29900b79f6c3e4eb62c7855bd5bb07504661cc59650c4e52f90504825820bc08576470a74179757177311dd8bdae9e56a637d58ed5d50a737d2b03dd9e68584034bd361d7b656b3b96357cc4f66d25756740f20ca2dbcf515c29f42d05733d004887a9312b52a73b60a1203f65db25b44dd1a8a89913f5d06b09eb0bd6570d04f5f6 \ No newline at end of file diff --git a/test_data/shelley4.tx b/test_data/shelley4.tx new file mode 100644 index 00000000..3cf50d53 --- /dev/null +++ b/test_data/shelley4.tx @@ -0,0 +1 @@ +84a40081825820e7db1f809fcc21d3dd108ced6218bf0f0cbb6a0f679f848ff1790b68d3a35872000181825839010c57a4aa08aaa7c42b45e4e9490151e2665dbb7d374e795ad5be5e4960562a0d213c675c2b84ee0e34eb377d4abbe82a4c256a0708baac251a0016e360021a0007a120031a010c59f8a200838258205df1be8b0071123c982a94c19c0c06485dbe9271e4381e8cf4fc2ed554ac133f5840c271a9d652ae95e6a8a5117c5026369235182da7e4fe040b02465089e5e2caf05063cf8b61601e6f6074c03f700bacaadcd62483c48d66a5f8d418a7c27c4b01825820403171966fadb1ce9b26852cb74018a04bc031a4aee92be39702b18efd75e058584039395858906ec9ab7540e79022b25a1f3bdfece09f9e2f36254eb5abe625b72dbd8179ece4c9fc1d537afce95b67d8095d29e1f3c50de4ecc30fd67e1ba440048258206311da054c5dfa3ac53c9fc3be859bd322f0712f7e093596fa5f6de031d95acd58403d7deba60f80a03f5bf172c1699f07ecef557d9509551cbe8d2b9000e903c3e3f60bb127c9c0b4cd5df84c01791b98e10fe19209088d0b085c74702b25914a0d01818202838200581ca96da581c39549aeda81f539ac3940ac0cb53657e774ca7e68f15ed98200581cccfcb3fed004562be1354c837a4a4b9f4b1c2b6705229efeedd12d4d8200581c74fcd61aecebe36aa6b6cd4314027282fa4b41c3ce8af17d9b77d0d1f5f6 \ No newline at end of file From 70ec8a123df2ec0e3171ca07f0b8823a3811e235 Mon Sep 17 00:00:00 2001 From: Ale Gadea Date: Fri, 23 Aug 2024 14:19:11 -0300 Subject: [PATCH 2/8] (feat) Improving compliance with respect of LEDGERS and LEDGER rules merged from pallas --- pallas-applying/src/lib.rs | 52 ++- pallas-applying/src/shelley_ma.rs | 433 +++++++++++++---------- pallas-applying/src/utils.rs | 27 +- pallas-applying/src/utils/environment.rs | 11 + pallas-applying/src/utils/validation.rs | 2 + pallas-applying/tests/alonzo.rs | 312 ++++++++++++++-- pallas-applying/tests/babbage.rs | 292 +++++++++++++-- pallas-applying/tests/byron.rs | 45 ++- pallas-applying/tests/shelley_ma.rs | 235 ++++++++++-- 9 files changed, 1094 insertions(+), 315 deletions(-) diff --git a/pallas-applying/src/lib.rs b/pallas-applying/src/lib.rs index e7facdc8..ac740cb4 100644 --- a/pallas-applying/src/lib.rs +++ b/pallas-applying/src/lib.rs @@ -9,41 +9,77 @@ pub mod utils; use alonzo::validate_alonzo_tx; use babbage::validate_babbage_tx; use byron::validate_byron_tx; +use pallas_primitives::alonzo::TransactionIndex; use pallas_traverse::{Era, MultiEraTx}; use shelley_ma::validate_shelley_ma_tx; pub use utils::{ - Environment, MultiEraProtocolParameters, UTxOs, - ValidationError::{TxAndProtParamsDiffer, UnknownProtParams}, + CertState, Environment, MultiEraProtocolParameters, UTxOs, + ValidationError::{ + EnvMissingAccountState, PParamsByronDoesntNeedAccountState, TxAndProtParamsDiffer, + UnknownProtParams, + }, ValidationResult, }; -pub fn validate(metx: &MultiEraTx, utxos: &UTxOs, env: &Environment) -> ValidationResult { - match env.prot_params() { - MultiEraProtocolParameters::Byron(bpp) => match metx { +/// Ledger sequence rule: LEDGERS +pub fn validate_txs( + metxs: &[MultiEraTx], + env: &Environment, + utxos: &UTxOs, + cert_state: &mut CertState, +) -> ValidationResult { + let mut delta_state: CertState = cert_state.clone(); + for (txix, metx) in metxs.iter().enumerate() { + validate_tx( + &metx, + txix.try_into().unwrap(), + env, + utxos, + &mut delta_state, + )?; + } + *cert_state = delta_state; + Ok(()) +} + +/// Ledger inference rule: LEDGER +pub fn validate_tx( + metx: &MultiEraTx, + txix: TransactionIndex, + env: &Environment, + utxos: &UTxOs, + cert_state: &mut CertState, +) -> ValidationResult { + let pp_acnt = (env.prot_params(), env.acnt()); + match pp_acnt { + (MultiEraProtocolParameters::Byron(bpp), None) => match metx { MultiEraTx::Byron(mtxp) => validate_byron_tx(mtxp, utxos, bpp, env.prot_magic()), _ => Err(TxAndProtParamsDiffer), }, - MultiEraProtocolParameters::Shelley(spp) => match metx { + (MultiEraProtocolParameters::Shelley(spp), Some(acnt)) => match metx { MultiEraTx::AlonzoCompatible(mtx, Era::Shelley) | MultiEraTx::AlonzoCompatible(mtx, Era::Allegra) | MultiEraTx::AlonzoCompatible(mtx, Era::Mary) => validate_shelley_ma_tx( mtx, + txix, utxos, + cert_state, spp, + &acnt, env.block_slot(), env.network_id(), &metx.era(), ), _ => Err(TxAndProtParamsDiffer), }, - MultiEraProtocolParameters::Alonzo(app) => match metx { + (MultiEraProtocolParameters::Alonzo(app), _) => match metx { MultiEraTx::AlonzoCompatible(mtx, Era::Alonzo) => { validate_alonzo_tx(mtx, utxos, app, env.block_slot(), env.network_id()) } _ => Err(TxAndProtParamsDiffer), }, - MultiEraProtocolParameters::Babbage(bpp) => match metx { + (MultiEraProtocolParameters::Babbage(bpp), _) => match metx { MultiEraTx::Babbage(mtx) => validate_babbage_tx( mtx, utxos, diff --git a/pallas-applying/src/shelley_ma.rs b/pallas-applying/src/shelley_ma.rs index 0764fe9f..698f707f 100644 --- a/pallas-applying/src/shelley_ma.rs +++ b/pallas-applying/src/shelley_ma.rs @@ -4,36 +4,40 @@ use crate::utils::{ 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, + AccountState, CertPointer, CertState, DState, PState, PoolParam, ShelleyMAError::*, ShelleyProtParams, UTxOs, ValidationError::{self, *}, ValidationResult, - CertPointer, DState, PState, PoolParam, CertState, AccountState }; use pallas_addresses::{PaymentKeyHash, ScriptHash, ShelleyAddress, ShelleyPaymentPart}; use pallas_codec::minicbor::encode; +use pallas_crypto::hash::Hasher as PallasHasher; use pallas_primitives::{ alonzo::{ - MintedTx, MintedWitnessSet, NativeScript, PolicyId, TransactionBody, TransactionOutput, - VKeyWitness, Value, StakeCredential::{self, *}, PoolKeyhash, Epoch, Coin, TransactionIndex, - Certificate::{self, *}, Genesishash, GenesisDelegateHash, VrfKeyhash, - MoveInstantaneousReward, InstantaneousRewardSource::*, InstantaneousRewardTarget::*, + Certificate::{self, *}, + Coin, Epoch, GenesisDelegateHash, Genesishash, + InstantaneousRewardSource::*, + InstantaneousRewardTarget::*, + MintedTx, MintedWitnessSet, MoveInstantaneousReward, NativeScript, PolicyId, PoolKeyhash, + StakeCredential::{self}, + TransactionBody, TransactionIndex, TransactionOutput, VKeyWitness, Value, VrfKeyhash, }, byron::TxOut, }; use pallas_traverse::{ - ComputeHash, Era, MultiEraInput, MultiEraOutput, time::Slot, - wellknown::GenesisValues, + time::Slot, wellknown::GenesisValues, ComputeHash, Era, MultiEraInput, MultiEraOutput, }; -use std::{cmp::max, ops::Deref, collections::HashMap}; -use pallas_crypto::hash::Hasher as PallasHasher; -use pallas_crypto::hash::Hash; // TODO: remove when fixed missing args -use std::str::FromStr; // TODO: remove when fixed missing args + +use std::{cmp::max, collections::HashMap, ops::Deref}; // TODO: remove when fixed missing args pub fn validate_shelley_ma_tx( mtx: &MintedTx, + txix: TransactionIndex, utxos: &UTxOs, + cert_state: &mut CertState, prot_pps: &ShelleyProtParams, + acnt: &AccountState, block_slot: &u64, network_id: &u8, era: &Era, @@ -44,24 +48,36 @@ pub fn validate_shelley_ma_tx( let stk_dep_count: &mut u64 = &mut 0; // count of key registrations (for deposits) let stk_refund_count: &mut u64 = &mut 0; // count of key deregs (for refunds) let pool_count: &mut u64 = &mut 0; // count of pool regs (for deposits) - + // FIXME: This section is entirely made up let stab_win = 129600; // FIXME: Found as "1.5 days" in unreliable sources. - let tx_ix: TransactionIndex = 0; // should be an argument - let acnt = AccountState { treasury: 261_254_564_000_000, reserves: 0 }; // should be an argument - let mut cert_state: CertState = CertState::default(); // should be an argument - let hash = Hash::from_str("FB2B631DB76384F64DD94B47F97FC8C2A206764C17A1DE7DA2F70E83").unwrap(); - cert_state.dstate.rewards.insert(AddrKeyhash(hash),0); - + check_ins_not_empty(tx_body)?; check_ins_in_utxos(tx_body, utxos)?; check_ttl(tx_body, block_slot)?; check_tx_size(&size, prot_pps)?; check_min_lovelace(tx_body, prot_pps, era)?; - check_certificates(&tx_body.certificates, tx_ix, &mut cert_state, stk_dep_count, - stk_refund_count, pool_count, &acnt, block_slot, &stab_win, prot_pps)?; - check_preservation_of_value(tx_body, utxos, stk_dep_count, stk_refund_count, - pool_count, era, prot_pps)?; + check_certificates( + &tx_body.certificates, + txix, + cert_state, + stk_dep_count, + stk_refund_count, + pool_count, + &acnt, + block_slot, + &stab_win, + prot_pps, + )?; + check_preservation_of_value( + tx_body, + utxos, + stk_dep_count, + stk_refund_count, + pool_count, + era, + prot_pps, + )?; check_fees(tx_body, &size, prot_pps)?; check_network_id(tx_body, network_id)?; check_metadata(tx_body, mtx)?; @@ -150,7 +166,7 @@ fn check_preservation_of_value( if !values_are_equal(&consumed, &produced) { Err(ShelleyMA(PreservationOfValue)) } else { - Ok(()) + Ok(()) } } @@ -183,7 +199,11 @@ fn get_consumed( } // TODO: Set right error message below. // Adding key refunds and minted assets - res = add_values(&res, &Value::Coin(prot_pps.key_deposit * *stk_refund_count), &neg_val_err)?; + res = add_values( + &res, + &Value::Coin(prot_pps.key_deposit * *stk_refund_count), + &neg_val_err, + )?; if let Some(m) = &tx_body.mint { res = add_minted_value(&res, m, &neg_val_err)?; } @@ -292,8 +312,12 @@ fn check_witnesses( } } let vkey_wits = &vk_wits.iter().map(|bv| bv.clone().1).collect(); - check_native_scripts(vkey_wits, &native_scripts, - &tx_body.validity_interval_start, &tx_body.ttl)?; + check_native_scripts( + vkey_wits, + &native_scripts, + &tx_body.validity_interval_start, + &tx_body.ttl, + )?; check_remaining_vk_wits(vk_wits, tx_hash) } @@ -396,82 +420,108 @@ fn check_certificates( prot_pps: &ShelleyProtParams, ) -> ValidationResult { if let Some(certs) = cert_opt { - let genesis = &GenesisValues::mainnet(); - let cepoch: Epoch = to_epoch(genesis, slot); - let mpc: Coin = prot_pps.min_pool_cost; - let mut ptr = CertPointer { slot: *slot, tx_ix, cert_ix: 0, }; - for (ix, cert) in certs.iter().enumerate() { - match cert { - StakeRegistration(stc) => { - *stk_dep_count += 1; - check_stake_registration(stc, &ptr, &mut cert_state.dstate)?; - }, - StakeDeregistration(stc) => { - check_stake_deregistration(stc, &mut cert_state.dstate)?; - *stk_refund_count += 1; - }, - StakeDelegation(stc, pk) => { - check_stake_delegation(stc, pk, &mut cert_state.dstate, &cert_state.pstate)?; - }, - PoolRegistration { operator, vrf_keyhash, pledge, cost, margin, reward_account, - pool_owners, relays, pool_metadata } => - { - if !cert_state.pstate.pool_params.contains_key(&operator) { - *pool_count += 1; - } - let pool_param = PoolParam { vrf_keyhash: *vrf_keyhash, pledge: *pledge, - cost: *cost, margin: margin.clone(), - reward_account: reward_account.clone(), - pool_owners: pool_owners.clone(), - relays: relays.clone(), - pool_metadata: pool_metadata.clone() - }; - check_pool_reg_or_update(operator, &pool_param, - &mpc, &mut cert_state.pstate)?; - }, - PoolRetirement(pk, repoch) => { - check_pool_retirement(pk, repoch, &cepoch, &prot_pps.maximum_epoch, - &mut cert_state.pstate)?; - }, - GenesisKeyDelegation(gkh, dkh, vrf) => { - check_genesis_key_delegation(gkh, dkh, vrf, slot, stab_win, - &mut cert_state.dstate)?; - }, - MoveInstantaneousRewardsCert(mir) => { - check_mir(mir, slot, stab_win, &mut cert_state.dstate, acnt)?; - } - } - ptr.cert_ix = ix as u32; // FIXME: Careful here, `ix` is `usize` - } - Ok(()) + let genesis = &GenesisValues::mainnet(); + let cepoch: Epoch = to_epoch(genesis, slot); + let mpc: Coin = prot_pps.min_pool_cost; + let mut ptr = CertPointer { + slot: *slot, + tx_ix, + cert_ix: 0, + }; + for (ix, cert) in certs.iter().enumerate() { + match cert { + StakeRegistration(stc) => { + *stk_dep_count += 1; + check_stake_registration(stc, &ptr, &mut cert_state.dstate)?; + } + StakeDeregistration(stc) => { + check_stake_deregistration(stc, &mut cert_state.dstate)?; + *stk_refund_count += 1; + } + StakeDelegation(stc, pk) => { + check_stake_delegation(stc, pk, &mut cert_state.dstate, &cert_state.pstate)?; + } + PoolRegistration { + operator, + vrf_keyhash, + pledge, + cost, + margin, + reward_account, + pool_owners, + relays, + pool_metadata, + } => { + if !cert_state.pstate.pool_params.contains_key(&operator) { + *pool_count += 1; + } + let pool_param = PoolParam { + vrf_keyhash: *vrf_keyhash, + pledge: *pledge, + cost: *cost, + margin: margin.clone(), + reward_account: reward_account.clone(), + pool_owners: pool_owners.clone(), + relays: relays.clone(), + pool_metadata: pool_metadata.clone(), + }; + check_pool_reg_or_update(operator, &pool_param, &mpc, &mut cert_state.pstate)?; + } + PoolRetirement(pk, repoch) => { + check_pool_retirement( + pk, + repoch, + &cepoch, + &prot_pps.maximum_epoch, + &mut cert_state.pstate, + )?; + } + GenesisKeyDelegation(gkh, dkh, vrf) => { + check_genesis_key_delegation( + gkh, + dkh, + vrf, + slot, + stab_win, + &mut cert_state.dstate, + )?; + } + MoveInstantaneousRewardsCert(mir) => { + check_mir(mir, slot, stab_win, &mut cert_state.dstate, acnt)?; + } + } + ptr.cert_ix = ix as u32; // FIXME: Careful here, `ix` is `usize` + } + Ok(()) } else { - Ok(()) + Ok(()) } } - fn check_stake_registration( stc: &StakeCredential, ptr: &CertPointer, ds: &mut DState, ) -> ValidationResult { - insert_or_err(&mut ds.rewards, stc, &0_u64, ShelleyMA(KeyAlreadyRegistered))?; + insert_or_err( + &mut ds.rewards, + stc, + &0_u64, + ShelleyMA(KeyAlreadyRegistered), + )?; insert_or_err(&mut ds.ptrs, ptr, stc, ShelleyMA(PointerInUse)) } -fn check_stake_deregistration( - stc: &StakeCredential, - ds: &mut DState, -) -> ValidationResult { +fn check_stake_deregistration(stc: &StakeCredential, ds: &mut DState) -> ValidationResult { match ds.rewards.get(stc) { - None => Err(ShelleyMA(KeyNotRegistered)), - Some(0) => { - ds.ptrs.retain(|_, v| v != stc); - ds.delegations.remove(stc); - ds.rewards.remove(stc); - Ok(()) - }, - Some(_) => Err(ShelleyMA(RewardsNotNull)), + None => Err(ShelleyMA(KeyNotRegistered)), + Some(0) => { + ds.ptrs.retain(|_, v| v != stc); + ds.delegations.remove(stc); + ds.rewards.remove(stc); + Ok(()) + } + Some(_) => Err(ShelleyMA(RewardsNotNull)), } } @@ -482,28 +532,29 @@ fn check_stake_delegation( ps: &PState, ) -> ValidationResult { if !ps.pool_params.contains_key(pk) { - Err(ShelleyMA(PoolNotRegistered)) + Err(ShelleyMA(PoolNotRegistered)) } else if ds.rewards.contains_key(stc) { - ds.delegations.insert(stc.clone(), *pk); - Ok(()) + ds.delegations.insert(stc.clone(), *pk); + Ok(()) } else { - Err(ShelleyMA(KeyNotRegistered)) + Err(ShelleyMA(KeyNotRegistered)) } } // Inserts a key-value pair if the key is not already in use, otherwise return // the provided error. -fn insert_or_err( - map: &mut HashMap, - key: &K, - value: &V, - error: E, -) -> Result<(), E> where K: Eq, K: std::hash::Hash, K: Clone, V: Clone { +fn insert_or_err(map: &mut HashMap, key: &K, value: &V, error: E) -> Result<(), E> +where + K: Eq, + K: std::hash::Hash, + K: Clone, + V: Clone, +{ if map.contains_key(key) { - return Err(error); + return Err(error); } else { - map.insert(key.clone(), value.clone()); - Ok(()) + map.insert(key.clone(), value.clone()); + Ok(()) } } @@ -514,16 +565,18 @@ fn check_pool_reg_or_update( ps: &mut PState, ) -> ValidationResult { if pool_param.cost < *min_pool_cost { - Err(ShelleyMA(PoolCostBelowMin)) + Err(ShelleyMA(PoolCostBelowMin)) } else if ps.pool_params.contains_key(pool_hash) { - // Updating - ps.fut_pool_params.insert(pool_hash.clone(), (*pool_param).clone()); - ps.retiring.remove(&pool_hash); - Ok(()) + // Updating + ps.fut_pool_params + .insert(pool_hash.clone(), (*pool_param).clone()); + ps.retiring.remove(&pool_hash); + Ok(()) } else { - // Registering - ps.pool_params.insert(pool_hash.clone(), (*pool_param).clone()); - Ok(()) + // Registering + ps.pool_params + .insert(pool_hash.clone(), (*pool_param).clone()); + Ok(()) } } @@ -535,13 +588,13 @@ fn check_pool_retirement( ps: &mut PState, ) -> ValidationResult { if !ps.pool_params.contains_key(&pool_hash) { - return Err(ShelleyMA(PoolNotRegistered)); + return Err(ShelleyMA(PoolNotRegistered)); } if (*cepoch < *repoch) & (*repoch <= *cepoch + *emax as u64) { - ps.retiring.insert(pool_hash.clone(), *repoch); - Ok(()) + ps.retiring.insert(pool_hash.clone(), *repoch); + Ok(()) } else { - Err(ShelleyMA(PoolNotRegistered)) + Err(ShelleyMA(PoolNotRegistered)) } } @@ -553,25 +606,35 @@ fn check_genesis_key_delegation( stab_win: &Slot, ds: &mut DState, ) -> ValidationResult { - let cod = ds.gen_delegs - .iter().filter(|kv| kv.0 != gkh) - .map(|kv| kv.1).collect::>(); - let fod = ds.fut_gen_delegs - .iter().filter(|kv| kv.0.1 != *gkh) - .map(|kv| kv.1).collect::>(); + let cod = ds + .gen_delegs + .iter() + .filter(|kv| kv.0 != gkh) + .map(|kv| kv.1) + .collect::>(); + let fod = ds + .fut_gen_delegs + .iter() + .filter(|kv| kv.0 .1 != *gkh) + .map(|kv| kv.1) + .collect::>(); let curr_keyhashes = cod.iter().map(|v| v.0.clone()).collect::>(); let curr_vrfs = cod.iter().map(|v| v.1).collect::>(); let fut_keyhashes = fod.iter().map(|v| v.0.clone()).collect::>(); let fut_vrfs = fod.iter().map(|v| v.1).collect::>(); - if curr_keyhashes.contains(dkh) | fut_keyhashes.contains(dkh) - | curr_vrfs.contains(vrf) | fut_vrfs.contains(vrf) { - Err(ShelleyMA(DuplicateGenesisDelegate)) + if curr_keyhashes.contains(dkh) + | fut_keyhashes.contains(dkh) + | curr_vrfs.contains(vrf) + | fut_vrfs.contains(vrf) + { + Err(ShelleyMA(DuplicateGenesisDelegate)) } else if !ds.gen_delegs.contains_key(gkh) { - Err(ShelleyMA(GenesisKeyNotInMapping)) + Err(ShelleyMA(GenesisKeyNotInMapping)) } else { - let gen_slot: Slot = *slot + *stab_win; - ds.fut_gen_delegs.insert((gen_slot, gkh.clone()), (dkh.clone(), vrf.clone())); - Ok(()) + let gen_slot: Slot = *slot + *stab_win; + ds.fut_gen_delegs + .insert((gen_slot, gkh.clone()), (dkh.clone(), vrf.clone())); + Ok(()) } } @@ -583,35 +646,35 @@ fn check_mir( acnt: &AccountState, ) -> ValidationResult { let genesis = &GenesisValues::mainnet(); - if !(*slot < first_slot(genesis, &(to_epoch(genesis, slot)+1)) - *stab_win) { - Err(ShelleyMA(MIRCertificateTooLateinEpoch)) + if !(*slot < first_slot(genesis, &(to_epoch(genesis, slot) + 1)) - *stab_win) { + Err(ShelleyMA(MIRCertificateTooLateinEpoch)) } else { - let (ir_reserves, ir_treasury) = ds.inst_rewards.clone(); - let (pot, ir_pot) = match mir.source { - Reserves => (acnt.reserves, ir_reserves.clone()), - Treasury => (acnt.treasury, ir_treasury.clone()), - }; - let mut combined: HashMap = HashMap::new(); - match &mir.target { - StakeCredentials(kvp) => { - let mut kvv: Vec<(StakeCredential, u64)> = // TODO: Err if the value is negative + let (ir_reserves, ir_treasury) = ds.inst_rewards.clone(); + let (pot, ir_pot) = match mir.source { + Reserves => (acnt.reserves, ir_reserves.clone()), + Treasury => (acnt.treasury, ir_treasury.clone()), + }; + let mut combined: HashMap = HashMap::new(); + match &mir.target { + StakeCredentials(kvp) => { + let mut kvv: Vec<(StakeCredential, u64)> = // TODO: Err if the value is negative kvp.iter().map(|kv| (kv.clone().0, kv.clone().1 as u64)).collect(); - kvv.extend(ir_pot); - for (key, value) in kvv { - combined.insert(key, value); - } - }, - _ => (), - } - if combined.iter().map(|kv| kv.1).sum::() > pot { - return Err(ShelleyMA(InsufficientForInstantaneousRewards)); - } else { - ds.inst_rewards = match mir.source { - Reserves => (combined, ir_reserves), - Treasury => (ir_treasury, combined), - } - }; - Ok(()) + kvv.extend(ir_pot); + for (key, value) in kvv { + combined.insert(key, value); + } + } + _ => (), + } + if combined.iter().map(|kv| kv.1).sum::() > pot { + return Err(ShelleyMA(InsufficientForInstantaneousRewards)); + } else { + ds.inst_rewards = match mir.source { + Reserves => (combined, ir_reserves), + Treasury => (ir_treasury, combined), + } + }; + Ok(()) } } @@ -634,9 +697,9 @@ fn check_native_scripts( upp_bnd: &Option, ) -> ValidationResult { for native_script in native_scripts { - if !eval_native_script(vkey_wits, native_script, low_bnd, upp_bnd) { - return Err(ShelleyMA(ScriptDenial)); - } + if !eval_native_script(vkey_wits, native_script, low_bnd, upp_bnd) { + return Err(ShelleyMA(ScriptDenial)); + } } Ok(()) } @@ -648,33 +711,33 @@ fn eval_native_script( upp_bnd: &Option, ) -> bool { match native_script { - NativeScript::ScriptAll(scripts) => - scripts.iter() - .all(|scr| eval_native_script(vkey_wits, scr, low_bnd, upp_bnd)), - NativeScript::ScriptAny(scripts) => - scripts.iter() - .any(|scr| eval_native_script(vkey_wits, scr, low_bnd, upp_bnd)), - NativeScript::ScriptPubkey(hash) => { - vkey_wits.iter() - .any(|vkey_wit| PallasHasher::<224>::hash(&vkey_wit.vkey.clone()) == *hash) - }, - NativeScript::ScriptNOfK(val, scripts) => { - let count = scripts.iter() - .map(|scr| eval_native_script(vkey_wits, scr, low_bnd, upp_bnd)) - .fold(0, |x, y| x + y as u32); - count >= *val - }, - NativeScript::InvalidBefore(val) => { - match low_bnd { - Some(time) => val >= time, - None => false, // as per mary-ledger.pdf, p.20 - } - }, - NativeScript::InvalidHereafter(val) => { - match upp_bnd { - Some(time) => val <= time, - None => false, // as per mary-ledger.pdf, p.20 - } - }, + NativeScript::ScriptAll(scripts) => scripts + .iter() + .all(|scr| eval_native_script(vkey_wits, scr, low_bnd, upp_bnd)), + NativeScript::ScriptAny(scripts) => scripts + .iter() + .any(|scr| eval_native_script(vkey_wits, scr, low_bnd, upp_bnd)), + NativeScript::ScriptPubkey(hash) => vkey_wits + .iter() + .any(|vkey_wit| PallasHasher::<224>::hash(&vkey_wit.vkey.clone()) == *hash), + NativeScript::ScriptNOfK(val, scripts) => { + let count = scripts + .iter() + .map(|scr| eval_native_script(vkey_wits, scr, low_bnd, upp_bnd)) + .fold(0, |x, y| x + y as u32); + count >= *val + } + NativeScript::InvalidBefore(val) => { + match low_bnd { + Some(time) => val >= time, + None => false, // as per mary-ledger.pdf, p.20 + } + } + NativeScript::InvalidHereafter(val) => { + match upp_bnd { + Some(time) => val <= time, + None => false, // as per mary-ledger.pdf, p.20 + } + } } } diff --git a/pallas-applying/src/utils.rs b/pallas-applying/src/utils.rs index a4e7946f..7334cd99 100644 --- a/pallas-applying/src/utils.rs +++ b/pallas-applying/src/utils.rs @@ -12,14 +12,14 @@ use pallas_codec::{ use pallas_crypto::key::ed25519::{PublicKey, Signature}; use pallas_primitives::{ alonzo::{ - AssetName, AuxiliaryData, Coin, MintedTx as AlonzoMintedTx, Multiasset, NativeScript, - NetworkId, PlutusScript, PolicyId, VKeyWitness, Value, StakeCredential, PoolKeyhash, - Epoch, VrfKeyhash, Relay, UnitInterval, RewardAccount, PoolMetadata, AddrKeyhash, - TransactionIndex, Genesishash, GenesisDelegateHash, + AddrKeyhash, AssetName, AuxiliaryData, Coin, Epoch, GenesisDelegateHash, Genesishash, + MintedTx as AlonzoMintedTx, Multiasset, NativeScript, NetworkId, PlutusScript, PolicyId, + PoolKeyhash, PoolMetadata, Relay, RewardAccount, StakeCredential, TransactionIndex, + UnitInterval, VKeyWitness, Value, VrfKeyhash, }, babbage::{MintedTx as BabbageMintedTx, PlutusV2Script}, }; -use pallas_traverse::{MultiEraInput, MultiEraOutput, time::Slot}; +use pallas_traverse::{time::Slot, MultiEraInput, MultiEraOutput}; use std::collections::HashMap; use std::ops::Deref; pub use validation::*; @@ -364,9 +364,12 @@ pub struct CertPointer { pub type GenesisDelegation = HashMap; pub type FutGenesisDelegation = HashMap<(Slot, Genesishash), (GenesisDelegateHash, VrfKeyhash)>; -pub type InstantaneousRewards = (HashMap, HashMap); +pub type InstantaneousRewards = ( + HashMap, + HashMap, +); -#[derive(Default)] // for testing +#[derive(Default, Clone)] // for testing pub struct DState { pub rewards: HashMap, pub delegations: HashMap, @@ -389,7 +392,7 @@ pub struct PoolParam { pub pool_metadata: Nullable, } -#[derive(Default)] // for testing +#[derive(Default, Clone)] // for testing pub struct PState { pub pool_params: HashMap, pub fut_pool_params: HashMap, @@ -399,14 +402,8 @@ pub struct PState { // Originally `DPState` in ShelleyMA specs, then updated to // `CertState` in Haskell sources at Intersect (#3369). #[non_exhaustive] -#[derive(Default)] // for testing +#[derive(Default, Clone)] // for testing pub struct CertState { pub pstate: PState, pub dstate: DState, } - -#[derive(Default)] // for testing -pub struct AccountState { - pub treasury: Coin, - pub reserves: Coin, -} diff --git a/pallas-applying/src/utils/environment.rs b/pallas-applying/src/utils/environment.rs index c9c9c5cd..84b49cbf 100644 --- a/pallas-applying/src/utils/environment.rs +++ b/pallas-applying/src/utils/environment.rs @@ -178,12 +178,19 @@ pub struct ConwayProtParams { pub minfee_refscript_cost_per_byte: UnitInterval, } +#[derive(Default, Debug)] +pub struct AccountState { + pub treasury: Coin, + pub reserves: Coin, +} + #[derive(Debug)] pub struct Environment { pub prot_params: MultiEraProtocolParameters, pub prot_magic: u32, pub block_slot: u64, pub network_id: u8, + pub acnt: Option, } impl Environment { @@ -202,4 +209,8 @@ impl Environment { pub fn network_id(&self) -> &u8 { &self.network_id } + + pub fn acnt(&self) -> &Option { + &self.acnt + } } diff --git a/pallas-applying/src/utils/validation.rs b/pallas-applying/src/utils/validation.rs index c05b55f4..0fe6d8f1 100644 --- a/pallas-applying/src/utils/validation.rs +++ b/pallas-applying/src/utils/validation.rs @@ -4,6 +4,8 @@ #[non_exhaustive] pub enum ValidationError { TxAndProtParamsDiffer, + PParamsByronDoesntNeedAccountState, + EnvMissingAccountState, UnknownProtParams, Byron(ByronError), ShelleyMA(ShelleyMAError), diff --git a/pallas-applying/tests/alonzo.rs b/pallas-applying/tests/alonzo.rs index 5a404984..ffc743b5 100644 --- a/pallas-applying/tests/alonzo.rs +++ b/pallas-applying/tests/alonzo.rs @@ -5,9 +5,10 @@ use common::*; use pallas_addresses::{Address, Network, ShelleyAddress, ShelleyPaymentPart}; use pallas_applying::{ utils::{ - AlonzoError, AlonzoProtParams, Environment, MultiEraProtocolParameters, ValidationError::*, + AccountState, AlonzoError, AlonzoProtParams, Environment, MultiEraProtocolParameters, + ValidationError::*, }, - validate, UTxOs, + validate_txs, CertState, UTxOs, }; use pallas_codec::{ minicbor::{ @@ -43,13 +44,21 @@ mod alonzo_tests { None, )], ); + + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_334()), prot_magic: 764824073, block_slot: 44237276, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => (), Err(err) => panic!("Unexpected error ({:?})", err), } @@ -143,13 +152,21 @@ mod alonzo_tests { None, )], ); + + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()), prot_magic: 764824073, block_slot: 58924928, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => (), Err(err) => panic!("Unexpected error ({:?})", err), } @@ -170,13 +187,21 @@ mod alonzo_tests { None, )], ); + + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()), prot_magic: 764824073, block_slot: 6447035, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => (), Err(err) => panic!("Unexpected error ({:?})", err), } @@ -197,13 +222,21 @@ mod alonzo_tests { None, )], ); + + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()), prot_magic: 764824073, block_slot: 6447038, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => (), Err(err) => panic!("Unexpected error ({:?})", err), } @@ -229,13 +262,21 @@ mod alonzo_tests { 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 acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_334()), prot_magic: 764824073, block_slot: 44237276, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("Inputs set should not be empty"), Err(err) => match err { Alonzo(AlonzoError::TxInsEmpty) => (), @@ -252,13 +293,21 @@ mod alonzo_tests { let mtx: MintedTx = minted_tx_from_cbor(&cbor_bytes); let utxos: UTxOs = UTxOs::new(); let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Alonzo); + + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_334()), prot_magic: 764824073, block_slot: 44237276, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("All inputs should be within the UTxO set"), Err(err) => match err { Alonzo(AlonzoError::InputNotInUTxO) => (), @@ -288,13 +337,21 @@ mod alonzo_tests { 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 acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_334()), prot_magic: 764824073, block_slot: 44237276, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("Validity interval lower bound should have been reached"), Err(err) => match err { Alonzo(AlonzoError::BlockPrecedesValInt) => (), @@ -324,13 +381,21 @@ mod alonzo_tests { 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 acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_334()), prot_magic: 764824073, block_slot: 44237276, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("Validity interval upper bound should not have been surpassed"), Err(err) => match err { Alonzo(AlonzoError::BlockExceedsValInt) => (), @@ -356,13 +421,21 @@ mod alonzo_tests { ); let mut alonzo_prot_params: AlonzoProtParams = mk_params_epoch_334(); alonzo_prot_params.minfee_a = 79; // This value was 44 during Alonzo on mainnet + + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Alonzo(alonzo_prot_params), prot_magic: 764824073, block_slot: 44237276, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("Fee should not be below minimum"), Err(err) => match err { Alonzo(AlonzoError::FeeBelowMin) => (), @@ -465,13 +538,21 @@ mod alonzo_tests { 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 acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()), prot_magic: 764824073, block_slot: 58924928, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("No collateral inputs"), Err(err) => match err { Alonzo(AlonzoError::CollateralMissing) => (), @@ -571,13 +652,20 @@ mod alonzo_tests { ); let mut alonzo_prot_params: AlonzoProtParams = mk_params_epoch_300(); alonzo_prot_params.max_collateral_inputs = 0; // This value was 3 during Alonzo on mainnet + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Alonzo(alonzo_prot_params), prot_magic: 764824073, block_slot: 58924928, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("Number of collateral inputs should be within limits"), Err(err) => match err { Alonzo(AlonzoError::TooManyCollaterals) => (), @@ -696,13 +784,20 @@ mod alonzo_tests { ); utxos.insert(multi_era_in, multi_era_out); let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Alonzo); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()), prot_magic: 764824073, block_slot: 58924928, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("Collateral inputs should be verification-key locked"), Err(err) => match err { Alonzo(AlonzoError::CollateralNotVKeyLocked) => (), @@ -810,13 +905,20 @@ mod alonzo_tests { None, )], ); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()), prot_magic: 764824073, block_slot: 58924928, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("Collateral inputs should contain only lovelace"), Err(err) => match err { Alonzo(AlonzoError::NonLovelaceCollateral) => (), @@ -915,13 +1017,20 @@ mod alonzo_tests { ); let mut alonzo_prot_params: AlonzoProtParams = mk_params_epoch_300(); alonzo_prot_params.collateral_percentage = 700; // This was 150 during Alonzo on mainnet. + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Alonzo(alonzo_prot_params), prot_magic: 764824073, block_slot: 58924928, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("Collateral inputs should contain the minimum lovelace"), Err(err) => match err { Alonzo(AlonzoError::CollateralMinLovelace) => (), @@ -951,13 +1060,20 @@ mod alonzo_tests { 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 acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_334()), prot_magic: 764824073, block_slot: 44237276, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("Preservation of value does not hold"), Err(err) => match err { Alonzo(AlonzoError::PreservationOfValue) => (), @@ -1007,13 +1123,20 @@ mod alonzo_tests { None, )], ); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_334()), prot_magic: 764824073, block_slot: 44237276, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("Output network ID should match environment network ID"), Err(err) => match err { Alonzo(AlonzoError::OutputWrongNetworkID) => (), @@ -1045,13 +1168,20 @@ mod alonzo_tests { None, )], ); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_334()), prot_magic: 764824073, block_slot: 44237276, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("Transaction network ID should match environment network ID"), Err(err) => match err { Alonzo(AlonzoError::TxWrongNetworkID) => (), @@ -1151,13 +1281,20 @@ mod alonzo_tests { let mut alonzo_prot_params: AlonzoProtParams = mk_params_epoch_300(); alonzo_prot_params.max_tx_ex_units.mem = 4649575; // This is 1 lower than that of the transaction alonzo_prot_params.max_tx_ex_units.steps = 1765246503; // This is 1 lower than that of the transaction + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Alonzo(alonzo_prot_params), prot_magic: 764824073, block_slot: 58924928, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("Transaction ex units should be below maximum"), Err(err) => match err { Alonzo(AlonzoError::TxExUnitsExceeded) => (), @@ -1184,13 +1321,20 @@ mod alonzo_tests { ); let mut alonzo_prot_params: AlonzoProtParams = mk_params_epoch_334(); alonzo_prot_params.max_transaction_size = 158; // 1 byte less than the size of the tx + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Alonzo(alonzo_prot_params), prot_magic: 764824073, block_slot: 44237276, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!( "Transaction size should not exceed the maximum allowed by the protocol parameter" ), @@ -1301,13 +1445,20 @@ mod alonzo_tests { 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 acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()), prot_magic: 764824073, block_slot: 58924928, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("All required signers should have signed the transaction"), Err(err) => match err { Alonzo(AlonzoError::ReqSignerMissing) => (), @@ -1337,13 +1488,20 @@ mod alonzo_tests { mtx.transaction_witness_set = Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap(); let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Alonzo); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_334()), prot_magic: 764824073, block_slot: 44237276, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("Missing verification key witness"), Err(err) => match err { Alonzo(AlonzoError::VKWitnessMissing) => (), @@ -1380,13 +1538,20 @@ mod alonzo_tests { mtx.transaction_witness_set = Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap(); let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Alonzo); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_334()), prot_magic: 764824073, block_slot: 44237276, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("Witness signature should verify the transaction"), Err(err) => match err { Alonzo(AlonzoError::VKWrongSignature) => (), @@ -1489,13 +1654,20 @@ mod alonzo_tests { mtx.transaction_witness_set = Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap(); let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Alonzo); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()), prot_magic: 764824073, block_slot: 58924928, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("Missing Plutus script"), Err(err) => match err { Alonzo(AlonzoError::ScriptWitnessMissing) => (), @@ -1606,13 +1778,20 @@ mod alonzo_tests { mtx.transaction_witness_set = Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap(); let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Alonzo); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()), prot_magic: 764824073, block_slot: 58924928, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("Unneeded Plutus script"), Err(err) => match err { Alonzo(AlonzoError::UnneededNativeScript) => (), @@ -1642,13 +1821,20 @@ mod alonzo_tests { mtx.transaction_witness_set = Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap(); let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Alonzo); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()), prot_magic: 764824073, block_slot: 6447035, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("Minting policy is not supported by a matching native script"), Err(err) => match err { Alonzo(AlonzoError::MintingLacksPolicy) => (), @@ -1751,13 +1937,20 @@ mod alonzo_tests { mtx.transaction_witness_set = Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap(); let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Alonzo); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()), prot_magic: 764824073, block_slot: 58924928, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("Missing datum"), Err(err) => match err { Alonzo(AlonzoError::DatumMissing) => (), @@ -1866,13 +2059,20 @@ mod alonzo_tests { mtx.transaction_witness_set = Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap(); let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Alonzo); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()), prot_magic: 764824073, block_slot: 58924928, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("Unneeded datum"), Err(err) => match err { Alonzo(AlonzoError::UnneededDatum) => (), @@ -1982,13 +2182,20 @@ mod alonzo_tests { mtx.transaction_witness_set = Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap(); let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Alonzo); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()), prot_magic: 764824073, block_slot: 58924928, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("Unneeded redeemer"), Err(err) => match err { Alonzo(AlonzoError::UnneededRedeemer) => (), @@ -2091,13 +2298,20 @@ mod alonzo_tests { mtx.transaction_witness_set = Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap(); let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Alonzo); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()), prot_magic: 764824073, block_slot: 58924928, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("Redeemer missing"), Err(err) => match err { Alonzo(AlonzoError::RedeemerMissing) => (), @@ -2122,13 +2336,20 @@ mod alonzo_tests { None, )], ); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()), prot_magic: 764824073, block_slot: 6447038, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("Transaction auxiliary data removed"), Err(err) => match err { Alonzo(AlonzoError::MetadataHash) => (), @@ -2154,13 +2375,20 @@ mod alonzo_tests { ); let mut alonzo_prot_params: AlonzoProtParams = mk_params_epoch_334(); alonzo_prot_params.ada_per_utxo_byte = 10000000; // This was 34482 during Alonzo on mainnet. + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Alonzo(alonzo_prot_params), prot_magic: 764824073, block_slot: 44237276, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("Output minimum lovelace is unreached"), Err(err) => match err { Alonzo(AlonzoError::MinLovelaceUnreached) => (), @@ -2186,13 +2414,20 @@ mod alonzo_tests { ); let mut alonzo_prot_params: AlonzoProtParams = mk_params_epoch_334(); alonzo_prot_params.max_value_size = 0; // This was 5000 during Alonzo on mainnet + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Alonzo(alonzo_prot_params), prot_magic: 764824073, block_slot: 44237276, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("Max value size exceeded"), Err(err) => match err { Alonzo(AlonzoError::MaxValSizeExceeded) => (), @@ -2299,13 +2534,20 @@ mod alonzo_tests { mtx.transaction_witness_set = Decode::decode(&mut Decoder::new(tx_witness_set_buf.as_slice()), &mut ()).unwrap(); let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Alonzo); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()), prot_magic: 764824073, block_slot: 58924928, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("Wrong script integrity hash"), Err(err) => match err { Alonzo(AlonzoError::ScriptIntegrityHash) => (), diff --git a/pallas-applying/tests/babbage.rs b/pallas-applying/tests/babbage.rs index 6acc31a5..7e21be9a 100644 --- a/pallas-applying/tests/babbage.rs +++ b/pallas-applying/tests/babbage.rs @@ -5,10 +5,10 @@ use hex; use pallas_addresses::{Address, Network, ShelleyAddress, ShelleyPaymentPart}; use pallas_applying::{ utils::{ - BabbageError, BabbageProtParams, Environment, MultiEraProtocolParameters, + AccountState, BabbageError, BabbageProtParams, Environment, MultiEraProtocolParameters, ValidationError::*, }, - validate, UTxOs, + validate_txs, CertState, UTxOs, }; use pallas_codec::utils::{Bytes, CborWrap, KeepRaw, KeyValuePairs}; use pallas_codec::{ @@ -50,13 +50,20 @@ mod babbage_tests { None, )]; let utxos: UTxOs = mk_utxo_for_babbage_tx(&mtx.transaction_body, tx_outs_info); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Babbage(mk_mainnet_params_epoch_365()), prot_magic: 764824073, block_slot: 72316896, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => (), Err(err) => assert!(false, "Unexpected error ({:?})", err), } @@ -120,13 +127,20 @@ mod babbage_tests { None, )]; add_collateral_babbage(&mtx.transaction_body, &mut utxos, collateral_info); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Babbage(mk_mainnet_params_epoch_365()), prot_magic: 764824073, block_slot: 72317003, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => (), Err(err) => assert!(false, "Unexpected error ({:?})", err), } @@ -199,13 +213,20 @@ mod babbage_tests { Some(CborWrap(PseudoScript::PlutusV2Script(PlutusV2Script(Bytes::from(hex::decode("5909fe010000323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232222323232533535533357346064606a0062646464642466002008004a666ae68c0d8c0e00044c848c004008c078d5d0981b8008191baa357426ae88c0d80154ccd5cd1819981b0008991919191919191919191919191919191919191919190919999999999980080b80a8098088078068058048038028018011aba135744004666068eb88004d5d08009aba2002357420026ae88008cc0c9d71aba1001357440046ae84004d5d10011aba1001357440046ae84004d5d10011aba1001357440046ae84004d5d10011981300f1aba1001357440046ae84004d5d1181b001198111192999ab9a30353038001132321233001003002301d357426ae88c0e0008c078d5d0981b8008191baa00135742606a0020606ea8d5d0981a001817911a8011111111111111a80691919299aa99a998149aa99a80109815a481035054380022100203d00303903a03a1533501213302549101350033302330340362350012232333027303803a235001223500122533533302b0440040062153353333026303e040223500222533500321533533303104a0030062153353302b0010031303f3305722533500104c221350022253353305100200a100313304d33047002001300600300215335330370010031303f333302d04b0043370200200600409209008e60720020044266060920102313000333573466e20ccd54c0fc104c0a8cc0f1c024000400266aa608008246a00209600200809208e266ae712410231310004813357389201023132000470023335530360393501b0403501b04233355303603922533535002222253353302200800413038003042213303d001002100103f010333301c303403622350022253353303c00b002100313333020303803a235001222533533302a0210030012133330260220043355303e03f235001223303d002333500120012235002223500322330433370000800466aa608e09046a002446608c004666a0024002e008004ccc0c013400c0048004ccc09c11000c0040084cccc09408400c00800400c0040f140044cc0952410134003330233034036235001223303b00a0025001153353355303403523500122350012222302c533350021303104821001213304e2253350011303404a221350022253353304800200710011300600300c0011302a49010136002213355303603723500122350012222302e533350021303304a2100121330502253350011303604c221350022253353304a00200710011300600300e0033335530310342253353353530283500203f03d203f253353303c001330482253350011302e044221350022253353303000200a135302f001223350022303504b20011300600301003b1302c4901013300133037002001100103a00d1120011533573892010350543500165333573460640020502a666ae68c0c400409c0b8c0ccdd50019baa00133019223355301f020235001223301e002335530220232350012233021002333500137009000380233700002900000099aa980f81011a800911980f001199a800919aa981181211a8009119811001180880080091199806815001000919aa981181211a80091198110011809000800999804012801000812111919807198021a8018139a801013a99a9a80181490a99a8011099a801119a80111980400100091101711119a80210171112999ab9a3370e00c0062a666ae68cdc38028010998068020008158158120a99a80090120121a8008141119a801119a8011198128010009014119a801101411981280100091199ab9a3370e00400204604a44446666aa00866032444600660040024002006002004444466aa603803a46a0024466036004666a0024002052400266600a0080026603c66030006004046444666aa603003603866aa603403646a00244660320046010002666aa6030036446a00444a66a666aa603a03e60106603444a66a00404a200204e46a002446601400400a00c200626604000800604200266aa603403646a00244660320046605e44a66a002260160064426a00444a66a6601800401022444660040140082600c00600800446602644666a0060420040026a00204242444600600842444600200844604e44a66a0020364426a00444a66a6601000400e2602a0022600c0064466aa0046602000603600244a66a004200202e44a66a00202e266ae7000806c8c94ccd5cd180f9811000899190919800801801198079192999ab9a3022302500113232123300100300233301075c464a666ae68c094c0a00044c8cc0514cd4cc028005200110011300e4901022d330033301375c464a66a660180029000080089808249022d3200375a0026ae84d5d118140011bad35742604e0020446ea8004d5d09aba23025002300c35742604800203e6ea8004d5d09aba23022002375c6ae84c084004070dd500091199ab9a3371200400203202e46a002444400844a666ae68cdc79a80100b1a80080b0999ab9a3370e6a0040306a00203002a02e024464a666ae68c06cc0780044c8c8c8c8c8c8c8c848cccc00402401c00c008d5d09aba20045333573466e1d2004001132122230020043574260460042a666ae68c0880044c84888c004010dd71aba1302300215333573460420022244400603c60460026ea8d5d08009aba200233300a75c66014eb9d69aba100135744603c004600a6ae84c074004060dd50009299ab9c001162325333573460326038002264646424660020060046eb4d5d09aba2301d003533357346034603a00226eb8d5d0980e00080b9baa35742603600202c6ea80048c94ccd5cd180c180d80089919191909198008028012999ab9a301b00113232300953335734603c00226464646424466600200c0080066eb4d5d09aba2002375a6ae84004d5d118100019bad35742603e0042a666ae68c0740044c8488c00800cc020d5d0980f80100d180f8009baa35742603a0042a666ae68c070004044060c074004dd51aba135744603600460066ae84c068004054dd5000919192999ab9a30190011321223001003375c6ae84c06800854ccd5cd180c00089909118010019bae35742603400402a60340026ea80048488c00800c888cc06888cccd55cf800900911919807198041803980e8009803180e00098021aba2003357420040166eac0048848cc00400c00888cc05c88cccd55cf800900791980518029aba10023003357440040106eb0004c05088448894cd40044008884cc014008ccd54c01c028014010004c04c88448894cd40044d400c040884ccd4014040c010008ccd54c01c024014010004c0488844894cd4004024884cc020c010008cd54c01801c0100044800488488cc00401000cc03c8894cd40080108854cd4cc02000800c01c4cc01400400c4014400888ccd5cd19b8f0020010030051001220021001220011533573892010350543100164901022d31004901013700370e90001b874800955cf2ab9d2323001001223300330020020011").unwrap()))))), )]; add_ref_input_babbage(&mtx.transaction_body, &mut utxos, ref_input_info); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Babbage(mk_mainnet_params_epoch_380()), prot_magic: 764824073, block_slot: 78797255, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => (), Err(err) => assert!(false, "Unexpected error ({:?})", err), } @@ -271,13 +292,20 @@ mod babbage_tests { None, )]; add_collateral_babbage(&mtx.transaction_body, &mut utxos, collateral_info); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Babbage(mk_preview_params_epoch_30()), prot_magic: 2, block_slot: 2592005, network_id: 0, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => (), Err(err) => assert!(false, "Unexpected error ({:?})", err), } @@ -348,13 +376,20 @@ mod babbage_tests { None, )]; add_collateral_babbage(&mtx.transaction_body, &mut utxos, collateral_info); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Babbage(mk_preprod_params_epoch_100()), prot_magic: 1, block_slot: 41558438, network_id: 0, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => (), Err(err) => assert!(false, "Unexpected error ({:?})", err), } @@ -446,13 +481,20 @@ mod babbage_tests { None, )]; add_collateral_babbage(&mtx.transaction_body, &mut utxos, collateral_info); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Babbage(mk_mainnet_params_epoch_365()), prot_magic: 764824073, block_slot: 72316896, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => (), Err(err) => assert!(false, "Unexpected error ({:?})", err), } @@ -513,13 +555,20 @@ mod babbage_tests { None, )]; add_collateral_babbage(&mtx.transaction_body, &mut utxos, collateral_info); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Babbage(mk_mainnet_params_epoch_365()), prot_magic: 764824073, block_slot: 72316896, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => (), Err(err) => assert!(false, "Unexpected error ({:?})", err), } @@ -549,13 +598,20 @@ mod babbage_tests { mtx.transaction_body = Decode::decode(&mut Decoder::new(&tx_buf.as_slice()), &mut ()).unwrap(); let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Babbage(mk_mainnet_params_epoch_365()), prot_magic: 764824073, block_slot: 72316896, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => assert!(false, "Inputs set should not be empty"), Err(err) => match err { Babbage(BabbageError::TxInsEmpty) => (), @@ -572,13 +628,20 @@ mod babbage_tests { let mtx: MintedTx = babbage_minted_tx_from_cbor(&cbor_bytes); let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); let utxos: UTxOs = UTxOs::new(); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Babbage(mk_mainnet_params_epoch_365()), prot_magic: 764824073, block_slot: 72316896, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => assert!(false, "All inputs should be within the UTxO set"), Err(err) => match err { Babbage(BabbageError::InputNotInUTxO) => (), @@ -612,13 +675,20 @@ mod babbage_tests { mtx.transaction_body = Decode::decode(&mut Decoder::new(&tx_buf.as_slice()), &mut ()).unwrap(); let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Babbage(mk_mainnet_params_epoch_365()), prot_magic: 764824073, block_slot: 72316896, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => assert!( false, "Validity interval lower bound should have been reached" @@ -655,13 +725,20 @@ mod babbage_tests { mtx.transaction_body = Decode::decode(&mut Decoder::new(&tx_buf.as_slice()), &mut ()).unwrap(); let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Babbage(mk_mainnet_params_epoch_365()), prot_magic: 764824073, block_slot: 72316896, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => assert!( false, "Validity interval upper bound should not have been surpassed" @@ -694,13 +771,20 @@ mod babbage_tests { let utxos: UTxOs = mk_utxo_for_babbage_tx(&mtx.transaction_body, tx_outs_info); let mut babbage_prot_params: BabbageProtParams = mk_mainnet_params_epoch_365(); babbage_prot_params.minfee_a = 76; // This value was 44 during Babbage on mainnet. + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Babbage(babbage_prot_params), prot_magic: 764824073, block_slot: 72316896, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => assert!(false, "Fee should not be below minimum"), Err(err) => match err { Babbage(BabbageError::FeeBelowMin) => (), @@ -773,13 +857,20 @@ mod babbage_tests { mtx.transaction_body = Decode::decode(&mut Decoder::new(&tx_buf.as_slice()), &mut ()).unwrap(); let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Babbage(mk_mainnet_params_epoch_365()), prot_magic: 764824073, block_slot: 72316896, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => assert!(false, "No collateral inputs"), Err(err) => match err { Babbage(BabbageError::CollateralMissing) => (), @@ -849,13 +940,20 @@ mod babbage_tests { add_collateral_babbage(&mtx.transaction_body, &mut utxos, collateral_info); let mut babbage_prot_params: BabbageProtParams = mk_mainnet_params_epoch_365(); babbage_prot_params.max_collateral_inputs = 0; // This value was 3 during Babbage on mainnet + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Babbage(babbage_prot_params), prot_magic: 764824073, block_slot: 72317003, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => assert!(false, "Number of collateral inputs should be within limits"), Err(err) => match err { Babbage(BabbageError::TooManyCollaterals) => (), @@ -941,13 +1039,20 @@ mod babbage_tests { ))); utxos.insert(multi_era_in, multi_era_out); let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Babbage(mk_mainnet_params_epoch_365()), prot_magic: 764824073, block_slot: 72316896, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => assert!(false, "Collateral inputs should be verification-key locked"), Err(err) => match err { Babbage(BabbageError::CollateralNotVKeyLocked) => (), @@ -1026,13 +1131,20 @@ mod babbage_tests { None, )]; add_collateral_babbage(&mtx.transaction_body, &mut utxos, collateral_info); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Babbage(mk_mainnet_params_epoch_365()), prot_magic: 764824073, block_slot: 72317003, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => assert!(false, "Collateral balance should contained only lovelace"), Err(err) => match err { Babbage(BabbageError::NonLovelaceCollateral) => (), @@ -1102,13 +1214,20 @@ mod babbage_tests { let mut babbage_prot_params: BabbageProtParams = mk_mainnet_params_epoch_365(); babbage_prot_params.collateral_percentage = 728; // This value was 150 during Babbage on // mainnet. + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Babbage(babbage_prot_params), prot_magic: 764824073, block_slot: 72317003, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => assert!( false, "Collateral balance should contained the minimum lovelace" @@ -1184,13 +1303,20 @@ mod babbage_tests { mtx.transaction_body = Decode::decode(&mut Decoder::new(&tx_buf.as_slice()), &mut ()).unwrap(); let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Babbage(mk_mainnet_params_epoch_365()), prot_magic: 764824073, block_slot: 72317003, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => assert!(false, "Collateral annotation"), Err(err) => match err { Babbage(BabbageError::CollateralAnnotation) => (), @@ -1224,13 +1350,20 @@ mod babbage_tests { mtx.transaction_body = Decode::decode(&mut Decoder::new(&tx_buf.as_slice()), &mut ()).unwrap(); let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Babbage(mk_mainnet_params_epoch_365()), prot_magic: 764824073, block_slot: 72316896, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => assert!(false, "Preservation of value does not hold"), Err(err) => match err { Babbage(BabbageError::PreservationOfValue) => (), @@ -1260,13 +1393,20 @@ mod babbage_tests { let utxos: UTxOs = mk_utxo_for_babbage_tx(&mtx.transaction_body, tx_outs_info); let mut babbage_prot_params: BabbageProtParams = mk_mainnet_params_epoch_365(); babbage_prot_params.ada_per_utxo_byte = 10000000; // This was 4310 during Alonzo on mainnet. + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Babbage(babbage_prot_params), prot_magic: 764824073, block_slot: 72316896, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => assert!(false, "Output minimum lovelace is unreached"), Err(err) => match err { Babbage(BabbageError::MinLovelaceUnreached) => (), @@ -1296,13 +1436,20 @@ mod babbage_tests { let utxos: UTxOs = mk_utxo_for_babbage_tx(&mtx.transaction_body, tx_outs_info); let mut babbage_prot_params: BabbageProtParams = mk_mainnet_params_epoch_365(); babbage_prot_params.max_value_size = 0; // This value was 5000 during Babbage on mainnet. + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Babbage(babbage_prot_params), prot_magic: 764824073, block_slot: 72316896, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => assert!(false, "Max value size exceeded"), Err(err) => match err { Babbage(BabbageError::MaxValSizeExceeded) => (), @@ -1364,13 +1511,20 @@ mod babbage_tests { mtx.transaction_body = Decode::decode(&mut Decoder::new(&tx_buf.as_slice()), &mut ()).unwrap(); let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Babbage(mk_mainnet_params_epoch_365()), prot_magic: 764824073, block_slot: 72316896, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => assert!( false, "Output network ID should match environment network ID" @@ -1409,13 +1563,20 @@ mod babbage_tests { mtx.transaction_body = Decode::decode(&mut Decoder::new(&tx_buf.as_slice()), &mut ()).unwrap(); let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Babbage(mk_mainnet_params_epoch_365()), prot_magic: 764824073, block_slot: 72316896, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => assert!( false, "Transaction network ID should match environment network ID" @@ -1489,13 +1650,20 @@ mod babbage_tests { let mut babbage_prot_params: BabbageProtParams = mk_mainnet_params_epoch_365(); babbage_prot_params.max_tx_ex_units.mem = 3678343; // 1 lower than that of the transaction babbage_prot_params.max_tx_ex_units.steps = 1304942838; // 1 lower than that of the transaction + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Babbage(babbage_prot_params), prot_magic: 764824073, block_slot: 72317003, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => assert!(false, "Transaction ex units should be below maximum"), Err(err) => match err { Babbage(BabbageError::TxExUnitsExceeded) => (), @@ -1526,13 +1694,20 @@ mod babbage_tests { let utxos: UTxOs = mk_utxo_for_babbage_tx(&mtx.transaction_body, tx_outs_info); let mut babbage_prot_params: BabbageProtParams = mk_mainnet_params_epoch_365(); babbage_prot_params.max_transaction_size = 154; // 1 less than the size of the transaction. + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Babbage(babbage_prot_params), prot_magic: 764824073, block_slot: 72316896, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => assert!( false, "Transaction size should not exceed the maximum allowed" @@ -1636,13 +1811,20 @@ mod babbage_tests { mtx.transaction_witness_set = Decode::decode(&mut Decoder::new(&tx_buf.as_slice()), &mut ()).unwrap(); let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Babbage(mk_mainnet_params_epoch_365()), prot_magic: 764824073, block_slot: 72316896, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => assert!( false, "Minting policy is not supported by the corresponding native script" @@ -1710,13 +1892,20 @@ mod babbage_tests { None, )]; add_collateral_babbage(&mtx.transaction_body, &mut utxos, collateral_info); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Babbage(mk_mainnet_params_epoch_365()), prot_magic: 764824073, block_slot: 72316896, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => assert!(false, "Transaction auxiliary data removed"), Err(err) => match err { Babbage(BabbageError::MetadataHash) => (), @@ -1790,13 +1979,20 @@ mod babbage_tests { mtx.transaction_witness_set = Decode::decode(&mut Decoder::new(&tx_buf.as_slice()), &mut ()).unwrap(); let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Babbage(mk_mainnet_params_epoch_365()), prot_magic: 764824073, block_slot: 72317003, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => assert!( false, "Script hash in input is not matched to a script in the witness set" @@ -1872,13 +2068,20 @@ mod babbage_tests { mtx.transaction_witness_set = Decode::decode(&mut Decoder::new(&tx_buf.as_slice()), &mut ()).unwrap(); let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Babbage(mk_mainnet_params_epoch_365()), prot_magic: 764824073, block_slot: 72317003, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => assert!( false, "Datum matching the script input datum hash is missing" @@ -1960,13 +2163,20 @@ mod babbage_tests { mtx.transaction_witness_set = Decode::decode(&mut Decoder::new(&tx_buf.as_slice()), &mut ()).unwrap(); let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Babbage(mk_mainnet_params_epoch_365()), prot_magic: 764824073, block_slot: 72317003, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => assert!(false, "Unneeded datum"), Err(err) => match err { Babbage(BabbageError::UnneededDatum) => (), @@ -2046,13 +2256,20 @@ mod babbage_tests { mtx.transaction_witness_set = Decode::decode(&mut Decoder::new(&tx_buf.as_slice()), &mut ()).unwrap(); let metx: MultiEraTx = MultiEraTx::from_babbage(&mtx); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Babbage(mk_mainnet_params_epoch_365()), prot_magic: 764824073, block_slot: 72317003, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => assert!(false, "Unneeded datum"), Err(err) => match err { Babbage(BabbageError::UnneededRedeemer) => (), @@ -2129,13 +2346,20 @@ mod babbage_tests { 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 acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Babbage(mk_mainnet_params_epoch_365()), prot_magic: 764824073, block_slot: 72317003, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => assert!(false, "Wrong script integrity hash"), Err(err) => match err { Babbage(BabbageError::ScriptIntegrityHash) => (), diff --git a/pallas-applying/tests/byron.rs b/pallas-applying/tests/byron.rs index 782170e4..63813b3d 100644 --- a/pallas-applying/tests/byron.rs +++ b/pallas-applying/tests/byron.rs @@ -3,9 +3,10 @@ pub mod common; use common::{cbor_to_bytes, minted_tx_payload_from_cbor, mk_utxo_for_byron_tx}; use pallas_applying::{ utils::{ - ByronError, ByronProtParams, Environment, MultiEraProtocolParameters, ValidationError::*, + ByronError, ByronProtParams, CertState, Environment, MultiEraProtocolParameters, + ValidationError::*, }, - validate, UTxOs, + validate_txs, UTxOs, }; use pallas_codec::{ @@ -59,8 +60,10 @@ mod byron_tests { prot_magic: 764824073, block_slot: 6341, network_id: 1, + acnt: None, }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => (), Err(err) => panic!("Unexpected error ({:?})", err), } @@ -102,8 +105,10 @@ mod byron_tests { prot_magic: 764824073, block_slot: 3241381, network_id: 1, + acnt: None, }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => (), Err(err) => panic!("Unexpected error ({:?})", err), } @@ -153,8 +158,10 @@ mod byron_tests { prot_magic: 764824073, block_slot: 3241381, network_id: 1, + acnt: None, }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("Inputs set should not be empty"), Err(err) => match err { Byron(ByronError::TxInsEmpty) => (), @@ -207,8 +214,10 @@ mod byron_tests { prot_magic: 764824073, block_slot: 3241381, network_id: 1, + acnt: None, }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("Outputs set should not be empty"), Err(err) => match err { Byron(ByronError::TxOutsEmpty) => (), @@ -246,8 +255,10 @@ mod byron_tests { prot_magic: 764824073, block_slot: 3241381, network_id: 1, + acnt: None, }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("All inputs must be within the UTxO set"), Err(err) => match err { Byron(ByronError::InputNotInUTxO) => (), @@ -306,8 +317,10 @@ mod byron_tests { prot_magic: 764824073, block_slot: 3241381, network_id: 1, + acnt: None, }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("All outputs must contain lovelace"), Err(err) => match err { Byron(ByronError::OutputWithoutLovelace) => (), @@ -351,8 +364,10 @@ mod byron_tests { prot_magic: 764824073, block_slot: 3241381, network_id: 1, + acnt: None, }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("Fees should not be below minimum"), Err(err) => match err { Byron(ByronError::FeesBelowMin) => (), @@ -396,8 +411,10 @@ mod byron_tests { prot_magic: 764824073, block_slot: 3241381, network_id: 1, + acnt: None, }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("Transaction size cannot exceed protocol limit"), Err(err) => match err { Byron(ByronError::MaxTxSizeExceeded) => (), @@ -449,8 +466,10 @@ mod byron_tests { prot_magic: 764824073, block_slot: 3241381, network_id: 1, + acnt: None, }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("All inputs must have a witness signature"), Err(err) => match err { Byron(ByronError::MissingWitness) => (), @@ -511,8 +530,10 @@ mod byron_tests { prot_magic: 764824073, block_slot: 3241381, network_id: 1, + acnt: None, }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("Witness signature should verify the transaction"), Err(err) => match err { Byron(ByronError::WrongSignature) => (), diff --git a/pallas-applying/tests/shelley_ma.rs b/pallas-applying/tests/shelley_ma.rs index 08ea3ff1..d0ef0a16 100644 --- a/pallas-applying/tests/shelley_ma.rs +++ b/pallas-applying/tests/shelley_ma.rs @@ -4,10 +4,10 @@ use common::*; use pallas_addresses::{Address, Network, ShelleyAddress}; use pallas_applying::{ utils::{ - Environment, MultiEraProtocolParameters, ShelleyMAError, ShelleyProtParams, + AccountState, Environment, MultiEraProtocolParameters, ShelleyMAError, ShelleyProtParams, ValidationError::*, }, - validate, UTxOs, + validate_txs, CertState, UTxOs, }; use pallas_codec::{ minicbor::{ @@ -16,11 +16,13 @@ use pallas_codec::{ }, utils::{Bytes, Nullable}, }; +use pallas_crypto::hash::Hash; use pallas_primitives::alonzo::{ - MintedTx, MintedWitnessSet, Nonce, NonceVariant, RationalNumber, TransactionBody, - TransactionOutput, VKeyWitness, Value, + MintedTx, MintedWitnessSet, Nonce, NonceVariant, RationalNumber, StakeCredential, + TransactionBody, TransactionOutput, VKeyWitness, Value, }; use pallas_traverse::{Era, MultiEraTx}; +use std::str::FromStr; #[cfg(test)] mod shelley_ma_tests { @@ -41,6 +43,11 @@ mod shelley_ma_tests { None, )], ); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Shelley(ShelleyProtParams { minfee_b: 155381, @@ -82,8 +89,10 @@ mod shelley_ma_tests { prot_magic: 764824073, block_slot: 5281340, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => (), Err(err) => panic!("Unexpected error ({:?})", err), } @@ -104,6 +113,12 @@ mod shelley_ma_tests { None, )], ); + + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Shelley(ShelleyProtParams { minfee_b: 155381, @@ -145,8 +160,10 @@ mod shelley_ma_tests { prot_magic: 764824073, block_slot: 17584925, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => (), Err(err) => panic!("Unexpected error ({:?})", err), } @@ -178,6 +195,11 @@ mod shelley_ma_tests { None, )], ); + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Shelley(ShelleyProtParams { minfee_b: 155381, @@ -219,8 +241,10 @@ mod shelley_ma_tests { prot_magic: 764824073, block_slot: 17584925, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => (), Err(err) => panic!("Unexpected error ({:?})", err), } @@ -241,6 +265,12 @@ mod shelley_ma_tests { None, )], ); + + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Shelley(ShelleyProtParams { minfee_b: 155381, @@ -282,8 +312,10 @@ mod shelley_ma_tests { prot_magic: 764824073, block_slot: 5860488, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => (), Err(err) => panic!("Unexpected error ({:?})", err), } @@ -304,6 +336,12 @@ mod shelley_ma_tests { None, )], ); + + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Shelley(ShelleyProtParams { minfee_b: 155381, @@ -345,8 +383,10 @@ mod shelley_ma_tests { prot_magic: 764824073, block_slot: 24381863, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => (), Err(err) => panic!("Unexpected error ({:?})", err), } @@ -432,6 +472,12 @@ mod shelley_ma_tests { None, )], ); + + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Shelley(ShelleyProtParams { minfee_b: 155381, @@ -470,8 +516,17 @@ mod shelley_ma_tests { prot_magic: 764824073, block_slot: 26342415, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + let hash = + Hash::from_str("FB2B631DB76384F64DD94B47F97FC8C2A206764C17A1DE7DA2F70E83").unwrap(); + cert_state + .dstate + .rewards + .insert(StakeCredential::AddrKeyhash(hash), 0); + + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => (), Err(err) => panic!("Unexpected error ({:?})", err), } @@ -493,6 +548,12 @@ mod shelley_ma_tests { None, )], ); + + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Shelley(ShelleyProtParams { minfee_b: 155381, @@ -531,8 +592,10 @@ mod shelley_ma_tests { prot_magic: 764824073, block_slot: 19282133, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => (), Err(err) => panic!("Unexpected error ({:?})", err), } @@ -562,6 +625,12 @@ mod shelley_ma_tests { mtx.transaction_body = Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap(); let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Shelley); + + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Shelley(ShelleyProtParams { minfee_b: 155381, @@ -603,8 +672,10 @@ mod shelley_ma_tests { prot_magic: 764824073, block_slot: 5281340, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("Inputs set should not be empty"), Err(err) => match err { ShelleyMA(ShelleyMAError::TxInsEmpty) => (), @@ -620,6 +691,12 @@ mod shelley_ma_tests { let mtx: MintedTx = minted_tx_from_cbor(&cbor_bytes); let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Shelley); let utxos: UTxOs = UTxOs::new(); + + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Shelley(ShelleyProtParams { minfee_b: 155381, @@ -661,8 +738,10 @@ mod shelley_ma_tests { prot_magic: 764824073, block_slot: 5281340, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("All inputs must be within the UTxO set"), Err(err) => match err { ShelleyMA(ShelleyMAError::InputNotInUTxO) => (), @@ -694,6 +773,12 @@ mod shelley_ma_tests { mtx.transaction_body = Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap(); let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Shelley); + + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Shelley(ShelleyProtParams { minfee_b: 155381, @@ -735,8 +820,10 @@ mod shelley_ma_tests { prot_magic: 764824073, block_slot: 5281340, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("TTL must always be present in Shelley transactions"), Err(err) => match err { ShelleyMA(ShelleyMAError::AlonzoCompNotShelley) => (), @@ -759,6 +846,12 @@ mod shelley_ma_tests { None, )], ); + + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Shelley(ShelleyProtParams { minfee_b: 155381, @@ -800,8 +893,10 @@ mod shelley_ma_tests { prot_magic: 764824073, block_slot: 9999999, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("TTL cannot be exceeded"), Err(err) => match err { ShelleyMA(ShelleyMAError::TTLExceeded) => (), @@ -824,6 +919,12 @@ mod shelley_ma_tests { None, )], ); + + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Shelley(ShelleyProtParams { minfee_b: 155381, @@ -865,8 +966,10 @@ mod shelley_ma_tests { prot_magic: 764824073, block_slot: 5281340, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("Tx size exceeds max limit"), Err(err) => match err { ShelleyMA(ShelleyMAError::MaxTxSizeExceeded) => (), @@ -890,6 +993,12 @@ mod shelley_ma_tests { None, )], ); + + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Shelley(ShelleyProtParams { minfee_b: 155381, @@ -931,8 +1040,10 @@ mod shelley_ma_tests { prot_magic: 764824073, block_slot: 5281340, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("Output amount must be above min lovelace value"), Err(err) => match err { ShelleyMA(ShelleyMAError::MinLovelaceUnreached) => (), @@ -965,6 +1076,12 @@ mod shelley_ma_tests { None, )], ); + + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Shelley(ShelleyProtParams { minfee_b: 155381, @@ -1006,8 +1123,10 @@ mod shelley_ma_tests { prot_magic: 764824073, block_slot: 5281340, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("Preservation of value property doesn't hold"), Err(err) => match err { ShelleyMA(ShelleyMAError::PreservationOfValue) => (), @@ -1030,6 +1149,12 @@ mod shelley_ma_tests { None, )], ); + + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Shelley(ShelleyProtParams { minfee_b: 155381, @@ -1071,8 +1196,10 @@ mod shelley_ma_tests { prot_magic: 764824073, block_slot: 5281340, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("Fee should not be below minimum"), Err(err) => match err { ShelleyMA(ShelleyMAError::FeesBelowMin) => (), @@ -1118,6 +1245,12 @@ mod shelley_ma_tests { mtx.transaction_body = Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap(); let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Shelley); + + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Shelley(ShelleyProtParams { minfee_b: 155381, @@ -1159,6 +1292,7 @@ mod shelley_ma_tests { prot_magic: 764824073, block_slot: 5281340, network_id: 1, + acnt: Some(acnt), }; let utxos: UTxOs = mk_utxo_for_alonzo_compatible_tx( &mtx.transaction_body, @@ -1168,7 +1302,8 @@ mod shelley_ma_tests { None, )], ); - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("Output with wrong network ID should be rejected"), Err(err) => match err { ShelleyMA(ShelleyMAError::WrongNetworkID) => (), @@ -1194,6 +1329,12 @@ mod shelley_ma_tests { None, )], ); + + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Shelley(ShelleyProtParams { minfee_b: 155381, @@ -1235,8 +1376,10 @@ mod shelley_ma_tests { prot_magic: 764824073, block_slot: 5860488, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("Output with wrong network ID should be rejected"), Err(err) => match err { ShelleyMA(ShelleyMAError::MetadataHash) => (), @@ -1263,6 +1406,12 @@ mod shelley_ma_tests { mtx.transaction_witness_set = Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap(); let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Shelley); + + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Shelley(ShelleyProtParams { minfee_b: 155381, @@ -1304,6 +1453,7 @@ mod shelley_ma_tests { prot_magic: 764824073, block_slot: 5281340, network_id: 1, + acnt: Some(acnt), }; let utxos: UTxOs = mk_utxo_for_alonzo_compatible_tx( &mtx.transaction_body, @@ -1313,7 +1463,8 @@ mod shelley_ma_tests { None, )], ); - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("Missing verification key witness"), Err(err) => match err { ShelleyMA(ShelleyMAError::MissingVKWitness) => (), @@ -1345,6 +1496,12 @@ mod shelley_ma_tests { mtx.transaction_witness_set = Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap(); let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Shelley); + + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Shelley(ShelleyProtParams { minfee_b: 155381, @@ -1386,6 +1543,7 @@ mod shelley_ma_tests { prot_magic: 764824073, block_slot: 5281340, network_id: 1, + acnt: Some(acnt), }; let utxos: UTxOs = mk_utxo_for_alonzo_compatible_tx( &mtx.transaction_body, @@ -1395,7 +1553,8 @@ mod shelley_ma_tests { None, )], ); - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("Missing verification key witness"), Err(err) => match err { ShelleyMA(ShelleyMAError::WrongSignature) => (), @@ -1422,6 +1581,12 @@ mod shelley_ma_tests { mtx.transaction_witness_set = Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap(); let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Shelley); + + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Shelley(ShelleyProtParams { minfee_b: 155381, @@ -1463,6 +1628,7 @@ mod shelley_ma_tests { prot_magic: 764824073, block_slot: 5281340, network_id: 1, + acnt: Some(acnt), }; let utxos: UTxOs = mk_utxo_for_alonzo_compatible_tx( &mtx.transaction_body, @@ -1472,7 +1638,8 @@ mod shelley_ma_tests { None, )], ); - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("Missing native script witness"), Err(err) => match err { ShelleyMA(ShelleyMAError::MissingScriptWitness) => (), @@ -1501,6 +1668,12 @@ mod shelley_ma_tests { mtx.transaction_witness_set = Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap(); let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Shelley); + + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Shelley(ShelleyProtParams { minfee_b: 155381, @@ -1542,6 +1715,7 @@ mod shelley_ma_tests { prot_magic: 764824073, block_slot: 5281340, network_id: 1, + acnt: Some(acnt), }; let utxos: UTxOs = mk_utxo_for_alonzo_compatible_tx( &mtx.transaction_body, @@ -1551,7 +1725,8 @@ mod shelley_ma_tests { None, )], ); - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("The script is not satisfied"), Err(err) => match err { ShelleyMA(ShelleyMAError::ScriptDenial) => (), @@ -1574,6 +1749,12 @@ mod shelley_ma_tests { None, )], ); + + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + let env: Environment = Environment { prot_params: MultiEraProtocolParameters::Shelley(ShelleyProtParams { minfee_b: 155381, @@ -1612,8 +1793,10 @@ mod shelley_ma_tests { prot_magic: 764824073, block_slot: 19483200, network_id: 1, + acnt: Some(acnt), }; - match validate(&metx, &utxos, &env) { + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { Ok(()) => panic!("MIR after the stability window"), Err(err) => match err { ShelleyMA(ShelleyMAError::MIRCertificateTooLateinEpoch) => (), From 78ea02f49585a79ecc7b2f9417b7dc24e3984bbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20S=C3=A1nchez=20Terraf?= Date: Fri, 23 Aug 2024 20:25:36 -0300 Subject: [PATCH 3/8] Fixed total deposit, added test --- pallas-applying/src/shelley_ma.rs | 4 +- pallas-applying/src/utils.rs | 4 - pallas-applying/tests/shelley_ma.rs | 159 ++++++++++++++++------------ 3 files changed, 96 insertions(+), 71 deletions(-) diff --git a/pallas-applying/src/shelley_ma.rs b/pallas-applying/src/shelley_ma.rs index 698f707f..597c540f 100644 --- a/pallas-applying/src/shelley_ma.rs +++ b/pallas-applying/src/shelley_ma.rs @@ -49,7 +49,6 @@ pub fn validate_shelley_ma_tx( let stk_refund_count: &mut u64 = &mut 0; // count of key deregs (for refunds) let pool_count: &mut u64 = &mut 0; // count of pool regs (for deposits) - // FIXME: This section is entirely made up let stab_win = 129600; // FIXME: Found as "1.5 days" in unreliable sources. check_ins_not_empty(tx_body)?; @@ -230,7 +229,8 @@ fn get_produced( // Adding fees res = add_values(&res, &Value::Coin(tx_body.fee), &neg_val_err)?; // Pool reg deposits and staking key registrations - let total_deposits = prot_pps.pool_deposit * (*pool_count + *stk_dep_count); + let total_deposits = prot_pps.pool_deposit * *pool_count + + prot_pps.key_deposit * *stk_dep_count; res = add_values(&res, &Value::Coin(total_deposits), &neg_val_err)?; Ok(res) } diff --git a/pallas-applying/src/utils.rs b/pallas-applying/src/utils.rs index 7334cd99..1520fd02 100644 --- a/pallas-applying/src/utils.rs +++ b/pallas-applying/src/utils.rs @@ -349,10 +349,6 @@ pub fn compute_plutus_v2_script_hash(script: &PlutusV2Script) -> PolicyId { pallas_crypto::hash::Hasher::<224>::hash(&payload) } -// Move to an appropriate place (primitives?) -// pub type RewardAccounts = HashMap; -// pub type Delegations = HashMap; - pub type CertificateIndex = u32; #[derive(PartialEq, Eq, Hash, Clone)] diff --git a/pallas-applying/tests/shelley_ma.rs b/pallas-applying/tests/shelley_ma.rs index d0ef0a16..6f220d91 100644 --- a/pallas-applying/tests/shelley_ma.rs +++ b/pallas-applying/tests/shelley_ma.rs @@ -19,10 +19,11 @@ use pallas_codec::{ use pallas_crypto::hash::Hash; use pallas_primitives::alonzo::{ MintedTx, MintedWitnessSet, Nonce, NonceVariant, RationalNumber, StakeCredential, - TransactionBody, TransactionOutput, VKeyWitness, Value, + TransactionBody, TransactionOutput, VKeyWitness, Value, PoolMetadata, Relay, }; use pallas_traverse::{Era, MultiEraTx}; use std::str::FromStr; +use pallas_applying::utils::PoolParam; #[cfg(test)] mod shelley_ma_tests { @@ -392,70 +393,98 @@ mod shelley_ma_tests { } } - // #[test] - // // Transaction hash: - // // 1dd22b2976374f9b8e6aa045ded141742fa5adc5184a505410fb9f343d14e407 - // fn successful_mainnet_mary_tx_with_stk_reg() { - // let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/mary3.tx")); - // let mtx: MintedTx = minted_tx_from_cbor(&cbor_bytes); - // let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Mary); - // let utxos: UTxOs = mk_utxo_for_alonzo_compatible_tx( - // &mtx.transaction_body, - // &[( - // String::from("018e8f7a7073b8a95a4c1f1cf412b1042fca4945b89eb11754b3481b29fb2b631db76384f64dd94b47f97fc8c2a206764c17a1de7da2f70e83"), - // Value::Coin(1_501_000_000), - // None, - // ), - // ( - // String::from("018e8f7a7073b8a95a4c1f1cf412b1042fca4945b89eb11754b3481b29fb2b631db76384f64dd94b47f97fc8c2a206764c17a1de7da2f70e83"), - // Value::Coin(9_000_000), - // None, - // )], - // ); - // let env: Environment = Environment { - // prot_params: MultiEraProtocolParameters::Shelley(ShelleyProtParams { - // minfee_b: 155381, - // minfee_a: 44, - // max_block_body_size: 65536, - // max_transaction_size: 16384, - // max_block_header_size: 1100, - // key_deposit: 2_000_000, - // pool_deposit: 500_000_000, - // maximum_epoch: 18, - // desired_number_of_stake_pools: 500, - // pool_pledge_influence: RationalNumber { - // numerator: 3, - // denominator: 10, - // }, - // expansion_rate: RationalNumber { - // numerator: 3, - // denominator: 1000, - // }, - // treasury_growth_rate: RationalNumber { - // numerator: 2, - // denominator: 10, - // }, - // decentralization_constant: RationalNumber { - // numerator: 0, - // denominator: 1, - // }, - // extra_entropy: Nonce { - // variant: NonceVariant::NeutralNonce, - // hash: None, - // }, - // protocol_version: (4, 0), - // min_utxo_value: 1_000_000, - // min_pool_cost: 340_000_000, - // }), - // prot_magic: 764824073, - // block_slot: 26342233, - // network_id: 1, - // }; - // match validate(&metx, &utxos, &env) { - // Ok(()) => (), - // Err(err) => panic!("Unexpected error ({:?})", err), - // } - // } + #[test] + // Transaction hash: + // 1dd22b2976374f9b8e6aa045ded141742fa5adc5184a505410fb9f343d14e407 + fn successful_mainnet_mary_tx_with_stk_reg() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/mary3.tx")); + let mtx: MintedTx = minted_tx_from_cbor(&cbor_bytes); + let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Mary); + let utxos: UTxOs = mk_utxo_for_alonzo_compatible_tx( + &mtx.transaction_body, + &[( + String::from("018e8f7a7073b8a95a4c1f1cf412b1042fca4945b89eb11754b3481b29fb2b631db76384f64dd94b47f97fc8c2a206764c17a1de7da2f70e83"), + Value::Coin(1_501_000_000), + None, + ), + ( + String::from("018e8f7a7073b8a95a4c1f1cf412b1042fca4945b89eb11754b3481b29fb2b631db76384f64dd94b47f97fc8c2a206764c17a1de7da2f70e83"), + Value::Coin(9_000_000), + None, + )], + ); + + let acnt = AccountState { + treasury: 374_930_989_230_000, + reserves: 12_618_536_190_580_000, + }; + + let env: Environment = Environment { + prot_params: MultiEraProtocolParameters::Shelley(ShelleyProtParams { + minfee_b: 155381, + minfee_a: 44, + max_block_body_size: 65536, + max_transaction_size: 16384, + max_block_header_size: 1100, + key_deposit: 2_000_000, + pool_deposit: 500_000_000, + maximum_epoch: 18, + desired_number_of_stake_pools: 500, + pool_pledge_influence: RationalNumber { + numerator: 3, + denominator: 10, + }, + expansion_rate: RationalNumber { + numerator: 3, + denominator: 1000, + }, + treasury_growth_rate: RationalNumber { + numerator: 2, + denominator: 10, + }, + decentralization_constant: RationalNumber { + numerator: 0, + denominator: 1, + }, + extra_entropy: Nonce { + variant: NonceVariant::NeutralNonce, + hash: None, + }, + protocol_version: (4, 0), + min_utxo_value: 1_000_000, + min_pool_cost: 340_000_000, + }), + prot_magic: 764824073, + block_slot: 26342233, + network_id: 1, + acnt: Some(acnt), + }; + let mut cert_state: CertState = CertState::default(); + // Params for the pool registered in `successful_mainnet_mary_tx_with_pool_reg` + let pool_param = PoolParam { + vrf_keyhash: Hash::from_str("1EFB798F239B9B02DEB4636A3AB1962AF43512595FCB82276E11971E684E49B7").unwrap(), + pledge: 1000000000, + cost: 340000000, + margin: RationalNumber { numerator: 3, denominator: 100 }, + reward_account: hex::decode("E1FB2B631DB76384F64DD94B47F97FC8C2A206764C17A1DE7DA2F70E83").unwrap().into(), + pool_owners: Vec::from([Hash::from_str("FB2B631DB76384F64DD94B47F97FC8C2A206764C17A1DE7DA2F70E83").unwrap()]), + relays: [ + Relay::SingleHostAddr(Nullable::Some(3001), + Nullable::Some(hex::decode("C22614BB").unwrap().into()), + Nullable::Null,) + ].to_vec(), + pool_metadata: Nullable::Some(PoolMetadata { + url: "https://cardapool.com/a.json".to_string(), + hash: Hash::from_str("01F708549816C9A075FF96E9682C11A5F5C7F4E147862A663BDEECE0716AB76E").unwrap(), + }), + }; + cert_state.pstate.pool_params + .insert(Hash::from_str("59EBE72AE96462018FBE04633100F90B3066688D85F00F3BD254707F").unwrap(), pool_param); + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { + Ok(()) => (), + Err(err) => panic!("Unexpected error ({:?})", err), + } + } #[test] // Transaction hash: From bfab4d4d049a53b068720f1b9eb9e6754308fe93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20S=C3=A1nchez=20Terraf?= Date: Sat, 24 Aug 2024 19:50:34 -0300 Subject: [PATCH 4/8] Testing key delegation --- pallas-applying/src/utils.rs | 2 +- pallas-applying/tests/shelley_ma.rs | 194 ++++++++++++++++++++++------ test_data/mary4.tx | 1 + 3 files changed, 155 insertions(+), 42 deletions(-) create mode 100644 test_data/mary4.tx diff --git a/pallas-applying/src/utils.rs b/pallas-applying/src/utils.rs index 1520fd02..7e868e02 100644 --- a/pallas-applying/src/utils.rs +++ b/pallas-applying/src/utils.rs @@ -376,7 +376,7 @@ pub struct DState { } // Essentially part of the `PoolRegistration` component of `Certificate` at alonzo/src/model.rs -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct PoolParam { pub vrf_keyhash: VrfKeyhash, pub pledge: Coin, diff --git a/pallas-applying/tests/shelley_ma.rs b/pallas-applying/tests/shelley_ma.rs index 6f220d91..c383725a 100644 --- a/pallas-applying/tests/shelley_ma.rs +++ b/pallas-applying/tests/shelley_ma.rs @@ -20,6 +20,7 @@ use pallas_crypto::hash::Hash; use pallas_primitives::alonzo::{ MintedTx, MintedWitnessSet, Nonce, NonceVariant, RationalNumber, StakeCredential, TransactionBody, TransactionOutput, VKeyWitness, Value, PoolMetadata, Relay, + PoolKeyhash, }; use pallas_traverse::{Era, MultiEraTx}; use std::str::FromStr; @@ -28,7 +29,7 @@ use pallas_applying::utils::PoolParam; #[cfg(test)] mod shelley_ma_tests { use super::*; - + #[test] // Transaction hash: // 50eba65e73c8c5f7b09f4ea28cf15dce169f3d1c322ca3deff03725f51518bb2 @@ -393,6 +394,85 @@ mod shelley_ma_tests { } } + #[test] + // Transaction hash: + // ce8ba608357e31695ce7be1a4a9875f43b3fd264f106e455e870714f149af925 + fn successful_mainnet_mary_tx_with_pool_reg() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/mary2.tx")); + let mtx: MintedTx = minted_tx_from_cbor(&cbor_bytes); + let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Mary); + let utxos: UTxOs = mk_utxo_for_alonzo_compatible_tx( + &mtx.transaction_body, + &[( + String::from("018e8f7a7073b8a95a4c1f1cf412b1042fca4945b89eb11754b3481b29fb2b631db76384f64dd94b47f97fc8c2a206764c17a1de7da2f70e83"), + Value::Coin(1_507_817_955), + None, + )], + ); + + let acnt = AccountState { + treasury: 261_254_564_000_000, + reserves: 0, + }; + + let env: Environment = Environment { + prot_params: MultiEraProtocolParameters::Shelley(ShelleyProtParams { + minfee_b: 155381, + minfee_a: 44, + max_block_body_size: 65536, + max_transaction_size: 16384, + max_block_header_size: 1100, + key_deposit: 2_000_000, + pool_deposit: 500_000_000, + maximum_epoch: 18, + desired_number_of_stake_pools: 500, + pool_pledge_influence: RationalNumber { + numerator: 3, + denominator: 10, + }, + expansion_rate: RationalNumber { + numerator: 3, + denominator: 1000, + }, + treasury_growth_rate: RationalNumber { + numerator: 2, + denominator: 10, + }, + decentralization_constant: RationalNumber { + numerator: 0, + denominator: 1, + }, + extra_entropy: Nonce { + variant: NonceVariant::NeutralNonce, + hash: None, + }, + protocol_version: (4, 0), + min_utxo_value: 1_000_000, + min_pool_cost: 340_000_000, + }), + prot_magic: 764824073, + block_slot: 26342415, + network_id: 1, + acnt: Some(acnt), + }; + let mut cert_state: CertState = CertState::default(); + let hash = + Hash::from_str("FB2B631DB76384F64DD94B47F97FC8C2A206764C17A1DE7DA2F70E83").unwrap(); + cert_state + .dstate + .rewards + .insert(StakeCredential::AddrKeyhash(hash), 0); + + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { + Ok(()) => (), + Err(err) => panic!("Unexpected error ({:?})", err), + }; + + if !cert_state.pstate.pool_params.contains_key(&mary2_pool_operator()) { + panic!("Pool not registered or keyhash mismatch"); + } + } + #[test] // Transaction hash: // 1dd22b2976374f9b8e6aa045ded141742fa5adc5184a505410fb9f343d14e407 @@ -460,8 +540,46 @@ mod shelley_ma_tests { acnt: Some(acnt), }; let mut cert_state: CertState = CertState::default(); - // Params for the pool registered in `successful_mainnet_mary_tx_with_pool_reg` - let pool_param = PoolParam { + match validate_txs(&[metx], &env, &utxos, &mut cert_state) { + Ok(()) => (), + Err(err) => panic!("Unexpected error ({:?})", err), + } + } + + const MARY4_UTXO: &str = "014faace6b1de3b825da7c7f4308917822049cdedb5868f7623f892d4e39cf0461807b986a6477205e376dac280d7f150eb497025f67c49757"; + + #[test] + // Transaction hash: + // cc6a92cc0f4ea326439bac6b18bc7b424470c508a99b9aebc8fafc027d906465 + fn successful_mainnet_mary_tx_with_stk_deleg() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/mary4.tx")); + let mtx: MintedTx = minted_tx_from_cbor(&cbor_bytes); + let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Mary); + let utxos: UTxOs = mk_utxo_for_alonzo_compatible_tx( + &mtx.transaction_body, + &[( + String::from(MARY4_UTXO), + Value::Coin(627_760_000), + None, + )], + ); + + let mut cert_state: CertState = CertState::default(); + cert_state.pstate.pool_params + .insert(mary2_pool_operator(), mary2_pool_param()); + match validate_txs(&[metx], &mary4_env(), &utxos, &mut cert_state) { + Ok(()) => (), + Err(err) => panic!("Unexpected error ({:?})", err), + } + } + + fn mary2_pool_operator() -> PoolKeyhash { + Hash::from_str("59EBE72AE96462018FBE04633100F90B3066688D85F00F3BD254707F").unwrap() + } + + // Params for the pool registered in `successful_mainnet_mary_tx_with_pool_reg` + fn mary2_pool_param() -> PoolParam { + PoolParam { vrf_keyhash: Hash::from_str("1EFB798F239B9B02DEB4636A3AB1962AF43512595FCB82276E11971E684E49B7").unwrap(), pledge: 1000000000, cost: 340000000, @@ -477,37 +595,16 @@ mod shelley_ma_tests { url: "https://cardapool.com/a.json".to_string(), hash: Hash::from_str("01F708549816C9A075FF96E9682C11A5F5C7F4E147862A663BDEECE0716AB76E").unwrap(), }), - }; - cert_state.pstate.pool_params - .insert(Hash::from_str("59EBE72AE96462018FBE04633100F90B3066688D85F00F3BD254707F").unwrap(), pool_param); - match validate_txs(&[metx], &env, &utxos, &mut cert_state) { - Ok(()) => (), - Err(err) => panic!("Unexpected error ({:?})", err), } } - #[test] - // Transaction hash: - // ce8ba608357e31695ce7be1a4a9875f43b3fd264f106e455e870714f149af925 - fn successful_mainnet_mary_tx_with_pool_reg() { - let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/mary2.tx")); - let mtx: MintedTx = minted_tx_from_cbor(&cbor_bytes); - let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Mary); - let utxos: UTxOs = mk_utxo_for_alonzo_compatible_tx( - &mtx.transaction_body, - &[( - String::from("018e8f7a7073b8a95a4c1f1cf412b1042fca4945b89eb11754b3481b29fb2b631db76384f64dd94b47f97fc8c2a206764c17a1de7da2f70e83"), - Value::Coin(1_507_817_955), - None, - )], - ); - + fn mary4_env() -> Environment { let acnt = AccountState { - treasury: 261_254_564_000_000, - reserves: 0, + treasury: 374_930_989_230_000, + reserves: 12_618_536_190_580_000, }; - let env: Environment = Environment { + Environment { prot_params: MultiEraProtocolParameters::Shelley(ShelleyProtParams { minfee_b: 155381, minfee_a: 44, @@ -543,21 +640,9 @@ mod shelley_ma_tests { min_pool_cost: 340_000_000, }), prot_magic: 764824073, - block_slot: 26342415, + block_slot: 26342233, network_id: 1, acnt: Some(acnt), - }; - let mut cert_state: CertState = CertState::default(); - let hash = - Hash::from_str("FB2B631DB76384F64DD94B47F97FC8C2A206764C17A1DE7DA2F70E83").unwrap(); - cert_state - .dstate - .rewards - .insert(StakeCredential::AddrKeyhash(hash), 0); - - match validate_txs(&[metx], &env, &utxos, &mut cert_state) { - Ok(()) => (), - Err(err) => panic!("Unexpected error ({:?})", err), } } @@ -1763,6 +1848,33 @@ mod shelley_ma_tests { }, } } + + #[test] + // Like `successful_mainnet_mary_tx_with_stk_deleg`, + // but the pool to which the delagation occurs is not registered. + fn unregistered_pool() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/mary4.tx")); + let mtx: MintedTx = minted_tx_from_cbor(&cbor_bytes); + let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Mary); + let utxos: UTxOs = mk_utxo_for_alonzo_compatible_tx( + &mtx.transaction_body, + &[( + String::from(MARY4_UTXO), + Value::Coin(627_760_000), + None, + )], + ); + + let mut cert_state: CertState = CertState::default(); + match validate_txs(&[metx], &mary4_env(), &utxos, &mut cert_state) { + Ok(()) => panic!("Pool is not registered"), + Err(err) => match err { + ShelleyMA(ShelleyMAError::PoolNotRegistered) => (), + _ => panic!("Unexpected error ({:?})", err), + }, + } + } + #[test] // Same as successful_mainnet_allegra_tx_with_mir(), // but the the slot is advanced to a later moment. diff --git a/test_data/mary4.tx b/test_data/mary4.tx new file mode 100644 index 00000000..cbc10a7f --- /dev/null +++ b/test_data/mary4.tx @@ -0,0 +1 @@ +84a50081825820f8cad2b3e5a3744e096cbb3040ff6057560d3ec8d5444fda1efc669f9c2c7d30060181825839015d74deb638695cc12d11b7dfa50b2023b626287371674fecaa569b8039cf0461807b986a6477205e376dac280d7f150eb497025f67c497571a2549ae4f021a0002a8b1031a01bb2735048282008200581c39cf0461807b986a6477205e376dac280d7f150eb497025f67c4975783028200581c39cf0461807b986a6477205e376dac280d7f150eb497025f67c49757581c59ebe72ae96462018fbe04633100f90b3066688d85f00f3bd254707fa100828258208f9010e31a25735302c168f71416bae8d5cc8333067e06cc5d3c3e0b9d1d262058403719305623d25de0648e17cf041a30f28483d4e0354cdd1d12a3e9c13210327c736754049686e6f7315bb9c4b5bcc50cf140290452362e8a2beb66efcd1b2a02825820e7101958033ebca48dc8d71dee85d1a10c1e94460ec43275b1e82e2638a096b05840b6c16dadb85ae630aa0376b6fd26d103b33085a8b12011e515fa265d548431e65a8eb2c8091ed0bc3b364551eaba47ced495bdce4de758509539481ec123d70af5f6 \ No newline at end of file From 200f29fb2b73bd7e0081e6c8857fed7155cd5f08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20S=C3=A1nchez=20Terraf?= Date: Sun, 25 Aug 2024 10:11:32 -0300 Subject: [PATCH 5/8] Corrected block slot --- pallas-applying/tests/shelley_ma.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallas-applying/tests/shelley_ma.rs b/pallas-applying/tests/shelley_ma.rs index c383725a..b1cb0fe9 100644 --- a/pallas-applying/tests/shelley_ma.rs +++ b/pallas-applying/tests/shelley_ma.rs @@ -640,7 +640,7 @@ mod shelley_ma_tests { min_pool_cost: 340_000_000, }), prot_magic: 764824073, - block_slot: 26342233, + block_slot: 29_035_358, network_id: 1, acnt: Some(acnt), } From c97ff87215ea52295ced6e7c6bc5f6a649c85af0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20S=C3=A1nchez=20Terraf?= Date: Sun, 25 Aug 2024 10:36:55 -0300 Subject: [PATCH 6/8] Redundant test, documenting --- pallas-applying/tests/README.md | 6 ++ pallas-applying/tests/shelley_ma.rs | 89 +++-------------------------- test_data/mary3.tx | 2 +- test_data/mary4.tx | 1 - 4 files changed, 15 insertions(+), 83 deletions(-) delete mode 100644 test_data/mary4.tx diff --git a/pallas-applying/tests/README.md b/pallas-applying/tests/README.md index c5ba3596..de84bbf8 100644 --- a/pallas-applying/tests/README.md +++ b/pallas-applying/tests/README.md @@ -36,6 +36,10 @@ List of positive unit tests: - **successful_mainnet_mary_tx_with_pool_reg** ([here](https://cexplorer.io/tx/ce8ba608357e31695ce7be1a4a9875f43b3fd264f106e455e870714f149af925) to see on Cardano explorer) is a Mary transaction with a pool registration. +- **successful_mainnet_mary_tx_with_stk_deleg** + ([here](https://cexplorer.io/tx/cc6a92cc0f4ea326439bac6b18bc7b424470c508a99b9aebc8fafc027d906465) + to see on Cardano explorer) is a Mary transaction with a staking key + registration and delegation to the pool above. - **successful_mainnet_allegra_tx_with_mir** ([here](https://cexplorer.io/tx/99f621beaacefc14ad8912b777422600e707f75bf619b2af20e918b0fe53f882) to see on Cardano explorer) is a Mary transaction moving instantaneous @@ -58,6 +62,8 @@ List of negative unit tests: - **missing_signature_native_script** takes successful_mainnet_shelley_tx but one verification-key witness is removed (the same one of successful_mainnet_shelley_tx_with_changed_script). +- **unregistered_pool** takes successful_mainnet_mary_tx_with_stk_deleg, + but the pool to which the delagation occurs is not registered. - **too_late_for_mir** takes successful_mainnet_allegra_tx_with_mir but the slot is advanced to a later moment. diff --git a/pallas-applying/tests/shelley_ma.rs b/pallas-applying/tests/shelley_ma.rs index b1cb0fe9..cfc1c38e 100644 --- a/pallas-applying/tests/shelley_ma.rs +++ b/pallas-applying/tests/shelley_ma.rs @@ -473,92 +473,19 @@ mod shelley_ma_tests { } } - #[test] - // Transaction hash: - // 1dd22b2976374f9b8e6aa045ded141742fa5adc5184a505410fb9f343d14e407 - fn successful_mainnet_mary_tx_with_stk_reg() { - let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/mary3.tx")); - let mtx: MintedTx = minted_tx_from_cbor(&cbor_bytes); - let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Mary); - let utxos: UTxOs = mk_utxo_for_alonzo_compatible_tx( - &mtx.transaction_body, - &[( - String::from("018e8f7a7073b8a95a4c1f1cf412b1042fca4945b89eb11754b3481b29fb2b631db76384f64dd94b47f97fc8c2a206764c17a1de7da2f70e83"), - Value::Coin(1_501_000_000), - None, - ), - ( - String::from("018e8f7a7073b8a95a4c1f1cf412b1042fca4945b89eb11754b3481b29fb2b631db76384f64dd94b47f97fc8c2a206764c17a1de7da2f70e83"), - Value::Coin(9_000_000), - None, - )], - ); - - let acnt = AccountState { - treasury: 374_930_989_230_000, - reserves: 12_618_536_190_580_000, - }; - - let env: Environment = Environment { - prot_params: MultiEraProtocolParameters::Shelley(ShelleyProtParams { - minfee_b: 155381, - minfee_a: 44, - max_block_body_size: 65536, - max_transaction_size: 16384, - max_block_header_size: 1100, - key_deposit: 2_000_000, - pool_deposit: 500_000_000, - maximum_epoch: 18, - desired_number_of_stake_pools: 500, - pool_pledge_influence: RationalNumber { - numerator: 3, - denominator: 10, - }, - expansion_rate: RationalNumber { - numerator: 3, - denominator: 1000, - }, - treasury_growth_rate: RationalNumber { - numerator: 2, - denominator: 10, - }, - decentralization_constant: RationalNumber { - numerator: 0, - denominator: 1, - }, - extra_entropy: Nonce { - variant: NonceVariant::NeutralNonce, - hash: None, - }, - protocol_version: (4, 0), - min_utxo_value: 1_000_000, - min_pool_cost: 340_000_000, - }), - prot_magic: 764824073, - block_slot: 26342233, - network_id: 1, - acnt: Some(acnt), - }; - let mut cert_state: CertState = CertState::default(); - match validate_txs(&[metx], &env, &utxos, &mut cert_state) { - Ok(()) => (), - Err(err) => panic!("Unexpected error ({:?})", err), - } - } - - const MARY4_UTXO: &str = "014faace6b1de3b825da7c7f4308917822049cdedb5868f7623f892d4e39cf0461807b986a6477205e376dac280d7f150eb497025f67c49757"; + const MARY3_UTXO: &str = "014faace6b1de3b825da7c7f4308917822049cdedb5868f7623f892d4e39cf0461807b986a6477205e376dac280d7f150eb497025f67c49757"; #[test] // Transaction hash: // cc6a92cc0f4ea326439bac6b18bc7b424470c508a99b9aebc8fafc027d906465 fn successful_mainnet_mary_tx_with_stk_deleg() { - let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/mary4.tx")); + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/mary3.tx")); let mtx: MintedTx = minted_tx_from_cbor(&cbor_bytes); let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Mary); let utxos: UTxOs = mk_utxo_for_alonzo_compatible_tx( &mtx.transaction_body, &[( - String::from(MARY4_UTXO), + String::from(MARY3_UTXO), Value::Coin(627_760_000), None, )], @@ -567,7 +494,7 @@ mod shelley_ma_tests { let mut cert_state: CertState = CertState::default(); cert_state.pstate.pool_params .insert(mary2_pool_operator(), mary2_pool_param()); - match validate_txs(&[metx], &mary4_env(), &utxos, &mut cert_state) { + match validate_txs(&[metx], &mary3_env(), &utxos, &mut cert_state) { Ok(()) => (), Err(err) => panic!("Unexpected error ({:?})", err), } @@ -598,7 +525,7 @@ mod shelley_ma_tests { } } - fn mary4_env() -> Environment { + fn mary3_env() -> Environment { let acnt = AccountState { treasury: 374_930_989_230_000, reserves: 12_618_536_190_580_000, @@ -1853,20 +1780,20 @@ mod shelley_ma_tests { // Like `successful_mainnet_mary_tx_with_stk_deleg`, // but the pool to which the delagation occurs is not registered. fn unregistered_pool() { - let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/mary4.tx")); + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/mary3.tx")); let mtx: MintedTx = minted_tx_from_cbor(&cbor_bytes); let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Mary); let utxos: UTxOs = mk_utxo_for_alonzo_compatible_tx( &mtx.transaction_body, &[( - String::from(MARY4_UTXO), + String::from(MARY3_UTXO), Value::Coin(627_760_000), None, )], ); let mut cert_state: CertState = CertState::default(); - match validate_txs(&[metx], &mary4_env(), &utxos, &mut cert_state) { + match validate_txs(&[metx], &mary3_env(), &utxos, &mut cert_state) { Ok(()) => panic!("Pool is not registered"), Err(err) => match err { ShelleyMA(ShelleyMAError::PoolNotRegistered) => (), diff --git a/test_data/mary3.tx b/test_data/mary3.tx index e790f2f1..cbc10a7f 100644 --- a/test_data/mary3.tx +++ b/test_data/mary3.tx @@ -1 +1 @@ -84a500828258204c89f16dd1e38ce56ab8292ff9e840bda3f66ac14eec444aa7d111ad14d6c02a0a8258208f30562f0e45329904dcb1b8d5b24c4e0bd9f9bb11e4c42540330269596cbb30000181825839018e8f7a7073b8a95a4c1f1cf412b1042fca4945b89eb11754b3481b29fb2b631db76384f64dd94b47f97fc8c2a206764c17a1de7da2f70e831a59df79e3021a0002c71d031a0191f5ab048182008200581cfb2b631db76384f64dd94b47f97fc8c2a206764c17a1de7da2f70e83a10082825820af5d32c73a976fcd7abed9fa9c85e128862bd870c8ebc51f602a053343fab8dc5840e5f4046fd411e43bc027519572441b73b5c96b253da8e7358665efed189419d381cf67edbe29900b79f6c3e4eb62c7855bd5bb07504661cc59650c4e52f90504825820bc08576470a74179757177311dd8bdae9e56a637d58ed5d50a737d2b03dd9e68584034bd361d7b656b3b96357cc4f66d25756740f20ca2dbcf515c29f42d05733d004887a9312b52a73b60a1203f65db25b44dd1a8a89913f5d06b09eb0bd6570d04f5f6 \ No newline at end of file +84a50081825820f8cad2b3e5a3744e096cbb3040ff6057560d3ec8d5444fda1efc669f9c2c7d30060181825839015d74deb638695cc12d11b7dfa50b2023b626287371674fecaa569b8039cf0461807b986a6477205e376dac280d7f150eb497025f67c497571a2549ae4f021a0002a8b1031a01bb2735048282008200581c39cf0461807b986a6477205e376dac280d7f150eb497025f67c4975783028200581c39cf0461807b986a6477205e376dac280d7f150eb497025f67c49757581c59ebe72ae96462018fbe04633100f90b3066688d85f00f3bd254707fa100828258208f9010e31a25735302c168f71416bae8d5cc8333067e06cc5d3c3e0b9d1d262058403719305623d25de0648e17cf041a30f28483d4e0354cdd1d12a3e9c13210327c736754049686e6f7315bb9c4b5bcc50cf140290452362e8a2beb66efcd1b2a02825820e7101958033ebca48dc8d71dee85d1a10c1e94460ec43275b1e82e2638a096b05840b6c16dadb85ae630aa0376b6fd26d103b33085a8b12011e515fa265d548431e65a8eb2c8091ed0bc3b364551eaba47ced495bdce4de758509539481ec123d70af5f6 \ No newline at end of file diff --git a/test_data/mary4.tx b/test_data/mary4.tx deleted file mode 100644 index cbc10a7f..00000000 --- a/test_data/mary4.tx +++ /dev/null @@ -1 +0,0 @@ -84a50081825820f8cad2b3e5a3744e096cbb3040ff6057560d3ec8d5444fda1efc669f9c2c7d30060181825839015d74deb638695cc12d11b7dfa50b2023b626287371674fecaa569b8039cf0461807b986a6477205e376dac280d7f150eb497025f67c497571a2549ae4f021a0002a8b1031a01bb2735048282008200581c39cf0461807b986a6477205e376dac280d7f150eb497025f67c4975783028200581c39cf0461807b986a6477205e376dac280d7f150eb497025f67c49757581c59ebe72ae96462018fbe04633100f90b3066688d85f00f3bd254707fa100828258208f9010e31a25735302c168f71416bae8d5cc8333067e06cc5d3c3e0b9d1d262058403719305623d25de0648e17cf041a30f28483d4e0354cdd1d12a3e9c13210327c736754049686e6f7315bb9c4b5bcc50cf140290452362e8a2beb66efcd1b2a02825820e7101958033ebca48dc8d71dee85d1a10c1e94460ec43275b1e82e2638a096b05840b6c16dadb85ae630aa0376b6fd26d103b33085a8b12011e515fa265d548431e65a8eb2c8091ed0bc3b364551eaba47ced495bdce4de758509539481ec123d70af5f6 \ No newline at end of file From 317511f99b50d543961cc1a49efd8aa358f9c009 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20S=C3=A1nchez=20Terraf?= Date: Sun, 25 Aug 2024 21:33:54 -0300 Subject: [PATCH 7/8] Test: delegation before key registration --- pallas-applying/tests/README.md | 5 ++- pallas-applying/tests/shelley_ma.rs | 48 +++++++++++++++++++++++++++-- 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/pallas-applying/tests/README.md b/pallas-applying/tests/README.md index de84bbf8..24c453d8 100644 --- a/pallas-applying/tests/README.md +++ b/pallas-applying/tests/README.md @@ -63,7 +63,10 @@ List of negative unit tests: one verification-key witness is removed (the same one of successful_mainnet_shelley_tx_with_changed_script). - **unregistered_pool** takes successful_mainnet_mary_tx_with_stk_deleg, - but the pool to which the delagation occurs is not registered. + but the pool to which the delegation occurs is not registered. +- **delegation_before_registration** takes + successful_mainnet_mary_tx_with_stk_deleg and flips the order of the + certificates (stake registration and delegation). - **too_late_for_mir** takes successful_mainnet_allegra_tx_with_mir but the slot is advanced to a later moment. diff --git a/pallas-applying/tests/shelley_ma.rs b/pallas-applying/tests/shelley_ma.rs index cfc1c38e..75ddcb88 100644 --- a/pallas-applying/tests/shelley_ma.rs +++ b/pallas-applying/tests/shelley_ma.rs @@ -20,7 +20,7 @@ use pallas_crypto::hash::Hash; use pallas_primitives::alonzo::{ MintedTx, MintedWitnessSet, Nonce, NonceVariant, RationalNumber, StakeCredential, TransactionBody, TransactionOutput, VKeyWitness, Value, PoolMetadata, Relay, - PoolKeyhash, + PoolKeyhash, Certificate, }; use pallas_traverse::{Era, MultiEraTx}; use std::str::FromStr; @@ -1778,7 +1778,7 @@ mod shelley_ma_tests { #[test] // Like `successful_mainnet_mary_tx_with_stk_deleg`, - // but the pool to which the delagation occurs is not registered. + // but the pool to which the delegation occurs is not registered. fn unregistered_pool() { let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/mary3.tx")); let mtx: MintedTx = minted_tx_from_cbor(&cbor_bytes); @@ -1802,6 +1802,50 @@ mod shelley_ma_tests { } } + #[test] + // Like `successful_mainnet_mary_tx_with_stk_deleg`, + // but the order of the certificates (stake registration and delegation) + // is flipped. + fn delegation_before_registration() { + let cbor_bytes: Vec = cbor_to_bytes(include_str!("../../test_data/mary3.tx")); + let mut mtx: MintedTx = minted_tx_from_cbor(&cbor_bytes); + // Permute certificates + let old_certs: Vec = + mtx.transaction_body.certificates.as_ref().unwrap().clone(); + let new_certs: Option> = + Some(Vec::from([old_certs[1].clone(), old_certs[0].clone()])); + let mut tx_body: TransactionBody = mtx.transaction_body.unwrap().clone(); + tx_body.certificates = new_certs; + let mut tx_buf: Vec = Vec::new(); + match encode(tx_body, &mut tx_buf) { + Ok(_) => (), + Err(err) => panic!("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::Mary); + + let utxos: UTxOs = mk_utxo_for_alonzo_compatible_tx( + &mtx.transaction_body, + &[( + String::from(MARY3_UTXO), + Value::Coin(627_760_000), + None, + )], + ); + + let mut cert_state: CertState = CertState::default(); + cert_state.pstate.pool_params + .insert(mary2_pool_operator(), mary2_pool_param()); + match validate_txs(&[metx], &mary3_env(), &utxos, &mut cert_state) { + Ok(()) => panic!("Staking key is not registered"), + Err(err) => match err { + ShelleyMA(ShelleyMAError::KeyNotRegistered) => (), + _ => panic!("Unexpected error ({:?})", err), + }, + } + } + #[test] // Same as successful_mainnet_allegra_tx_with_mir(), // but the the slot is advanced to a later moment. From afe8b0305a01225b747106b2d8083020cfb2ab04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20S=C3=A1nchez=20Terraf?= Date: Mon, 26 Aug 2024 08:54:52 -0300 Subject: [PATCH 8/8] Some debris from rebase --- pallas-applying/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pallas-applying/src/lib.rs b/pallas-applying/src/lib.rs index ac740cb4..9d12f9d5 100644 --- a/pallas-applying/src/lib.rs +++ b/pallas-applying/src/lib.rs @@ -57,6 +57,7 @@ pub fn validate_tx( MultiEraTx::Byron(mtxp) => validate_byron_tx(mtxp, utxos, bpp, env.prot_magic()), _ => Err(TxAndProtParamsDiffer), }, + (MultiEraProtocolParameters::Byron(_), Some(_)) => Err(PParamsByronDoesntNeedAccountState), (MultiEraProtocolParameters::Shelley(spp), Some(acnt)) => match metx { MultiEraTx::AlonzoCompatible(mtx, Era::Shelley) | MultiEraTx::AlonzoCompatible(mtx, Era::Allegra) @@ -90,8 +91,9 @@ pub fn validate_tx( ), _ => Err(TxAndProtParamsDiffer), }, - MultiEraProtocolParameters::Conway(_) => { + (MultiEraProtocolParameters::Conway(_), _) => { todo!("conway phase-1 validation not yet implemented"); } + (_, None) => Err(EnvMissingAccountState), } }