Skip to main content

pallas_applying/
utils.rs

1//! Base types used for validating transactions in each era.
2
3pub mod environment;
4pub mod validation;
5
6pub use environment::*;
7use pallas_addresses::{Address, ShelleyAddress, ShelleyPaymentPart};
8use pallas_codec::{
9    minicbor::encode,
10    utils::{Bytes, KeepRaw, KeyValuePairs, Nullable},
11};
12use pallas_crypto::key::ed25519::{PublicKey, Signature};
13use pallas_primitives::{
14    alonzo::{
15        AuxiliaryData, MintedTx as AlonzoMintedTx, Multiasset, NativeScript, VKeyWitness, Value,
16    },
17    babbage::MintedTx as BabbageMintedTx,
18    AddrKeyhash, AssetName, Coin, Epoch, GenesisDelegateHash, Genesishash, NetworkId, PlutusScript,
19    PolicyId, PoolKeyhash, PoolMetadata, Relay, RewardAccount, StakeCredential, TransactionIndex,
20    UnitInterval, VrfKeyhash,
21};
22use pallas_traverse::{time::Slot, MultiEraInput, MultiEraOutput};
23use std::collections::HashMap;
24use std::ops::Deref;
25pub use validation::*;
26
27pub type UTxOs<'b> = HashMap<MultiEraInput<'b>, MultiEraOutput<'b>>;
28
29pub fn get_alonzo_comp_tx_size(mtx: &AlonzoMintedTx) -> u32 {
30    match &mtx.auxiliary_data {
31        Nullable::Some(aux_data) => {
32            (aux_data.raw_cbor().len()
33                + mtx.transaction_body.raw_cbor().len()
34                + mtx.transaction_witness_set.raw_cbor().len()) as u32
35        }
36        _ => {
37            (mtx.transaction_body.raw_cbor().len() + mtx.transaction_witness_set.raw_cbor().len())
38                as u32
39        }
40    }
41}
42
43pub fn get_babbage_tx_size(mtx: &BabbageMintedTx) -> Option<u32> {
44    let mut buff: Vec<u8> = Vec::new();
45    match encode(mtx, &mut buff) {
46        Ok(()) => Some(buff.len() as u32),
47        Err(_) => None,
48    }
49}
50
51pub fn empty_value() -> Value {
52    Value::Multiasset(0, Multiasset::<Coin>::from(Vec::new()))
53}
54
55pub fn add_values(
56    first: &Value,
57    second: &Value,
58    err: &ValidationError,
59) -> Result<Value, ValidationError> {
60    match (first, second) {
61        (Value::Coin(f), Value::Coin(s)) => Ok(Value::Coin(f + s)),
62        (Value::Multiasset(f, fma), Value::Coin(s)) => Ok(Value::Multiasset(f + s, fma.clone())),
63        (Value::Coin(f), Value::Multiasset(s, sma)) => Ok(Value::Multiasset(f + s, sma.clone())),
64        (Value::Multiasset(f, fma), Value::Multiasset(s, sma)) => Ok(Value::Multiasset(
65            f + s,
66            coerce_to_coin(
67                &add_multiasset_values(&coerce_to_i64(fma), &coerce_to_i64(sma)),
68                err,
69            )?,
70        )),
71    }
72}
73
74pub fn lovelace_diff_or_fail(
75    first: &Value,
76    second: &Value,
77    err: &ValidationError,
78) -> Result<u64, ValidationError> {
79    match (first, second) {
80        (Value::Coin(f), Value::Coin(s)) => {
81            if f >= s {
82                Ok(f - s)
83            } else {
84                Err(err.clone())
85            }
86        }
87        (Value::Coin(_), Value::Multiasset(_, _)) => Err(err.clone()),
88        (Value::Multiasset(f, fma), Value::Coin(s)) => {
89            if f >= s && fma.is_empty() {
90                Ok(f - s)
91            } else {
92                Err(err.clone())
93            }
94        }
95        (Value::Multiasset(f, fma), Value::Multiasset(s, sma)) => {
96            if f >= s && multi_assets_are_equal(fma, sma) {
97                Ok(f - s)
98            } else {
99                Err(err.clone())
100            }
101        }
102    }
103}
104
105pub fn multi_assets_are_equal(fma: &Multiasset<Coin>, sma: &Multiasset<Coin>) -> bool {
106    multi_asset_included(fma, sma) && multi_asset_included(sma, fma)
107}
108
109pub fn multi_asset_included(fma: &Multiasset<Coin>, sma: &Multiasset<Coin>) -> bool {
110    for (fpolicy, fassets) in fma.iter() {
111        match find_policy(sma, fpolicy) {
112            Some(sassets) => {
113                for (fasset_name, famount) in fassets.iter() {
114                    // Discard the case where there is 0 of an asset
115                    if *famount != 0 {
116                        match find_assets(&sassets, fasset_name) {
117                            Some(samount) => {
118                                if *famount != samount {
119                                    return false;
120                                }
121                            }
122                            None => return false,
123                        };
124                    }
125                }
126            }
127            None => return false,
128        }
129    }
130    true
131}
132
133pub fn add_minted_value(
134    base_value: &Value,
135    minted_value: &Multiasset<i64>,
136    err: &ValidationError,
137) -> Result<Value, ValidationError> {
138    match base_value {
139        Value::Coin(n) => Ok(Value::Multiasset(*n, coerce_to_coin(minted_value, err)?)),
140        Value::Multiasset(n, mary_base_value) => Ok(Value::Multiasset(
141            *n,
142            coerce_to_coin(
143                &add_multiasset_values(&coerce_to_i64(mary_base_value), minted_value),
144                err,
145            )?,
146        )),
147    }
148}
149
150fn coerce_to_i64(value: &Multiasset<Coin>) -> Multiasset<i64> {
151    let mut res: Vec<(PolicyId, KeyValuePairs<AssetName, i64>)> = Vec::new();
152    for (policy, assets) in value.clone().to_vec().iter() {
153        let mut aa: Vec<(AssetName, i64)> = Vec::new();
154        for (asset_name, amount) in assets.clone().to_vec().iter() {
155            aa.push((asset_name.clone(), *amount as i64));
156        }
157        res.push((*policy, KeyValuePairs::<AssetName, i64>::from(aa)));
158    }
159    KeyValuePairs::<PolicyId, KeyValuePairs<AssetName, i64>>::from(res)
160}
161
162fn coerce_to_coin(
163    value: &Multiasset<i64>,
164    err: &ValidationError,
165) -> Result<Multiasset<Coin>, ValidationError> {
166    let mut res: Vec<(PolicyId, KeyValuePairs<AssetName, Coin>)> = Vec::new();
167    for (policy, assets) in value.iter() {
168        let mut aa: Vec<(AssetName, Coin)> = Vec::new();
169        for (asset_name, amount) in assets.clone().to_vec().iter() {
170            if *amount < 0 {
171                return Err(err.clone());
172            }
173            aa.push((asset_name.clone(), *amount as u64));
174        }
175        res.push((*policy, KeyValuePairs::<AssetName, Coin>::from(aa)));
176    }
177    Ok(KeyValuePairs::<PolicyId, KeyValuePairs<AssetName, Coin>>::from(res))
178}
179
180fn add_multiasset_values(first: &Multiasset<i64>, second: &Multiasset<i64>) -> Multiasset<i64> {
181    let mut res: HashMap<PolicyId, HashMap<AssetName, i64>> = HashMap::new();
182    for (policy, new_assets) in first.iter() {
183        match res.get(policy) {
184            Some(old_assets) => res.insert(*policy, add_same_policy_assets(old_assets, new_assets)),
185            None => res.insert(*policy, add_same_policy_assets(&HashMap::new(), new_assets)),
186        };
187    }
188    for (policy, new_assets) in second.iter() {
189        match res.get(policy) {
190            Some(old_assets) => res.insert(*policy, add_same_policy_assets(old_assets, new_assets)),
191            None => res.insert(*policy, add_same_policy_assets(&HashMap::new(), new_assets)),
192        };
193    }
194    wrap_multiasset(res)
195}
196
197fn add_same_policy_assets(
198    old_assets: &HashMap<AssetName, i64>,
199    new_assets: &KeyValuePairs<AssetName, i64>,
200) -> HashMap<AssetName, i64> {
201    let mut res: HashMap<AssetName, i64> = old_assets.clone();
202    for (asset_name, new_amount) in new_assets.iter() {
203        match res.get(asset_name) {
204            Some(old_amount) => res.insert(asset_name.clone(), old_amount + *new_amount),
205            None => res.insert(asset_name.clone(), *new_amount),
206        };
207    }
208    res
209}
210
211fn wrap_multiasset(input: HashMap<PolicyId, HashMap<AssetName, i64>>) -> Multiasset<i64> {
212    Multiasset::<i64>::from(
213        input
214            .into_iter()
215            .map(|(policy, assets)| {
216                (
217                    policy,
218                    KeyValuePairs::<AssetName, i64>::from(
219                        assets.into_iter().collect::<Vec<(AssetName, i64)>>(),
220                    ),
221                )
222            })
223            .collect::<Vec<(PolicyId, KeyValuePairs<AssetName, i64>)>>(),
224    )
225}
226
227pub fn values_are_equal(first: &Value, second: &Value) -> bool {
228    match (first, second) {
229        (Value::Coin(f), Value::Coin(s)) => f == s,
230        (Value::Multiasset(..), Value::Coin(..)) => false,
231        (Value::Coin(..), Value::Multiasset(..)) => false,
232        (Value::Multiasset(f, fma), Value::Multiasset(s, sma)) => {
233            if f != s {
234                false
235            } else {
236                multi_assets_are_equal(fma, sma)
237            }
238        }
239    }
240}
241
242fn find_policy(
243    mary_value: &Multiasset<Coin>,
244    search_policy: &PolicyId,
245) -> Option<KeyValuePairs<AssetName, Coin>> {
246    for (policy, assets) in mary_value.clone().to_vec().iter() {
247        if policy == search_policy {
248            return Some(assets.clone());
249        }
250    }
251    None
252}
253
254fn find_assets(assets: &KeyValuePairs<AssetName, Coin>, asset_name: &AssetName) -> Option<Coin> {
255    for (an, amount) in assets.clone().to_vec().iter() {
256        if an == asset_name {
257            return Some(*amount);
258        }
259    }
260    None
261}
262
263pub fn get_lovelace_from_alonzo_val(val: &Value) -> Coin {
264    match val {
265        Value::Coin(res) => *res,
266        Value::Multiasset(res, _) => *res,
267    }
268}
269
270#[deprecated(since = "0.31.0", note = "use `u8::from(...)` instead")]
271pub fn get_network_id_value(network_id: NetworkId) -> u8 {
272    u8::from(network_id)
273}
274
275pub fn mk_alonzo_vk_wits_check_list(
276    wits: &Option<Vec<VKeyWitness>>,
277    err: ValidationError,
278) -> Result<Vec<(bool, VKeyWitness)>, ValidationError> {
279    Ok(wits
280        .clone()
281        .ok_or(err)?
282        .iter()
283        .map(|x| (false, x.clone()))
284        .collect::<Vec<(bool, VKeyWitness)>>())
285}
286
287pub fn verify_signature(vk_wit: &VKeyWitness, data_to_verify: &[u8]) -> bool {
288    let mut public_key_source: [u8; PublicKey::SIZE] = [0; PublicKey::SIZE];
289    public_key_source.copy_from_slice(vk_wit.vkey.as_slice());
290    let public_key: PublicKey = From::<[u8; PublicKey::SIZE]>::from(public_key_source);
291    let mut signature_source: [u8; Signature::SIZE] = [0; Signature::SIZE];
292    signature_source.copy_from_slice(vk_wit.signature.as_slice());
293    let sig: Signature = From::<[u8; Signature::SIZE]>::from(signature_source);
294    public_key.verify(data_to_verify, &sig)
295}
296
297pub fn get_payment_part(address: &Bytes) -> Option<ShelleyPaymentPart> {
298    let addr: ShelleyAddress = get_shelley_address(Bytes::deref(address))?;
299    Some(addr.payment().clone())
300}
301
302pub fn get_shelley_address(address: &[u8]) -> Option<ShelleyAddress> {
303    match Address::from_bytes(address) {
304        Ok(Address::Shelley(sa)) => Some(sa),
305        _ => None,
306    }
307}
308
309pub fn is_byron_address(address: &[u8]) -> bool {
310    matches!(Address::from_bytes(address), Ok(Address::Byron(_)))
311}
312
313pub fn aux_data_from_alonzo_minted_tx<'a>(mtx: &'a AlonzoMintedTx) -> Option<&'a [u8]> {
314    Option::<KeepRaw<AuxiliaryData>>::from((mtx.auxiliary_data).clone())
315        .as_ref()
316        .map(KeepRaw::raw_cbor)
317}
318
319pub fn aux_data_from_babbage_minted_tx<'a>(mtx: &'a BabbageMintedTx) -> Option<&'a [u8]> {
320    Option::<KeepRaw<AuxiliaryData>>::from((mtx.auxiliary_data).clone())
321        .as_ref()
322        .map(KeepRaw::raw_cbor)
323}
324
325pub fn get_val_size_in_words(val: &Value) -> u64 {
326    let mut tx_buf: Vec<u8> = Vec::new();
327    let _ = encode(val, &mut tx_buf);
328    (tx_buf.len() as u64).div_ceil(8) // ceiling of the result of dividing
329}
330
331pub fn compute_native_script_hash(script: &NativeScript) -> PolicyId {
332    let mut payload = Vec::new();
333    let _ = encode(script, &mut payload);
334    payload.insert(0, 0);
335    pallas_crypto::hash::Hasher::<224>::hash(&payload)
336}
337
338#[deprecated(since = "0.31.0", note = "use `compute_plutus_v1_script_hash` instead")]
339pub fn compute_plutus_script_hash(script: &PlutusScript<1>) -> PolicyId {
340    compute_plutus_v1_script_hash(script)
341}
342
343pub fn compute_plutus_v1_script_hash(script: &PlutusScript<1>) -> PolicyId {
344    let mut payload: Vec<u8> = Vec::from(script.as_ref());
345    payload.insert(0, 1);
346    pallas_crypto::hash::Hasher::<224>::hash(&payload)
347}
348
349pub fn compute_plutus_v2_script_hash(script: &PlutusScript<2>) -> PolicyId {
350    let mut payload: Vec<u8> = Vec::from(script.as_ref());
351    payload.insert(0, 2);
352    pallas_crypto::hash::Hasher::<224>::hash(&payload)
353}
354
355pub type CertificateIndex = u32;
356
357#[derive(PartialEq, Eq, Hash, Clone)]
358pub struct CertPointer {
359    pub slot: Slot,
360    pub tx_ix: TransactionIndex,
361    pub cert_ix: CertificateIndex,
362}
363
364pub type GenesisDelegation = HashMap<Genesishash, (GenesisDelegateHash, VrfKeyhash)>;
365pub type FutGenesisDelegation = HashMap<(Slot, Genesishash), (GenesisDelegateHash, VrfKeyhash)>;
366pub type InstantaneousRewards = (
367    HashMap<StakeCredential, Coin>,
368    HashMap<StakeCredential, Coin>,
369);
370
371#[derive(Default, Clone)] // for testing
372pub struct DState {
373    pub rewards: HashMap<StakeCredential, Coin>,
374    pub delegations: HashMap<StakeCredential, PoolKeyhash>,
375    pub ptrs: HashMap<CertPointer, StakeCredential>,
376    pub fut_gen_delegs: FutGenesisDelegation,
377    pub gen_delegs: GenesisDelegation,
378    pub inst_rewards: InstantaneousRewards,
379}
380
381// Essentially part of the `PoolRegistration` component of `Certificate` at
382// alonzo/src/model.rs
383#[derive(Clone, Debug)]
384pub struct PoolParam {
385    pub vrf_keyhash: VrfKeyhash,
386    pub pledge: Coin,
387    pub cost: Coin,
388    pub margin: UnitInterval,
389    pub reward_account: RewardAccount, // FIXME: Should be a `StakeCredential`, or `Hash<_>`???
390    pub pool_owners: Vec<AddrKeyhash>,
391    pub relays: Vec<Relay>,
392    pub pool_metadata: Nullable<PoolMetadata>,
393}
394
395#[derive(Default, Clone)] // for testing
396pub struct PState {
397    pub pool_params: HashMap<PoolKeyhash, PoolParam>,
398    pub fut_pool_params: HashMap<PoolKeyhash, PoolParam>,
399    pub retiring: HashMap<PoolKeyhash, Epoch>,
400}
401
402// Originally `DPState` in ShelleyMA specs, then updated to
403// `CertState` in Haskell sources at Intersect (#3369).
404#[non_exhaustive]
405#[derive(Default, Clone)] // for testing
406pub struct CertState {
407    pub pstate: PState,
408    pub dstate: DState,
409}