feat(applying): add cert and native script validation for ShelleyMA (#510)

BREAKING CHANGE: the `validate` fn signature has changed to support these changes

---------

Co-authored-by: Ale Gadea <ale.gadea@txpipe.io>
This commit is contained in:
Pedro Sánchez Terraf 2024-09-23 13:24:32 -03:00 committed by GitHub
parent 87e47de094
commit 7c7a3c25ab
15 changed files with 1881 additions and 134 deletions

View file

@ -9,41 +9,78 @@ pub mod utils;
use alonzo::validate_alonzo_tx; use alonzo::validate_alonzo_tx;
use babbage::validate_babbage_tx; use babbage::validate_babbage_tx;
use byron::validate_byron_tx; use byron::validate_byron_tx;
use pallas_primitives::alonzo::TransactionIndex;
use pallas_traverse::{Era, MultiEraTx}; use pallas_traverse::{Era, MultiEraTx};
use shelley_ma::validate_shelley_ma_tx; use shelley_ma::validate_shelley_ma_tx;
pub use utils::{ pub use utils::{
Environment, MultiEraProtocolParameters, UTxOs, CertState, Environment, MultiEraProtocolParameters, UTxOs,
ValidationError::{TxAndProtParamsDiffer, UnknownProtParams}, ValidationError::{
EnvMissingAccountState, PParamsByronDoesntNeedAccountState, TxAndProtParamsDiffer,
UnknownProtParams,
},
ValidationResult, ValidationResult,
}; };
pub fn validate(metx: &MultiEraTx, utxos: &UTxOs, env: &Environment) -> ValidationResult { /// Ledger sequence rule: LEDGERS
match env.prot_params() { pub fn validate_txs(
MultiEraProtocolParameters::Byron(bpp) => match metx { 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()), MultiEraTx::Byron(mtxp) => validate_byron_tx(mtxp, utxos, bpp, env.prot_magic()),
_ => Err(TxAndProtParamsDiffer), _ => Err(TxAndProtParamsDiffer),
}, },
MultiEraProtocolParameters::Shelley(spp) => match metx { (MultiEraProtocolParameters::Byron(_), Some(_)) => Err(PParamsByronDoesntNeedAccountState),
(MultiEraProtocolParameters::Shelley(spp), Some(acnt)) => match metx {
MultiEraTx::AlonzoCompatible(mtx, Era::Shelley) MultiEraTx::AlonzoCompatible(mtx, Era::Shelley)
| MultiEraTx::AlonzoCompatible(mtx, Era::Allegra) | MultiEraTx::AlonzoCompatible(mtx, Era::Allegra)
| MultiEraTx::AlonzoCompatible(mtx, Era::Mary) => validate_shelley_ma_tx( | MultiEraTx::AlonzoCompatible(mtx, Era::Mary) => validate_shelley_ma_tx(
mtx, mtx,
txix,
utxos, utxos,
cert_state,
spp, spp,
&acnt,
env.block_slot(), env.block_slot(),
env.network_id(), env.network_id(),
&metx.era(), &metx.era(),
), ),
_ => Err(TxAndProtParamsDiffer), _ => Err(TxAndProtParamsDiffer),
}, },
MultiEraProtocolParameters::Alonzo(app) => match metx { (MultiEraProtocolParameters::Alonzo(app), _) => match metx {
MultiEraTx::AlonzoCompatible(mtx, Era::Alonzo) => { MultiEraTx::AlonzoCompatible(mtx, Era::Alonzo) => {
validate_alonzo_tx(mtx, utxos, app, env.block_slot(), env.network_id()) validate_alonzo_tx(mtx, utxos, app, env.block_slot(), env.network_id())
} }
_ => Err(TxAndProtParamsDiffer), _ => Err(TxAndProtParamsDiffer),
}, },
MultiEraProtocolParameters::Babbage(bpp) => match metx { (MultiEraProtocolParameters::Babbage(bpp), _) => match metx {
MultiEraTx::Babbage(mtx) => validate_babbage_tx( MultiEraTx::Babbage(mtx) => validate_babbage_tx(
mtx, mtx,
utxos, utxos,
@ -54,8 +91,9 @@ pub fn validate(metx: &MultiEraTx, utxos: &UTxOs, env: &Environment) -> Validati
), ),
_ => Err(TxAndProtParamsDiffer), _ => Err(TxAndProtParamsDiffer),
}, },
MultiEraProtocolParameters::Conway(_) => { (MultiEraProtocolParameters::Conway(_), _) => {
todo!("conway phase-1 validation not yet implemented"); todo!("conway phase-1 validation not yet implemented");
} }
(_, None) => Err(EnvMissingAccountState),
} }
} }

View file

@ -4,6 +4,7 @@ use crate::utils::{
add_minted_value, add_values, aux_data_from_alonzo_minted_tx, empty_value, 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_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, get_val_size_in_words, mk_alonzo_vk_wits_check_list, values_are_equal, verify_signature,
AccountState, CertPointer, CertState, DState, PState, PoolParam,
ShelleyMAError::*, ShelleyMAError::*,
ShelleyProtParams, UTxOs, ShelleyProtParams, UTxOs,
ValidationError::{self, *}, ValidationError::{self, *},
@ -11,20 +12,32 @@ use crate::utils::{
}; };
use pallas_addresses::{PaymentKeyHash, ScriptHash, ShelleyAddress, ShelleyPaymentPart}; use pallas_addresses::{PaymentKeyHash, ScriptHash, ShelleyAddress, ShelleyPaymentPart};
use pallas_codec::minicbor::encode; use pallas_codec::minicbor::encode;
use pallas_crypto::hash::Hasher as PallasHasher;
use pallas_primitives::{ use pallas_primitives::{
alonzo::{ alonzo::{
MintedTx, MintedWitnessSet, NativeScript, PolicyId, TransactionBody, TransactionOutput, Certificate::{self, *},
VKeyWitness, Value, Coin, Epoch, GenesisDelegateHash, Genesishash,
InstantaneousRewardSource::*,
InstantaneousRewardTarget::*,
MintedTx, MintedWitnessSet, MoveInstantaneousReward, NativeScript, PolicyId, PoolKeyhash,
StakeCredential::{self},
TransactionBody, TransactionIndex, TransactionOutput, VKeyWitness, Value, VrfKeyhash,
}, },
byron::TxOut, byron::TxOut,
}; };
use pallas_traverse::{ComputeHash, Era, MultiEraInput, MultiEraOutput}; use pallas_traverse::{
use std::{cmp::max, ops::Deref}; time::Slot, wellknown::GenesisValues, ComputeHash, Era, MultiEraInput, MultiEraOutput,
};
use std::{cmp::max, collections::HashMap, ops::Deref}; // TODO: remove when fixed missing args
pub fn validate_shelley_ma_tx( pub fn validate_shelley_ma_tx(
mtx: &MintedTx, mtx: &MintedTx,
txix: TransactionIndex,
utxos: &UTxOs, utxos: &UTxOs,
cert_state: &mut CertState,
prot_pps: &ShelleyProtParams, prot_pps: &ShelleyProtParams,
acnt: &AccountState,
block_slot: &u64, block_slot: &u64,
network_id: &u8, network_id: &u8,
era: &Era, era: &Era,
@ -32,12 +45,38 @@ pub fn validate_shelley_ma_tx(
let tx_body: &TransactionBody = &mtx.transaction_body; let tx_body: &TransactionBody = &mtx.transaction_body;
let tx_wits: &MintedWitnessSet = &mtx.transaction_witness_set; let tx_wits: &MintedWitnessSet = &mtx.transaction_witness_set;
let size: u32 = get_alonzo_comp_tx_size(mtx); 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)
let stab_win = 129600; // FIXME: Found as "1.5 days" in unreliable sources.
check_ins_not_empty(tx_body)?; check_ins_not_empty(tx_body)?;
check_ins_in_utxos(tx_body, utxos)?; check_ins_in_utxos(tx_body, utxos)?;
check_ttl(tx_body, block_slot)?; check_ttl(tx_body, block_slot)?;
check_tx_size(&size, prot_pps)?; check_tx_size(&size, prot_pps)?;
check_min_lovelace(tx_body, prot_pps, era)?; check_min_lovelace(tx_body, prot_pps, era)?;
check_preservation_of_value(tx_body, utxos, era)?; 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_fees(tx_body, &size, prot_pps)?;
check_network_id(tx_body, network_id)?; check_network_id(tx_body, network_id)?;
check_metadata(tx_body, mtx)?; check_metadata(tx_body, mtx)?;
@ -115,25 +154,27 @@ fn compute_min_lovelace(output: &TransactionOutput, prot_pps: &ShelleyProtParams
fn check_preservation_of_value( fn check_preservation_of_value(
tx_body: &TransactionBody, tx_body: &TransactionBody,
utxos: &UTxOs, utxos: &UTxOs,
stk_dep_count: &u64,
stk_refund_count: &u64,
pool_count: &u64,
era: &Era, era: &Era,
prot_pps: &ShelleyProtParams,
) -> ValidationResult { ) -> ValidationResult {
let neg_val_err: ValidationError = ShelleyMA(NegativeValue); let consumed: Value = get_consumed(tx_body, utxos, stk_refund_count, era, prot_pps)?;
let input: Value = get_consumed(tx_body, utxos, era)?; let produced: Value = get_produced(tx_body, stk_dep_count, pool_count, era, prot_pps)?;
let produced: Value = get_produced(tx_body, era)?; if !values_are_equal(&consumed, &produced) {
let output: Value = add_values(&produced, &Value::Coin(tx_body.fee), &neg_val_err)?; Err(ShelleyMA(PreservationOfValue))
if let Some(m) = &tx_body.mint { } else {
add_minted_value(&output, m, &neg_val_err)?; Ok(())
} }
if !values_are_equal(&input, &output) {
return Err(ShelleyMA(PreservationOfValue));
}
Ok(())
} }
fn get_consumed( fn get_consumed(
tx_body: &TransactionBody, tx_body: &TransactionBody,
utxos: &UTxOs, utxos: &UTxOs,
stk_refund_count: &u64,
era: &Era, era: &Era,
prot_pps: &ShelleyProtParams,
) -> Result<Value, ValidationError> { ) -> Result<Value, ValidationError> {
let neg_val_err: ValidationError = ShelleyMA(NegativeValue); let neg_val_err: ValidationError = ShelleyMA(NegativeValue);
let mut res: Value = empty_value(); let mut res: Value = empty_value();
@ -155,10 +196,26 @@ 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) Ok(res)
} }
fn get_produced(tx_body: &TransactionBody, era: &Era) -> Result<Value, ValidationError> { fn get_produced(
tx_body: &TransactionBody,
stk_dep_count: &u64,
pool_count: &u64,
era: &Era,
prot_pps: &ShelleyProtParams,
) -> Result<Value, ValidationError> {
let neg_val_err: ValidationError = ShelleyMA(NegativeValue); let neg_val_err: ValidationError = ShelleyMA(NegativeValue);
let mut res: Value = empty_value(); let mut res: Value = empty_value();
for TransactionOutput { amount, .. } in tx_body.outputs.iter() { for TransactionOutput { amount, .. } in tx_body.outputs.iter() {
@ -168,6 +225,13 @@ fn get_produced(tx_body: &TransactionBody, era: &Era) -> Result<Value, Validatio
_ => res = add_values(&res, amount, &neg_val_err)?, _ => 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 +
prot_pps.key_deposit * *stk_dep_count;
res = add_values(&res, &Value::Coin(total_deposits), &neg_val_err)?;
Ok(res) Ok(res)
} }
@ -220,6 +284,10 @@ fn check_witnesses(
let vk_wits: &mut Vec<(bool, VKeyWitness)> = let vk_wits: &mut Vec<(bool, VKeyWitness)> =
&mut mk_alonzo_vk_wits_check_list(&tx_wits.vkeywitness, ShelleyMA(MissingVKWitness))?; &mut mk_alonzo_vk_wits_check_list(&tx_wits.vkeywitness, ShelleyMA(MissingVKWitness))?;
let tx_hash: &Vec<u8> = &Vec::from(tx_body.compute_hash().as_ref()); let tx_hash: &Vec<u8> = &Vec::from(tx_body.compute_hash().as_ref());
let native_scripts: Vec<NativeScript> = 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() { for input in tx_body.inputs.iter() {
match utxos.get(&MultiEraInput::from_alonzo_compatible(input)) { match utxos.get(&MultiEraInput::from_alonzo_compatible(input)) {
Some(multi_era_output) => { Some(multi_era_output) => {
@ -243,6 +311,13 @@ fn check_witnesses(
None => return Err(ShelleyMA(InputNotInUTxO)), 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) check_remaining_vk_wits(vk_wits, tx_hash)
} }
@ -330,3 +405,339 @@ fn compute_script_hash(script: &NativeScript) -> PolicyId {
payload.insert(0, 0); payload.insert(0, 0);
pallas_crypto::hash::Hasher::<224>::hash(&payload) 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<Vec<Certificate>>,
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<K, V, E>(map: &mut HashMap<K, V>, 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::<Vec<_>>();
let fod = ds
.fut_gen_delegs
.iter()
.filter(|kv| kv.0 .1 != *gkh)
.map(|kv| kv.1)
.collect::<Vec<_>>();
let curr_keyhashes = cod.iter().map(|v| v.0.clone()).collect::<Vec<_>>();
let curr_vrfs = cod.iter().map(|v| v.1).collect::<Vec<_>>();
let fut_keyhashes = fod.iter().map(|v| v.0.clone()).collect::<Vec<_>>();
let fut_vrfs = fod.iter().map(|v| v.1).collect::<Vec<_>>();
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<StakeCredential, Coin> = 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::<u64>() > 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<VKeyWitness>, // changed from alonzo
native_scripts: &Vec<NativeScript>,
low_bnd: &Option<u64>,
upp_bnd: &Option<u64>,
) -> 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<VKeyWitness>, // changed from alonzo
native_script: &NativeScript,
low_bnd: &Option<u64>,
upp_bnd: &Option<u64>,
) -> 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
}
}
}
}

View file

@ -12,12 +12,14 @@ use pallas_codec::{
use pallas_crypto::key::ed25519::{PublicKey, Signature}; use pallas_crypto::key::ed25519::{PublicKey, Signature};
use pallas_primitives::{ use pallas_primitives::{
alonzo::{ alonzo::{
AssetName, AuxiliaryData, Coin, MintedTx as AlonzoMintedTx, Multiasset, NativeScript, AddrKeyhash, AssetName, AuxiliaryData, Coin, Epoch, GenesisDelegateHash, Genesishash,
NetworkId, PlutusScript, PolicyId, VKeyWitness, Value, MintedTx as AlonzoMintedTx, Multiasset, NativeScript, NetworkId, PlutusScript, PolicyId,
PoolKeyhash, PoolMetadata, Relay, RewardAccount, StakeCredential, TransactionIndex,
UnitInterval, VKeyWitness, Value, VrfKeyhash,
}, },
babbage::{MintedTx as BabbageMintedTx, PlutusV2Script}, babbage::{MintedTx as BabbageMintedTx, PlutusV2Script},
}; };
use pallas_traverse::{MultiEraInput, MultiEraOutput}; use pallas_traverse::{time::Slot, MultiEraInput, MultiEraOutput};
use std::collections::HashMap; use std::collections::HashMap;
use std::ops::Deref; use std::ops::Deref;
pub use validation::*; pub use validation::*;
@ -101,6 +103,10 @@ pub fn lovelace_diff_or_fail(
} }
pub fn multi_assets_are_equal(fma: &Multiasset<Coin>, sma: &Multiasset<Coin>) -> bool { pub fn multi_assets_are_equal(fma: &Multiasset<Coin>, sma: &Multiasset<Coin>) -> bool {
multi_asset_included(fma, sma) && multi_asset_included(sma, fma)
}
pub fn multi_asset_included(fma: &Multiasset<Coin>, sma: &Multiasset<Coin>) -> bool {
for (fpolicy, fassets) in fma.iter() { for (fpolicy, fassets) in fma.iter() {
match find_policy(sma, fpolicy) { match find_policy(sma, fpolicy) {
Some(sassets) => { Some(sassets) => {
@ -158,7 +164,7 @@ fn coerce_to_coin(
err: &ValidationError, err: &ValidationError,
) -> Result<Multiasset<Coin>, ValidationError> { ) -> Result<Multiasset<Coin>, ValidationError> {
let mut res: Vec<(PolicyId, KeyValuePairs<AssetName, Coin>)> = Vec::new(); let mut res: Vec<(PolicyId, KeyValuePairs<AssetName, Coin>)> = 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(); let mut aa: Vec<(AssetName, Coin)> = Vec::new();
for (asset_name, amount) in assets.clone().to_vec().iter() { for (asset_name, amount) in assets.clone().to_vec().iter() {
if *amount < 0 { if *amount < 0 {
@ -342,3 +348,58 @@ pub fn compute_plutus_v2_script_hash(script: &PlutusV2Script) -> PolicyId {
payload.insert(0, 2); payload.insert(0, 2);
pallas_crypto::hash::Hasher::<224>::hash(&payload) pallas_crypto::hash::Hasher::<224>::hash(&payload)
} }
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<Genesishash, (GenesisDelegateHash, VrfKeyhash)>;
pub type FutGenesisDelegation = HashMap<(Slot, Genesishash), (GenesisDelegateHash, VrfKeyhash)>;
pub type InstantaneousRewards = (
HashMap<StakeCredential, Coin>,
HashMap<StakeCredential, Coin>,
);
#[derive(Default, Clone)] // for testing
pub struct DState {
pub rewards: HashMap<StakeCredential, Coin>,
pub delegations: HashMap<StakeCredential, PoolKeyhash>,
pub ptrs: HashMap<CertPointer, StakeCredential>,
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, Debug)]
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<AddrKeyhash>,
pub relays: Vec<Relay>,
pub pool_metadata: Nullable<PoolMetadata>,
}
#[derive(Default, Clone)] // for testing
pub struct PState {
pub pool_params: HashMap<PoolKeyhash, PoolParam>,
pub fut_pool_params: HashMap<PoolKeyhash, PoolParam>,
pub retiring: HashMap<PoolKeyhash, Epoch>,
}
// Originally `DPState` in ShelleyMA specs, then updated to
// `CertState` in Haskell sources at Intersect (#3369).
#[non_exhaustive]
#[derive(Default, Clone)] // for testing
pub struct CertState {
pub pstate: PState,
pub dstate: DState,
}

View file

@ -178,12 +178,19 @@ pub struct ConwayProtParams {
pub minfee_refscript_cost_per_byte: UnitInterval, pub minfee_refscript_cost_per_byte: UnitInterval,
} }
#[derive(Default, Debug)]
pub struct AccountState {
pub treasury: Coin,
pub reserves: Coin,
}
#[derive(Debug)] #[derive(Debug)]
pub struct Environment { pub struct Environment {
pub prot_params: MultiEraProtocolParameters, pub prot_params: MultiEraProtocolParameters,
pub prot_magic: u32, pub prot_magic: u32,
pub block_slot: u64, pub block_slot: u64,
pub network_id: u8, pub network_id: u8,
pub acnt: Option<AccountState>,
} }
impl Environment { impl Environment {
@ -202,4 +209,8 @@ impl Environment {
pub fn network_id(&self) -> &u8 { pub fn network_id(&self) -> &u8 {
&self.network_id &self.network_id
} }
pub fn acnt(&self) -> &Option<AccountState> {
&self.acnt
}
} }

View file

@ -4,6 +4,8 @@
#[non_exhaustive] #[non_exhaustive]
pub enum ValidationError { pub enum ValidationError {
TxAndProtParamsDiffer, TxAndProtParamsDiffer,
PParamsByronDoesntNeedAccountState,
EnvMissingAccountState,
UnknownProtParams, UnknownProtParams,
Byron(ByronError), Byron(ByronError),
ShelleyMA(ShelleyMAError), ShelleyMA(ShelleyMAError),
@ -49,6 +51,19 @@ pub enum ShelleyMAError {
MissingScriptWitness, MissingScriptWitness,
WrongSignature, WrongSignature,
MintingLacksPolicy, MintingLacksPolicy,
KeyAlreadyRegistered,
KeyNotRegistered,
PointerInUse,
RewardsNotNull,
PoolAlreadyRegistered,
PoolNotRegistered,
PoolCostBelowMin,
DuplicateGenesisDelegate,
DuplicateGenesisVRF,
GenesisKeyNotInMapping,
InsufficientForInstantaneousRewards,
MIRCertificateTooLateinEpoch,
ScriptDenial,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View file

@ -28,8 +28,22 @@ Note that, since phase-1 validations do not include the execution of native scri
List of positive unit tests: 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** ([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_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_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_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_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
rewards, drawn from the Treasury.
List of negative unit tests: List of negative unit tests:
- **empty_ins** takes successful_mainnet_shelley_tx and removes its input. - **empty_ins** takes successful_mainnet_shelley_tx and removes its input.
@ -45,6 +59,16 @@ 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. - **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. - **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_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).
- **unregistered_pool** takes successful_mainnet_mary_tx_with_stk_deleg,
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.
### Alonzo ### Alonzo
*pallas-applying/tests/alonzo.rs* contains multiple unit tests for validation in the Alonzo era. *pallas-applying/tests/alonzo.rs* contains multiple unit tests for validation in the Alonzo era.

View file

@ -5,9 +5,10 @@ use common::*;
use pallas_addresses::{Address, Network, ShelleyAddress, ShelleyPaymentPart}; use pallas_addresses::{Address, Network, ShelleyAddress, ShelleyPaymentPart};
use pallas_applying::{ use pallas_applying::{
utils::{ utils::{
AlonzoError, AlonzoProtParams, Environment, MultiEraProtocolParameters, ValidationError::*, AccountState, AlonzoError, AlonzoProtParams, Environment, MultiEraProtocolParameters,
ValidationError::*,
}, },
validate, UTxOs, validate_txs, CertState, UTxOs,
}; };
use pallas_codec::{ use pallas_codec::{
minicbor::{ minicbor::{
@ -43,13 +44,21 @@ mod alonzo_tests {
None, None,
)], )],
); );
let acnt = AccountState {
treasury: 261_254_564_000_000,
reserves: 0,
};
let env: Environment = Environment { let env: Environment = Environment {
prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_334()), prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_334()),
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 44237276, block_slot: 44237276,
network_id: 1, 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(()) => (), Ok(()) => (),
Err(err) => panic!("Unexpected error ({:?})", err), Err(err) => panic!("Unexpected error ({:?})", err),
} }
@ -143,13 +152,21 @@ mod alonzo_tests {
None, None,
)], )],
); );
let acnt = AccountState {
treasury: 261_254_564_000_000,
reserves: 0,
};
let env: Environment = Environment { let env: Environment = Environment {
prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()), prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()),
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 58924928, block_slot: 58924928,
network_id: 1, 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(()) => (), Ok(()) => (),
Err(err) => panic!("Unexpected error ({:?})", err), Err(err) => panic!("Unexpected error ({:?})", err),
} }
@ -170,13 +187,21 @@ mod alonzo_tests {
None, None,
)], )],
); );
let acnt = AccountState {
treasury: 261_254_564_000_000,
reserves: 0,
};
let env: Environment = Environment { let env: Environment = Environment {
prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()), prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()),
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 6447035, block_slot: 6447035,
network_id: 1, 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(()) => (), Ok(()) => (),
Err(err) => panic!("Unexpected error ({:?})", err), Err(err) => panic!("Unexpected error ({:?})", err),
} }
@ -197,13 +222,21 @@ mod alonzo_tests {
None, None,
)], )],
); );
let acnt = AccountState {
treasury: 261_254_564_000_000,
reserves: 0,
};
let env: Environment = Environment { let env: Environment = Environment {
prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()), prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()),
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 6447038, block_slot: 6447038,
network_id: 1, 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(()) => (), Ok(()) => (),
Err(err) => panic!("Unexpected error ({:?})", err), Err(err) => panic!("Unexpected error ({:?})", err),
} }
@ -229,13 +262,21 @@ mod alonzo_tests {
mtx.transaction_body = mtx.transaction_body =
Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap(); Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap();
let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Alonzo); 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 { let env: Environment = Environment {
prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_334()), prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_334()),
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 44237276, block_slot: 44237276,
network_id: 1, 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"), Ok(()) => panic!("Inputs set should not be empty"),
Err(err) => match err { Err(err) => match err {
Alonzo(AlonzoError::TxInsEmpty) => (), Alonzo(AlonzoError::TxInsEmpty) => (),
@ -252,13 +293,21 @@ mod alonzo_tests {
let mtx: MintedTx = minted_tx_from_cbor(&cbor_bytes); let mtx: MintedTx = minted_tx_from_cbor(&cbor_bytes);
let utxos: UTxOs = UTxOs::new(); let utxos: UTxOs = UTxOs::new();
let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Alonzo); 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 { let env: Environment = Environment {
prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_334()), prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_334()),
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 44237276, block_slot: 44237276,
network_id: 1, 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"), Ok(()) => panic!("All inputs should be within the UTxO set"),
Err(err) => match err { Err(err) => match err {
Alonzo(AlonzoError::InputNotInUTxO) => (), Alonzo(AlonzoError::InputNotInUTxO) => (),
@ -288,13 +337,21 @@ mod alonzo_tests {
mtx.transaction_body = mtx.transaction_body =
Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap(); Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap();
let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Alonzo); 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 { let env: Environment = Environment {
prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_334()), prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_334()),
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 44237276, block_slot: 44237276,
network_id: 1, 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"), Ok(()) => panic!("Validity interval lower bound should have been reached"),
Err(err) => match err { Err(err) => match err {
Alonzo(AlonzoError::BlockPrecedesValInt) => (), Alonzo(AlonzoError::BlockPrecedesValInt) => (),
@ -324,13 +381,21 @@ mod alonzo_tests {
mtx.transaction_body = mtx.transaction_body =
Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap(); Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap();
let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Alonzo); 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 { let env: Environment = Environment {
prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_334()), prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_334()),
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 44237276, block_slot: 44237276,
network_id: 1, 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"), Ok(()) => panic!("Validity interval upper bound should not have been surpassed"),
Err(err) => match err { Err(err) => match err {
Alonzo(AlonzoError::BlockExceedsValInt) => (), Alonzo(AlonzoError::BlockExceedsValInt) => (),
@ -356,13 +421,21 @@ mod alonzo_tests {
); );
let mut alonzo_prot_params: AlonzoProtParams = mk_params_epoch_334(); let mut alonzo_prot_params: AlonzoProtParams = mk_params_epoch_334();
alonzo_prot_params.minfee_a = 79; // This value was 44 during Alonzo on mainnet 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 { let env: Environment = Environment {
prot_params: MultiEraProtocolParameters::Alonzo(alonzo_prot_params), prot_params: MultiEraProtocolParameters::Alonzo(alonzo_prot_params),
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 44237276, block_slot: 44237276,
network_id: 1, 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"), Ok(()) => panic!("Fee should not be below minimum"),
Err(err) => match err { Err(err) => match err {
Alonzo(AlonzoError::FeeBelowMin) => (), Alonzo(AlonzoError::FeeBelowMin) => (),
@ -465,13 +538,21 @@ mod alonzo_tests {
mtx.transaction_body = mtx.transaction_body =
Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap(); Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap();
let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Alonzo); 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 { let env: Environment = Environment {
prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()), prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()),
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 58924928, block_slot: 58924928,
network_id: 1, 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"), Ok(()) => panic!("No collateral inputs"),
Err(err) => match err { Err(err) => match err {
Alonzo(AlonzoError::CollateralMissing) => (), Alonzo(AlonzoError::CollateralMissing) => (),
@ -571,13 +652,20 @@ mod alonzo_tests {
); );
let mut alonzo_prot_params: AlonzoProtParams = mk_params_epoch_300(); 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 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 { let env: Environment = Environment {
prot_params: MultiEraProtocolParameters::Alonzo(alonzo_prot_params), prot_params: MultiEraProtocolParameters::Alonzo(alonzo_prot_params),
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 58924928, block_slot: 58924928,
network_id: 1, 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"), Ok(()) => panic!("Number of collateral inputs should be within limits"),
Err(err) => match err { Err(err) => match err {
Alonzo(AlonzoError::TooManyCollaterals) => (), Alonzo(AlonzoError::TooManyCollaterals) => (),
@ -696,13 +784,20 @@ mod alonzo_tests {
); );
utxos.insert(multi_era_in, multi_era_out); utxos.insert(multi_era_in, multi_era_out);
let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Alonzo); 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 { let env: Environment = Environment {
prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()), prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()),
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 58924928, block_slot: 58924928,
network_id: 1, 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"), Ok(()) => panic!("Collateral inputs should be verification-key locked"),
Err(err) => match err { Err(err) => match err {
Alonzo(AlonzoError::CollateralNotVKeyLocked) => (), Alonzo(AlonzoError::CollateralNotVKeyLocked) => (),
@ -810,13 +905,20 @@ mod alonzo_tests {
None, None,
)], )],
); );
let acnt = AccountState {
treasury: 261_254_564_000_000,
reserves: 0,
};
let env: Environment = Environment { let env: Environment = Environment {
prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()), prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()),
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 58924928, block_slot: 58924928,
network_id: 1, 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"), Ok(()) => panic!("Collateral inputs should contain only lovelace"),
Err(err) => match err { Err(err) => match err {
Alonzo(AlonzoError::NonLovelaceCollateral) => (), Alonzo(AlonzoError::NonLovelaceCollateral) => (),
@ -915,13 +1017,20 @@ mod alonzo_tests {
); );
let mut alonzo_prot_params: AlonzoProtParams = mk_params_epoch_300(); let mut alonzo_prot_params: AlonzoProtParams = mk_params_epoch_300();
alonzo_prot_params.collateral_percentage = 700; // This was 150 during Alonzo on mainnet. 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 { let env: Environment = Environment {
prot_params: MultiEraProtocolParameters::Alonzo(alonzo_prot_params), prot_params: MultiEraProtocolParameters::Alonzo(alonzo_prot_params),
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 58924928, block_slot: 58924928,
network_id: 1, 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"), Ok(()) => panic!("Collateral inputs should contain the minimum lovelace"),
Err(err) => match err { Err(err) => match err {
Alonzo(AlonzoError::CollateralMinLovelace) => (), Alonzo(AlonzoError::CollateralMinLovelace) => (),
@ -951,13 +1060,20 @@ mod alonzo_tests {
mtx.transaction_body = mtx.transaction_body =
Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap(); Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap();
let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Alonzo); 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 { let env: Environment = Environment {
prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_334()), prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_334()),
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 44237276, block_slot: 44237276,
network_id: 1, 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"), Ok(()) => panic!("Preservation of value does not hold"),
Err(err) => match err { Err(err) => match err {
Alonzo(AlonzoError::PreservationOfValue) => (), Alonzo(AlonzoError::PreservationOfValue) => (),
@ -1007,13 +1123,20 @@ mod alonzo_tests {
None, None,
)], )],
); );
let acnt = AccountState {
treasury: 261_254_564_000_000,
reserves: 0,
};
let env: Environment = Environment { let env: Environment = Environment {
prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_334()), prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_334()),
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 44237276, block_slot: 44237276,
network_id: 1, 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"), Ok(()) => panic!("Output network ID should match environment network ID"),
Err(err) => match err { Err(err) => match err {
Alonzo(AlonzoError::OutputWrongNetworkID) => (), Alonzo(AlonzoError::OutputWrongNetworkID) => (),
@ -1045,13 +1168,20 @@ mod alonzo_tests {
None, None,
)], )],
); );
let acnt = AccountState {
treasury: 261_254_564_000_000,
reserves: 0,
};
let env: Environment = Environment { let env: Environment = Environment {
prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_334()), prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_334()),
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 44237276, block_slot: 44237276,
network_id: 1, 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"), Ok(()) => panic!("Transaction network ID should match environment network ID"),
Err(err) => match err { Err(err) => match err {
Alonzo(AlonzoError::TxWrongNetworkID) => (), Alonzo(AlonzoError::TxWrongNetworkID) => (),
@ -1151,13 +1281,20 @@ mod alonzo_tests {
let mut alonzo_prot_params: AlonzoProtParams = mk_params_epoch_300(); 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.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 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 { let env: Environment = Environment {
prot_params: MultiEraProtocolParameters::Alonzo(alonzo_prot_params), prot_params: MultiEraProtocolParameters::Alonzo(alonzo_prot_params),
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 58924928, block_slot: 58924928,
network_id: 1, 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"), Ok(()) => panic!("Transaction ex units should be below maximum"),
Err(err) => match err { Err(err) => match err {
Alonzo(AlonzoError::TxExUnitsExceeded) => (), Alonzo(AlonzoError::TxExUnitsExceeded) => (),
@ -1184,13 +1321,20 @@ mod alonzo_tests {
); );
let mut alonzo_prot_params: AlonzoProtParams = mk_params_epoch_334(); 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 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 { let env: Environment = Environment {
prot_params: MultiEraProtocolParameters::Alonzo(alonzo_prot_params), prot_params: MultiEraProtocolParameters::Alonzo(alonzo_prot_params),
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 44237276, block_slot: 44237276,
network_id: 1, 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!( Ok(()) => panic!(
"Transaction size should not exceed the maximum allowed by the protocol parameter" "Transaction size should not exceed the maximum allowed by the protocol parameter"
), ),
@ -1301,13 +1445,20 @@ mod alonzo_tests {
mtx.transaction_body = mtx.transaction_body =
Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap(); Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap();
let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Alonzo); 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 { let env: Environment = Environment {
prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()), prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()),
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 58924928, block_slot: 58924928,
network_id: 1, 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"), Ok(()) => panic!("All required signers should have signed the transaction"),
Err(err) => match err { Err(err) => match err {
Alonzo(AlonzoError::ReqSignerMissing) => (), Alonzo(AlonzoError::ReqSignerMissing) => (),
@ -1337,13 +1488,20 @@ mod alonzo_tests {
mtx.transaction_witness_set = mtx.transaction_witness_set =
Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap(); Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap();
let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Alonzo); 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 { let env: Environment = Environment {
prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_334()), prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_334()),
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 44237276, block_slot: 44237276,
network_id: 1, 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"), Ok(()) => panic!("Missing verification key witness"),
Err(err) => match err { Err(err) => match err {
Alonzo(AlonzoError::VKWitnessMissing) => (), Alonzo(AlonzoError::VKWitnessMissing) => (),
@ -1380,13 +1538,20 @@ mod alonzo_tests {
mtx.transaction_witness_set = mtx.transaction_witness_set =
Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap(); Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap();
let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Alonzo); 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 { let env: Environment = Environment {
prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_334()), prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_334()),
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 44237276, block_slot: 44237276,
network_id: 1, 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"), Ok(()) => panic!("Witness signature should verify the transaction"),
Err(err) => match err { Err(err) => match err {
Alonzo(AlonzoError::VKWrongSignature) => (), Alonzo(AlonzoError::VKWrongSignature) => (),
@ -1489,13 +1654,20 @@ mod alonzo_tests {
mtx.transaction_witness_set = mtx.transaction_witness_set =
Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap(); Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap();
let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Alonzo); 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 { let env: Environment = Environment {
prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()), prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()),
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 58924928, block_slot: 58924928,
network_id: 1, 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"), Ok(()) => panic!("Missing Plutus script"),
Err(err) => match err { Err(err) => match err {
Alonzo(AlonzoError::ScriptWitnessMissing) => (), Alonzo(AlonzoError::ScriptWitnessMissing) => (),
@ -1606,13 +1778,20 @@ mod alonzo_tests {
mtx.transaction_witness_set = mtx.transaction_witness_set =
Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap(); Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap();
let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Alonzo); 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 { let env: Environment = Environment {
prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()), prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()),
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 58924928, block_slot: 58924928,
network_id: 1, 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"), Ok(()) => panic!("Unneeded Plutus script"),
Err(err) => match err { Err(err) => match err {
Alonzo(AlonzoError::UnneededNativeScript) => (), Alonzo(AlonzoError::UnneededNativeScript) => (),
@ -1642,13 +1821,20 @@ mod alonzo_tests {
mtx.transaction_witness_set = mtx.transaction_witness_set =
Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap(); Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap();
let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Alonzo); 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 { let env: Environment = Environment {
prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()), prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()),
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 6447035, block_slot: 6447035,
network_id: 1, 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"), Ok(()) => panic!("Minting policy is not supported by a matching native script"),
Err(err) => match err { Err(err) => match err {
Alonzo(AlonzoError::MintingLacksPolicy) => (), Alonzo(AlonzoError::MintingLacksPolicy) => (),
@ -1751,13 +1937,20 @@ mod alonzo_tests {
mtx.transaction_witness_set = mtx.transaction_witness_set =
Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap(); Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap();
let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Alonzo); 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 { let env: Environment = Environment {
prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()), prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()),
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 58924928, block_slot: 58924928,
network_id: 1, 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"), Ok(()) => panic!("Missing datum"),
Err(err) => match err { Err(err) => match err {
Alonzo(AlonzoError::DatumMissing) => (), Alonzo(AlonzoError::DatumMissing) => (),
@ -1866,13 +2059,20 @@ mod alonzo_tests {
mtx.transaction_witness_set = mtx.transaction_witness_set =
Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap(); Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap();
let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Alonzo); 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 { let env: Environment = Environment {
prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()), prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()),
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 58924928, block_slot: 58924928,
network_id: 1, 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"), Ok(()) => panic!("Unneeded datum"),
Err(err) => match err { Err(err) => match err {
Alonzo(AlonzoError::UnneededDatum) => (), Alonzo(AlonzoError::UnneededDatum) => (),
@ -1982,13 +2182,20 @@ mod alonzo_tests {
mtx.transaction_witness_set = mtx.transaction_witness_set =
Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap(); Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap();
let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Alonzo); 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 { let env: Environment = Environment {
prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()), prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()),
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 58924928, block_slot: 58924928,
network_id: 1, 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"), Ok(()) => panic!("Unneeded redeemer"),
Err(err) => match err { Err(err) => match err {
Alonzo(AlonzoError::UnneededRedeemer) => (), Alonzo(AlonzoError::UnneededRedeemer) => (),
@ -2091,13 +2298,20 @@ mod alonzo_tests {
mtx.transaction_witness_set = mtx.transaction_witness_set =
Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap(); Decode::decode(&mut Decoder::new(tx_buf.as_slice()), &mut ()).unwrap();
let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Alonzo); 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 { let env: Environment = Environment {
prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()), prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()),
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 58924928, block_slot: 58924928,
network_id: 1, 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"), Ok(()) => panic!("Redeemer missing"),
Err(err) => match err { Err(err) => match err {
Alonzo(AlonzoError::RedeemerMissing) => (), Alonzo(AlonzoError::RedeemerMissing) => (),
@ -2122,13 +2336,20 @@ mod alonzo_tests {
None, None,
)], )],
); );
let acnt = AccountState {
treasury: 261_254_564_000_000,
reserves: 0,
};
let env: Environment = Environment { let env: Environment = Environment {
prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()), prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()),
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 6447038, block_slot: 6447038,
network_id: 1, 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"), Ok(()) => panic!("Transaction auxiliary data removed"),
Err(err) => match err { Err(err) => match err {
Alonzo(AlonzoError::MetadataHash) => (), Alonzo(AlonzoError::MetadataHash) => (),
@ -2154,13 +2375,20 @@ mod alonzo_tests {
); );
let mut alonzo_prot_params: AlonzoProtParams = mk_params_epoch_334(); 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. 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 { let env: Environment = Environment {
prot_params: MultiEraProtocolParameters::Alonzo(alonzo_prot_params), prot_params: MultiEraProtocolParameters::Alonzo(alonzo_prot_params),
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 44237276, block_slot: 44237276,
network_id: 1, 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"), Ok(()) => panic!("Output minimum lovelace is unreached"),
Err(err) => match err { Err(err) => match err {
Alonzo(AlonzoError::MinLovelaceUnreached) => (), Alonzo(AlonzoError::MinLovelaceUnreached) => (),
@ -2186,13 +2414,20 @@ mod alonzo_tests {
); );
let mut alonzo_prot_params: AlonzoProtParams = mk_params_epoch_334(); let mut alonzo_prot_params: AlonzoProtParams = mk_params_epoch_334();
alonzo_prot_params.max_value_size = 0; // This was 5000 during Alonzo on mainnet 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 { let env: Environment = Environment {
prot_params: MultiEraProtocolParameters::Alonzo(alonzo_prot_params), prot_params: MultiEraProtocolParameters::Alonzo(alonzo_prot_params),
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 44237276, block_slot: 44237276,
network_id: 1, 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"), Ok(()) => panic!("Max value size exceeded"),
Err(err) => match err { Err(err) => match err {
Alonzo(AlonzoError::MaxValSizeExceeded) => (), Alonzo(AlonzoError::MaxValSizeExceeded) => (),
@ -2299,13 +2534,20 @@ mod alonzo_tests {
mtx.transaction_witness_set = mtx.transaction_witness_set =
Decode::decode(&mut Decoder::new(tx_witness_set_buf.as_slice()), &mut ()).unwrap(); Decode::decode(&mut Decoder::new(tx_witness_set_buf.as_slice()), &mut ()).unwrap();
let metx: MultiEraTx = MultiEraTx::from_alonzo_compatible(&mtx, Era::Alonzo); 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 { let env: Environment = Environment {
prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()), prot_params: MultiEraProtocolParameters::Alonzo(mk_params_epoch_300()),
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 58924928, block_slot: 58924928,
network_id: 1, 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"), Ok(()) => panic!("Wrong script integrity hash"),
Err(err) => match err { Err(err) => match err {
Alonzo(AlonzoError::ScriptIntegrityHash) => (), Alonzo(AlonzoError::ScriptIntegrityHash) => (),

File diff suppressed because one or more lines are too long

View file

@ -3,9 +3,10 @@ pub mod common;
use common::{cbor_to_bytes, minted_tx_payload_from_cbor, mk_utxo_for_byron_tx}; use common::{cbor_to_bytes, minted_tx_payload_from_cbor, mk_utxo_for_byron_tx};
use pallas_applying::{ use pallas_applying::{
utils::{ utils::{
ByronError, ByronProtParams, Environment, MultiEraProtocolParameters, ValidationError::*, ByronError, ByronProtParams, CertState, Environment, MultiEraProtocolParameters,
ValidationError::*,
}, },
validate, UTxOs, validate_txs, UTxOs,
}; };
use pallas_codec::{ use pallas_codec::{
@ -59,8 +60,10 @@ mod byron_tests {
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 6341, block_slot: 6341,
network_id: 1, 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(()) => (), Ok(()) => (),
Err(err) => panic!("Unexpected error ({:?})", err), Err(err) => panic!("Unexpected error ({:?})", err),
} }
@ -102,8 +105,10 @@ mod byron_tests {
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 3241381, block_slot: 3241381,
network_id: 1, 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(()) => (), Ok(()) => (),
Err(err) => panic!("Unexpected error ({:?})", err), Err(err) => panic!("Unexpected error ({:?})", err),
} }
@ -153,8 +158,10 @@ mod byron_tests {
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 3241381, block_slot: 3241381,
network_id: 1, 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"), Ok(()) => panic!("Inputs set should not be empty"),
Err(err) => match err { Err(err) => match err {
Byron(ByronError::TxInsEmpty) => (), Byron(ByronError::TxInsEmpty) => (),
@ -207,8 +214,10 @@ mod byron_tests {
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 3241381, block_slot: 3241381,
network_id: 1, 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"), Ok(()) => panic!("Outputs set should not be empty"),
Err(err) => match err { Err(err) => match err {
Byron(ByronError::TxOutsEmpty) => (), Byron(ByronError::TxOutsEmpty) => (),
@ -246,8 +255,10 @@ mod byron_tests {
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 3241381, block_slot: 3241381,
network_id: 1, 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"), Ok(()) => panic!("All inputs must be within the UTxO set"),
Err(err) => match err { Err(err) => match err {
Byron(ByronError::InputNotInUTxO) => (), Byron(ByronError::InputNotInUTxO) => (),
@ -306,8 +317,10 @@ mod byron_tests {
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 3241381, block_slot: 3241381,
network_id: 1, 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"), Ok(()) => panic!("All outputs must contain lovelace"),
Err(err) => match err { Err(err) => match err {
Byron(ByronError::OutputWithoutLovelace) => (), Byron(ByronError::OutputWithoutLovelace) => (),
@ -351,8 +364,10 @@ mod byron_tests {
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 3241381, block_slot: 3241381,
network_id: 1, 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"), Ok(()) => panic!("Fees should not be below minimum"),
Err(err) => match err { Err(err) => match err {
Byron(ByronError::FeesBelowMin) => (), Byron(ByronError::FeesBelowMin) => (),
@ -396,8 +411,10 @@ mod byron_tests {
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 3241381, block_slot: 3241381,
network_id: 1, 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"), Ok(()) => panic!("Transaction size cannot exceed protocol limit"),
Err(err) => match err { Err(err) => match err {
Byron(ByronError::MaxTxSizeExceeded) => (), Byron(ByronError::MaxTxSizeExceeded) => (),
@ -449,8 +466,10 @@ mod byron_tests {
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 3241381, block_slot: 3241381,
network_id: 1, 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"), Ok(()) => panic!("All inputs must have a witness signature"),
Err(err) => match err { Err(err) => match err {
Byron(ByronError::MissingWitness) => (), Byron(ByronError::MissingWitness) => (),
@ -511,8 +530,10 @@ mod byron_tests {
prot_magic: 764824073, prot_magic: 764824073,
block_slot: 3241381, block_slot: 3241381,
network_id: 1, 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"), Ok(()) => panic!("Witness signature should verify the transaction"),
Err(err) => match err { Err(err) => match err {
Byron(ByronError::WrongSignature) => (), Byron(ByronError::WrongSignature) => (),

File diff suppressed because it is too large Load diff

View file

@ -413,7 +413,7 @@ pub type UnitInterval = RationalNumber;
pub type PositiveInterval = 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 { pub enum StakeCredential {
AddrKeyhash(AddrKeyhash), AddrKeyhash(AddrKeyhash),
Scripthash(Scripthash), Scripthash(Scripthash),

1
test_data/allegra1.tx Normal file

File diff suppressed because one or more lines are too long

1
test_data/mary2.tx Normal file
View file

@ -0,0 +1 @@
84a500818258201dd22b2976374f9b8e6aa045ded141742fa5adc5184a505410fb9f343d14e407000181825839018e8f7a7073b8a95a4c1f1cf412b1042fca4945b89eb11754b3481b29fb2b631db76384f64dd94b47f97fc8c2a206764c17a1de7da2f70e831a3c0f17fa021a0002fce9031a0191f62f04828a03581c59ebe72ae96462018fbe04633100f90b3066688d85f00f3bd254707f58201efb798f239b9b02deb4636a3ab1962af43512595fcb82276e11971e684e49b71a3b9aca001a1443fd00d81e82011864581de1fb2b631db76384f64dd94b47f97fc8c2a206764c17a1de7da2f70e8381581cfb2b631db76384f64dd94b47f97fc8c2a206764c17a1de7da2f70e83818400190bb944c22614bbf682781c68747470733a2f2f6361726461706f6f6c2e636f6d2f612e6a736f6e582001f708549816c9a075ff96e9682c11a5f5c7f4e147862a663bdeece0716ab76e83028200581cfb2b631db76384f64dd94b47f97fc8c2a206764c17a1de7da2f70e83581c59ebe72ae96462018fbe04633100f90b3066688d85f00f3bd254707fa100838258205ac72fb72ac603eb4aa1be5b98af76f193e24924b4a7c877f9e9c401fb9cda385840bdbd394d90106cd033224caffbba57fc071a0e00087bc26aa07174ec42d26060259cb865cbd8ce0981897c1fb0ebc38678420b52731827e9cb00f5209649850b825820af5d32c73a976fcd7abed9fa9c85e128862bd870c8ebc51f602a053343fab8dc584032bb6013443435846521d42e6cf076f0769766ed366661d286fda0788622f603339afa20649123a04cc4266581e328a5f02f9eca866fa44e971a6d8ab7bb5c04825820bc08576470a74179757177311dd8bdae9e56a637d58ed5d50a737d2b03dd9e685840cf586f9bc2ef1684c396e047d9923ab94fa616aed013d5f7c7d3c730ec75f0cd6021a51bd9bdb6f91711d92e332b21fef2221809659b56d538debaaf0bc02b0ef5f6

1
test_data/mary3.tx Normal file
View file

@ -0,0 +1 @@
84a50081825820f8cad2b3e5a3744e096cbb3040ff6057560d3ec8d5444fda1efc669f9c2c7d30060181825839015d74deb638695cc12d11b7dfa50b2023b626287371674fecaa569b8039cf0461807b986a6477205e376dac280d7f150eb497025f67c497571a2549ae4f021a0002a8b1031a01bb2735048282008200581c39cf0461807b986a6477205e376dac280d7f150eb497025f67c4975783028200581c39cf0461807b986a6477205e376dac280d7f150eb497025f67c49757581c59ebe72ae96462018fbe04633100f90b3066688d85f00f3bd254707fa100828258208f9010e31a25735302c168f71416bae8d5cc8333067e06cc5d3c3e0b9d1d262058403719305623d25de0648e17cf041a30f28483d4e0354cdd1d12a3e9c13210327c736754049686e6f7315bb9c4b5bcc50cf140290452362e8a2beb66efcd1b2a02825820e7101958033ebca48dc8d71dee85d1a10c1e94460ec43275b1e82e2638a096b05840b6c16dadb85ae630aa0376b6fd26d103b33085a8b12011e515fa265d548431e65a8eb2c8091ed0bc3b364551eaba47ced495bdce4de758509539481ec123d70af5f6

1
test_data/shelley4.tx Normal file
View file

@ -0,0 +1 @@
84a40081825820e7db1f809fcc21d3dd108ced6218bf0f0cbb6a0f679f848ff1790b68d3a35872000181825839010c57a4aa08aaa7c42b45e4e9490151e2665dbb7d374e795ad5be5e4960562a0d213c675c2b84ee0e34eb377d4abbe82a4c256a0708baac251a0016e360021a0007a120031a010c59f8a200838258205df1be8b0071123c982a94c19c0c06485dbe9271e4381e8cf4fc2ed554ac133f5840c271a9d652ae95e6a8a5117c5026369235182da7e4fe040b02465089e5e2caf05063cf8b61601e6f6074c03f700bacaadcd62483c48d66a5f8d418a7c27c4b01825820403171966fadb1ce9b26852cb74018a04bc031a4aee92be39702b18efd75e058584039395858906ec9ab7540e79022b25a1f3bdfece09f9e2f36254eb5abe625b72dbd8179ece4c9fc1d537afce95b67d8095d29e1f3c50de4ecc30fd67e1ba440048258206311da054c5dfa3ac53c9fc3be859bd322f0712f7e093596fa5f6de031d95acd58403d7deba60f80a03f5bf172c1699f07ecef557d9509551cbe8d2b9000e903c3e3f60bb127c9c0b4cd5df84c01791b98e10fe19209088d0b085c74702b25914a0d01818202838200581ca96da581c39549aeda81f539ac3940ac0cb53657e774ca7e68f15ed98200581cccfcb3fed004562be1354c837a4a4b9f4b1c2b6705229efeedd12d4d8200581c74fcd61aecebe36aa6b6cd4314027282fa4b41c3ce8af17d9b77d0d1f5f6