Skip to main content

amaru_kernel/cardano/
protocol_parameters.rs

1// Copyright 2025 PRAGMA
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::collections::BTreeMap;
16
17use pallas_math::math::{FixedDecimal, FixedPrecision};
18use serde::{Deserialize, Deserializer, Serialize, Serializer};
19
20use crate::{
21    CostModel, CostModels, DRepVotingThresholds, EraHistory, ExUnitPrices, ExUnits, Language, Lovelace, PoolId,
22    PoolVotingThresholds, ProtocolParamUpdate, ProtocolVersion, RationalNumber, Slot, cbor,
23};
24
25mod default;
26pub use default::*;
27
28/// Model from <https://github.com/IntersectMBO/formal-ledger-specifications/blob/master/src/Ledger/PParams.lagda>
29/// Some of the names have been adapted to improve readability.
30/// Also see <https://github.com/IntersectMBO/cardano-ledger/blob/d90eb4df4651970972d860e95f1a3697a3de8977/eras/conway/impl/cddl-files/conway.cddl#L324>
31#[derive(Clone, Debug, PartialEq, Eq)]
32pub struct ProtocolParameters {
33    // Outside of all groups.
34    pub protocol_version: ProtocolVersion,
35
36    // Network group
37    pub max_block_body_size: u64,
38    pub max_transaction_size: u64,
39    pub max_block_header_size: u16,
40    pub max_tx_ex_units: ExUnits,
41    pub max_block_ex_units: ExUnits,
42    pub max_value_size: u64,
43    pub max_collateral_inputs: u16,
44
45    // Economic group
46    pub min_fee_a: Lovelace,
47    pub min_fee_b: u64,
48    pub stake_credential_deposit: Lovelace,
49    pub stake_pool_deposit: Lovelace,
50    pub monetary_expansion_rate: RationalNumber,
51    pub treasury_expansion_rate: RationalNumber,
52    pub min_pool_cost: u64,
53    pub lovelace_per_utxo_byte: Lovelace,
54    pub prices: ExUnitPrices,
55    pub min_fee_ref_script_lovelace_per_byte: RationalNumber,
56    pub max_ref_script_size_per_tx: u32,
57    pub max_ref_script_size_per_block: u32,
58    pub ref_script_cost_stride: u32,
59    pub ref_script_cost_multiplier: RationalNumber,
60
61    // Technical group
62    pub stake_pool_max_retirement_epoch: u64,
63    pub optimal_stake_pools_count: u16,
64    pub pledge_influence: RationalNumber,
65    pub collateral_percentage: u16,
66    pub cost_models: CostModels,
67
68    // Governance group
69    pub pool_voting_thresholds: PoolVotingThresholds,
70    pub drep_voting_thresholds: DRepVotingThresholds,
71    pub min_committee_size: u16,
72    pub max_committee_term_length: u64,
73    pub gov_action_lifetime: u64,
74    pub gov_action_deposit: Lovelace,
75    pub drep_deposit: Lovelace,
76    pub drep_expiry: u64,
77}
78
79impl ProtocolParameters {
80    pub fn update(&mut self, u: ProtocolParamUpdate) {
81        #[inline]
82        fn set<T>(field: &mut T, opt: Option<T>) {
83            if let Some(val) = opt {
84                *field = val
85            }
86        }
87        set(&mut self.min_fee_a, u.minfee_a);
88        set(&mut self.min_fee_b, u.minfee_b);
89        set(&mut self.max_block_body_size, u.max_block_body_size);
90        set(&mut self.max_transaction_size, u.max_transaction_size);
91        set(
92            &mut self.max_block_header_size,
93            // FIXME: update in Pallas; should be a u16
94            u.max_block_header_size.map(|x| x as u16),
95        );
96        set(&mut self.stake_credential_deposit, u.key_deposit);
97        set(&mut self.stake_pool_deposit, u.pool_deposit);
98        set(&mut self.stake_pool_max_retirement_epoch, u.maximum_epoch);
99        set(
100            &mut self.optimal_stake_pools_count,
101            // FIXME: update in Pallas; should be a u16
102            u.desired_number_of_stake_pools.map(|x| x as u16),
103        );
104        set(&mut self.pledge_influence, u.pool_pledge_influence);
105        set(&mut self.treasury_expansion_rate, u.expansion_rate);
106        set(&mut self.monetary_expansion_rate, u.treasury_growth_rate);
107        set(&mut self.min_pool_cost, u.min_pool_cost);
108        set(&mut self.lovelace_per_utxo_byte, u.ada_per_utxo_byte);
109        if let Some(cost_models) = u.cost_models_for_script_languages {
110            // NOTE: This code may looks a little convoluted here, but it exists for the sake of
111            // generating a compiler error in due time. Should we not do that, and add a new language,
112            // it is highly likely that we may forget to apply the corresponding cost model update for
113            // that language.
114            //
115            // Now, we'll get the following pattern-match to fail due to non exhaustivness.
116            match Language::PlutusV1 {
117                Language::PlutusV1 => {
118                    if let Some(plutus_v1) = cost_models.plutus_v1 {
119                        self.cost_models.plutus_v1 = Some(plutus_v1);
120                    }
121                }
122                Language::PlutusV2 | Language::PlutusV3 => (),
123            }
124            if let Some(plutus_v2) = cost_models.plutus_v2 {
125                self.cost_models.plutus_v2 = Some(plutus_v2);
126            }
127            if let Some(plutus_v3) = cost_models.plutus_v3 {
128                self.cost_models.plutus_v3 = Some(plutus_v3);
129            }
130        }
131        set(&mut self.prices, u.execution_costs);
132        set(&mut self.max_tx_ex_units, u.max_tx_ex_units);
133        set(&mut self.max_block_ex_units, u.max_block_ex_units);
134        set(&mut self.max_value_size, u.max_value_size);
135        set(
136            &mut self.collateral_percentage,
137            // FIXME: update in Pallas; should be a u16
138            u.collateral_percentage.map(|x| x as u16),
139        );
140        set(
141            &mut self.max_collateral_inputs,
142            // FIXME: update in Pallas; should be a u16
143            u.max_collateral_inputs.map(|x| x as u16),
144        );
145        set(&mut self.pool_voting_thresholds, u.pool_voting_thresholds);
146        set(&mut self.drep_voting_thresholds, u.drep_voting_thresholds);
147        set(
148            &mut self.min_committee_size,
149            // FIXME: update in Pallas; should be a u16
150            u.min_committee_size.map(|x| x as u16),
151        );
152        set(&mut self.max_committee_term_length, u.committee_term_limit);
153        set(&mut self.gov_action_lifetime, u.governance_action_validity_period);
154        set(&mut self.gov_action_deposit, u.governance_action_deposit);
155        set(&mut self.drep_deposit, u.drep_deposit);
156        set(&mut self.drep_expiry, u.drep_inactivity_period);
157        set(&mut self.min_fee_ref_script_lovelace_per_byte, u.minfee_refscript_cost_per_byte);
158    }
159}
160
161fn decode_rationale(d: &mut cbor::Decoder<'_>) -> Result<RationalNumber, cbor::decode::Error> {
162    cbor::allow_tag(d, cbor::Tag::new(30))?;
163    cbor::heterogeneous_array(d, |d, assert_len| {
164        assert_len(2)?;
165        let numerator = d.u64()?;
166        let denominator = d.u64()?;
167        Ok(RationalNumber { numerator, denominator })
168    })
169}
170
171fn decode_protocol_version(d: &mut cbor::Decoder<'_>) -> Result<ProtocolVersion, cbor::decode::Error> {
172    cbor::heterogeneous_array(d, |d, assert_len| {
173        assert_len(2)?;
174        let major = d.u8()?;
175
176        // See: https://github.com/IntersectMBO/cardano-ledger/blob/693218df6cd90263da24e6c2118bac420ceea3a1/eras/conway/impl/cddl-files/conway.cddl#L126
177        if major > 12 {
178            return Err(cbor::decode::Error::message("invalid protocol version's major: too high"));
179        }
180        Ok((major as u64, d.u64()?))
181    })
182}
183
184impl<'b, C> cbor::decode::Decode<'b, C> for ProtocolParameters {
185    fn decode(d: &mut cbor::Decoder<'b>, ctx: &mut C) -> Result<Self, cbor::decode::Error> {
186        d.array()?;
187        let min_fee_a = d.u64()?;
188        let min_fee_b = d.u64()?;
189        let max_block_body_size = d.u64()?;
190        let max_transaction_size = d.u64()?;
191        let max_block_header_size = d.u16()?;
192        let stake_credential_deposit = d.u64()?;
193        let stake_pool_deposit = d.u64()?;
194        let stake_pool_max_retirement_epoch = d.u64()?;
195        let optimal_stake_pools_count = d.u16()?;
196        let pledge_influence = decode_rationale(d)?;
197        let monetary_expansion_rate = decode_rationale(d)?;
198        let treasury_expansion_rate = decode_rationale(d)?;
199        let protocol_version = decode_protocol_version(d)?;
200        let min_pool_cost = d.u64()?;
201        let lovelace_per_utxo_byte = d.u64()?;
202
203        let mut plutus_v1 = None;
204        let mut plutus_v2 = None;
205        let mut plutus_v3 = None;
206        let i = d.map_iter_with::<C, u8, CostModel>(ctx)?;
207        for item in i {
208            let (k, v) = item?;
209            match k {
210                0 => {
211                    plutus_v1 = Some(v);
212                }
213                1 => {
214                    plutus_v2 = Some(v);
215                }
216                2 => {
217                    plutus_v3 = Some(v);
218                }
219                _ => unreachable!("unexpected language version: {k}"),
220            }
221        }
222        let prices = d.decode_with(ctx)?;
223        let max_tx_ex_units = d.decode_with(ctx)?;
224        let max_block_ex_units = d.decode_with(ctx)?;
225        let max_value_size = d.u64()?;
226        let collateral_percentage = d.u16()?;
227        let max_collateral_inputs = d.u16()?;
228        let pool_voting_thresholds = d.decode_with(ctx)?;
229        let drep_voting_thresholds = d.decode_with(ctx)?;
230        let min_committee_size = d.u16()?;
231        let max_committee_term_length = d.u64()?;
232        let gov_action_lifetime = d.u64()?;
233        let gov_action_deposit = d.u64()?;
234        let drep_deposit = d.u64()?;
235        let drep_expiry = d.decode_with(ctx)?;
236        let min_fee_ref_script_lovelace_per_byte = decode_rationale(d)?;
237
238        Ok(ProtocolParameters {
239            protocol_version,
240            min_fee_a,
241            min_fee_b,
242            max_block_body_size,
243            max_transaction_size,
244            max_block_header_size,
245            stake_credential_deposit,
246            stake_pool_deposit,
247            stake_pool_max_retirement_epoch,
248            optimal_stake_pools_count,
249            pledge_influence,
250            monetary_expansion_rate,
251            treasury_expansion_rate,
252            min_pool_cost,
253            lovelace_per_utxo_byte,
254            cost_models: CostModels { plutus_v1, plutus_v2, plutus_v3 },
255            prices,
256            max_tx_ex_units,
257            max_block_ex_units,
258            max_value_size,
259            collateral_percentage,
260            max_collateral_inputs,
261            pool_voting_thresholds,
262            drep_voting_thresholds,
263            min_committee_size,
264            max_committee_term_length,
265            gov_action_lifetime,
266            gov_action_deposit,
267            drep_deposit,
268            drep_expiry,
269            min_fee_ref_script_lovelace_per_byte,
270            max_ref_script_size_per_tx: 200 * 1024, //Hardcoded in the haskell ledger (https://github.com/IntersectMBO/cardano-ledger/blob/3fe73a26588876bbf033bf4c4d25c97c2d8564dd/eras/conway/impl/src/Cardano/Ledger/Conway/Rules/Ledger.hs#L154)
271            max_ref_script_size_per_block: 1024 * 1024, // Hardcoded in the haskell ledger (https://github.com/IntersectMBO/cardano-ledger/blob/3fe73a26588876bbf033bf4c4d25c97c2d8564dd/eras/conway/impl/src/Cardano/Ledger/Conway/Rules/Bbody.hs#L91)
272            ref_script_cost_stride: 25600, // Hardcoded in the haskell ledger (https://github.com/IntersectMBO/cardano-ledger/blob/3fe73a26588876bbf033bf4c4d25c97c2d8564dd/eras/conway/impl/src/Cardano/Ledger/Conway/Tx.hs#L82)
273            ref_script_cost_multiplier: RationalNumber { numerator: 12, denominator: 10 }, // Hardcoded in the haskell ledger (https://github.com/IntersectMBO/cardano-ledger/blob/3fe73a26588876bbf033bf4c4d25c97c2d8564dd/eras/conway/impl/src/Cardano/Ledger/Conway/Tx.hs#L85)
274        })
275    }
276}
277
278fn encode_rationale<W: cbor::encode::Write>(
279    e: &mut cbor::Encoder<W>,
280    rat: &RationalNumber,
281) -> Result<(), cbor::encode::Error<W::Error>> {
282    e.tag(cbor::Tag::new(30))?;
283    e.array(2)?;
284
285    e.u64(rat.numerator)?;
286    e.u64(rat.denominator)?;
287    Ok(())
288}
289
290fn encode_protocol_version<W: cbor::encode::Write>(
291    e: &mut cbor::Encoder<W>,
292    v: &ProtocolVersion,
293) -> Result<(), cbor::encode::Error<W::Error>> {
294    e.array(2)?;
295    e.u64(v.0)?;
296    e.u64(v.1)?;
297    Ok(())
298}
299
300impl<C> cbor::encode::Encode<C> for ProtocolParameters {
301    fn encode<W: cbor::encode::Write>(
302        &self,
303        e: &mut cbor::Encoder<W>,
304        ctx: &mut C,
305    ) -> Result<(), cbor::encode::Error<W::Error>> {
306        e.array(31)?;
307        e.u64(self.min_fee_a)?;
308        e.u64(self.min_fee_b)?;
309        e.u64(self.max_block_body_size)?;
310        e.u64(self.max_transaction_size)?;
311        e.u16(self.max_block_header_size)?;
312        e.u64(self.stake_credential_deposit)?;
313        e.u64(self.stake_pool_deposit)?;
314        e.u64(self.stake_pool_max_retirement_epoch)?;
315        e.u16(self.optimal_stake_pools_count)?;
316        encode_rationale(e, &self.pledge_influence)?;
317        encode_rationale(e, &self.monetary_expansion_rate)?;
318        encode_rationale(e, &self.treasury_expansion_rate)?;
319        encode_protocol_version(e, &self.protocol_version)?;
320        e.u64(self.min_pool_cost)?;
321        e.u64(self.lovelace_per_utxo_byte)?;
322
323        let mut count = 0;
324        if self.cost_models.plutus_v1.is_some() {
325            count += 1;
326        }
327        if self.cost_models.plutus_v2.is_some() {
328            count += 1;
329        }
330        if self.cost_models.plutus_v3.is_some() {
331            count += 1;
332        }
333        e.map(count)?;
334        if let Some(v) = self.cost_models.plutus_v1.as_ref() {
335            e.u8(0)?;
336            e.encode_with(v, ctx)?;
337        }
338        if let Some(v) = self.cost_models.plutus_v2.as_ref() {
339            e.u8(1)?;
340            e.encode_with(v, ctx)?;
341        }
342        if let Some(v) = self.cost_models.plutus_v3.as_ref() {
343            e.u8(2)?;
344            e.encode_with(v, ctx)?;
345        }
346
347        e.encode_with(&self.prices, ctx)?;
348        e.encode_with(self.max_tx_ex_units, ctx)?;
349        e.encode_with(self.max_block_ex_units, ctx)?;
350
351        e.u64(self.max_value_size)?;
352        e.u16(self.collateral_percentage)?;
353        e.u16(self.max_collateral_inputs)?;
354
355        e.encode_with(&self.pool_voting_thresholds, ctx)?;
356        e.encode_with(&self.drep_voting_thresholds, ctx)?;
357
358        e.u16(self.min_committee_size)?;
359        e.u64(self.max_committee_term_length)?;
360        e.u64(self.gov_action_lifetime)?;
361        e.u64(self.gov_action_deposit)?;
362        e.u64(self.drep_deposit)?;
363        e.encode_with(self.drep_expiry, ctx)?;
364        encode_rationale(e, &self.min_fee_ref_script_lovelace_per_byte)?;
365
366        Ok(())
367    }
368}
369
370#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
371pub struct GlobalParameters {
372    /// The maximum depth of a rollback, also known as the security parameter 'k'.
373    /// This translates down to the length of our volatile storage, containing states of the ledger
374    /// which aren't yet considered final.
375    pub consensus_security_param: usize,
376
377    /// Multiplier applied to the CONSENSUS_SECURITY_PARAM to determine the epoch length.
378    pub epoch_length_scale_factor: usize,
379
380    /// Inverse of the active slot coefficient (i.e. 1/f);
381    pub active_slot_coeff_inverse: usize,
382
383    /// Maximum supply of Ada, in lovelace (1 Ada = 1,000,000 Lovelace)
384    pub max_lovelace_supply: Lovelace,
385
386    /// Number of slots for a single KES validity period.
387    pub slots_per_kes_period: u64,
388
389    /// Maximum number of KES key evolution. Combined with SLOTS_PER_KES_PERIOD, these values
390    /// indicates the validity period of a KES key before a new one is required.
391    pub max_kes_evolution: u8,
392
393    /// Number of slots in an epoch
394    pub epoch_length: usize,
395
396    /// Relative slot from which data of the previous epoch can be considered stable.
397    pub stability_window: Slot,
398
399    /// Number of slots at the end of each epoch which do NOT contribute randomness to the candidate
400    /// nonce of the following epoch.
401    pub randomness_stabilization_window: u64,
402
403    /// POSIX time (milliseconds) of the System Start.
404    pub system_start: u64,
405}
406
407/// This data type encapsulates the parameters needed by the consensus layer to operate.
408#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
409pub struct ConsensusParameters {
410    randomness_stabilization_window: u64,
411    slots_per_kes_period: u64,
412    max_kes_evolution: u64,
413    active_slot_coeff: SerializedFixedDecimal,
414    era_history: EraHistory,
415    ocert_counters: BTreeMap<PoolId, u64>,
416}
417
418#[derive(Clone, Debug, PartialEq)]
419struct SerializedFixedDecimal(FixedDecimal);
420
421impl Serialize for SerializedFixedDecimal {
422    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
423    where
424        S: Serializer,
425    {
426        serializer.serialize_str(&self.0.to_string())
427    }
428}
429
430impl<'a> Deserialize<'a> for SerializedFixedDecimal {
431    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
432    where
433        D: Deserializer<'a>,
434    {
435        let s = String::deserialize(deserializer)?;
436        FixedDecimal::from_str(&s, s.len() as u64).map(SerializedFixedDecimal).map_err(serde::de::Error::custom)
437    }
438}
439
440impl ConsensusParameters {
441    /// Create new consensus parameters from the given global parameters.
442    pub fn new(
443        global_parameters: GlobalParameters,
444        era_history: &EraHistory,
445        ocert_counters: BTreeMap<PoolId, u64>,
446    ) -> Self {
447        Self::create(
448            global_parameters.randomness_stabilization_window,
449            global_parameters.slots_per_kes_period,
450            global_parameters.max_kes_evolution as u64,
451            1f64 / global_parameters.active_slot_coeff_inverse as f64,
452            era_history,
453            ocert_counters,
454        )
455    }
456
457    /// Create new consensus parameters from individual values.
458    pub fn create(
459        randomness_stabilization_window: u64,
460        slots_per_kes_period: u64,
461        max_kes_evolution: u64,
462        active_slot_coeff: f64,
463        era_history: &EraHistory,
464        ocert_counters: BTreeMap<PoolId, u64>,
465    ) -> ConsensusParameters {
466        let active_slot_coeff = FixedDecimal::from((active_slot_coeff * 100.0) as u64) / FixedDecimal::from(100u64);
467        Self {
468            randomness_stabilization_window,
469            slots_per_kes_period,
470            max_kes_evolution,
471            active_slot_coeff: SerializedFixedDecimal(active_slot_coeff),
472            era_history: era_history.clone(),
473            ocert_counters,
474        }
475    }
476
477    pub fn era_history(&self) -> &EraHistory {
478        &self.era_history
479    }
480
481    pub fn randomness_stabilization_window(&self) -> u64 {
482        self.randomness_stabilization_window
483    }
484
485    pub fn slot_to_kes_period(&self, slot: Slot) -> u64 {
486        u64::from(slot) / self.slots_per_kes_period
487    }
488
489    pub fn max_kes_evolutions(&self) -> u64 {
490        self.max_kes_evolution
491    }
492
493    pub fn latest_opcert_sequence_number(&self, pool_id: &PoolId) -> Option<u64> {
494        self.ocert_counters.get(pool_id).copied()
495    }
496
497    pub fn active_slot_coeff(&self) -> FixedDecimal {
498        self.active_slot_coeff.0.clone()
499    }
500}
501
502#[cfg(any(test, feature = "test-utils"))]
503pub use tests::*;
504
505#[cfg(any(test, feature = "test-utils"))]
506mod tests {
507    use proptest::{collection, option, prelude::*};
508
509    use super::PREPROD_INITIAL_PROTOCOL_PARAMETERS;
510    #[cfg(not(target_os = "windows"))]
511    use crate::prop_cbor_roundtrip;
512    use crate::{
513        CostModel, CostModels, DRepVotingThresholds, ExUnitPrices, ExUnits, GovernanceAction, Hash, KeyValuePairs,
514        Lovelace, Nullable, PoolVotingThresholds, ProposalId, ProtocolParamUpdate, ProtocolParameters, ProtocolVersion,
515        RewardAccount, Set, StakeCredential, any_constitution, any_hash28, any_nullable, any_proposal_id,
516        any_rational_number, any_reward_account, any_stake_credential, size::SCRIPT,
517    };
518
519    #[cfg(not(target_os = "windows"))]
520    prop_cbor_roundtrip!(ProtocolParameters, any_protocol_parameter());
521
522    prop_compose! {
523        pub fn any_ex_units()(
524            mem in any::<u64>(),
525            steps in any::<u64>(),
526        ) -> ExUnits {
527            ExUnits {
528                mem,
529                steps,
530            }
531        }
532    }
533
534    prop_compose! {
535        pub fn any_ex_units_prices()(
536            mem_price in any_rational_number(),
537            step_price in any_rational_number(),
538        ) -> ExUnitPrices {
539            ExUnitPrices {
540                mem_price,
541                step_price,
542            }
543        }
544    }
545
546    prop_compose! {
547        pub fn any_protocol_version()(
548            major in any::<u8>(),
549            minor in any::<u64>(),
550        ) -> ProtocolVersion {
551            ((major % 13) as u64, minor)
552        }
553    }
554
555    prop_compose! {
556        pub fn any_drep_voting_thresholds()(
557            motion_no_confidence in any_rational_number(),
558            committee_normal in any_rational_number(),
559            committee_no_confidence in any_rational_number(),
560            update_constitution in any_rational_number(),
561            hard_fork_initiation in any_rational_number(),
562            pp_network_group in any_rational_number(),
563            pp_economic_group in any_rational_number(),
564            pp_technical_group in any_rational_number(),
565            pp_governance_group in any_rational_number(),
566            treasury_withdrawal in any_rational_number(),
567        ) -> DRepVotingThresholds {
568            DRepVotingThresholds {
569                motion_no_confidence,
570                committee_normal,
571                committee_no_confidence,
572                update_constitution,
573                hard_fork_initiation,
574                pp_network_group,
575                pp_economic_group,
576                pp_technical_group,
577                pp_governance_group,
578                treasury_withdrawal,
579            }
580        }
581    }
582
583    prop_compose! {
584        pub fn any_pool_voting_thresholds()(
585            motion_no_confidence in any_rational_number(),
586            committee_normal in any_rational_number(),
587            committee_no_confidence in any_rational_number(),
588            hard_fork_initiation in any_rational_number(),
589            security_voting_threshold in any_rational_number(),
590        ) -> PoolVotingThresholds {
591            PoolVotingThresholds {
592                motion_no_confidence,
593                committee_normal,
594                committee_no_confidence,
595                hard_fork_initiation,
596                security_voting_threshold,
597            }
598        }
599    }
600
601    prop_compose! {
602        pub fn any_cost_model()(
603            machine_cost in option::of(any::<i64>()),
604            some_builtin in option::of(any::<i64>()),
605            some_other_builtin in option::of(any::<i64>()),
606        ) -> CostModel {
607            vec![
608                machine_cost,
609                some_builtin,
610                some_other_builtin,
611            ]
612            .into_iter()
613            .flatten()
614            .collect()
615        }
616    }
617
618    prop_compose! {
619        pub fn any_cost_models()(
620            plutus_v1 in option::of(any_cost_model()),
621            plutus_v2 in option::of(any_cost_model()),
622            plutus_v3 in option::of(any_cost_model()),
623        ) -> CostModels {
624            CostModels {
625                plutus_v1,
626                plutus_v2,
627                plutus_v3,
628            }
629        }
630    }
631
632    prop_compose! {
633        pub fn any_ex_unit_prices()(
634            mem_price in any_rational_number(),
635            step_price in any_rational_number(),
636        ) -> ExUnitPrices {
637            ExUnitPrices {
638                mem_price,
639                step_price,
640            }
641        }
642    }
643
644    prop_compose! {
645        pub fn any_protocol_params_update()(
646            minfee_a in option::of(any::<u64>()),
647            minfee_b in option::of(any::<u64>()),
648            max_block_body_size in option::of(any::<u64>()),
649            max_transaction_size in option::of(any::<u64>()),
650            max_block_header_size in option::of(any::<u64>()),
651            key_deposit in option::of(any::<Lovelace>()),
652            pool_deposit in option::of(any::<Lovelace>()),
653            maximum_epoch in option::of(any::<u64>()),
654            desired_number_of_stake_pools in option::of(any::<u64>()),
655            pool_pledge_influence in option::of(any_rational_number()),
656            expansion_rate in option::of(any_rational_number()),
657            treasury_growth_rate in option::of(any_rational_number()),
658            min_pool_cost in option::of(any::<Lovelace>()),
659            ada_per_utxo_byte in option::of(any::<Lovelace>()),
660            cost_models_for_script_languages in option::of(any_cost_models()),
661            execution_costs in option::of(any_ex_unit_prices()),
662            max_tx_ex_units in option::of(any_ex_units()),
663            max_block_ex_units in option::of(any_ex_units()),
664            max_value_size in option::of(any::<u64>()),
665            collateral_percentage in option::of(any::<u64>()),
666            max_collateral_inputs in option::of(any::<u64>()),
667            pool_voting_thresholds in option::of(any_pool_voting_thresholds()),
668            drep_voting_thresholds in option::of(any_drep_voting_thresholds()),
669            min_committee_size in option::of(any::<u64>()),
670            committee_term_limit in option::of(any::<u64>()),
671            governance_action_validity_period in option::of(any::<u64>()),
672            governance_action_deposit in option::of(any::<Lovelace>()),
673            drep_deposit in option::of(any::<Lovelace>()),
674            drep_inactivity_period in option::of(any::<u64>()),
675            minfee_refscript_cost_per_byte in option::of(any_rational_number()),
676        ) -> ProtocolParamUpdate {
677            ProtocolParamUpdate {
678                minfee_a,
679                minfee_b,
680                max_block_body_size,
681                max_transaction_size,
682                max_block_header_size,
683                key_deposit,
684                pool_deposit,
685                maximum_epoch,
686                desired_number_of_stake_pools,
687                pool_pledge_influence,
688                expansion_rate,
689                treasury_growth_rate,
690                min_pool_cost,
691                ada_per_utxo_byte,
692                cost_models_for_script_languages,
693                execution_costs,
694                max_tx_ex_units,
695                max_block_ex_units,
696                max_value_size,
697                collateral_percentage,
698                max_collateral_inputs,
699                pool_voting_thresholds,
700                drep_voting_thresholds,
701                min_committee_size,
702                committee_term_limit,
703                governance_action_validity_period,
704                governance_action_deposit,
705                drep_deposit,
706                drep_inactivity_period,
707                minfee_refscript_cost_per_byte,
708            }
709        }
710    }
711
712    pub fn any_gov_action() -> impl Strategy<Value = GovernanceAction> {
713        prop_compose! {
714            fn any_parent_proposal_id()(
715                proposal_id in option::of(any_proposal_id()),
716            ) -> Nullable<ProposalId> {
717                Nullable::from(proposal_id)
718            }
719        }
720
721        prop_compose! {
722            fn any_action_parameter_change()(
723                parent_proposal_id in any_parent_proposal_id(),
724                pparams in any_protocol_params_update(),
725                guardrails in any_guardrails_script(),
726            ) -> GovernanceAction {
727                GovernanceAction::ParameterChange(parent_proposal_id, Box::new(pparams), guardrails)
728            }
729        }
730
731        prop_compose! {
732            fn any_hardfork_initiation()(
733                parent_proposal_id in any_parent_proposal_id(),
734                protocol_version in any_protocol_version(),
735            ) -> GovernanceAction {
736                GovernanceAction::HardForkInitiation(parent_proposal_id, protocol_version)
737            }
738        }
739
740        prop_compose! {
741            #[allow(clippy::unwrap_used)]
742            fn any_treasury_withdrawals()(
743                withdrawals in collection::vec(any_withdrawal(), 0..3),
744                guardrails in any_guardrails_script(),
745            ) -> GovernanceAction {
746                GovernanceAction::TreasuryWithdrawals(
747                    KeyValuePairs::try_from(withdrawals).unwrap().as_pallas(),
748                    guardrails
749                )
750            }
751        }
752
753        prop_compose! {
754            fn any_no_confidence()(
755                parent_proposal_id in any_parent_proposal_id(),
756            ) -> GovernanceAction {
757                GovernanceAction::NoConfidence(parent_proposal_id)
758            }
759        }
760
761        prop_compose! {
762            fn any_committee_registration()(
763                credential in any_stake_credential(),
764                epoch in any::<u64>(),
765            ) -> (StakeCredential, u64) {
766                (credential, epoch)
767            }
768        }
769
770        prop_compose! {
771            #[allow(clippy::unwrap_used)]
772            fn any_committee_update()(
773                parent_proposal_id in any_parent_proposal_id(),
774                to_remove in collection::btree_set(any_stake_credential(), 0..3),
775                to_add in collection::vec(any_committee_registration(), 0..3),
776                quorum in any_rational_number(),
777            ) -> GovernanceAction {
778                GovernanceAction::UpdateCommittee(
779                    parent_proposal_id,
780                    Set::from(to_remove.into_iter().collect::<Vec<_>>()),
781                    KeyValuePairs::try_from(to_add).unwrap().as_pallas(),
782                    quorum
783                )
784            }
785        }
786
787        prop_compose! {
788            fn any_new_constitution()(
789                parent_proposal_id in any_parent_proposal_id(),
790                constitution in any_constitution(),
791            ) -> GovernanceAction {
792                GovernanceAction::NewConstitution(parent_proposal_id, constitution)
793            }
794        }
795
796        fn any_nice_poll() -> impl Strategy<Value = GovernanceAction> {
797            prop::strategy::Just(GovernanceAction::Information)
798        }
799
800        prop_oneof![
801            any_action_parameter_change(),
802            any_hardfork_initiation(),
803            any_treasury_withdrawals(),
804            any_no_confidence(),
805            any_committee_update(),
806            any_new_constitution(),
807            any_nice_poll(),
808        ]
809    }
810
811    prop_compose! {
812        pub fn any_withdrawal()(
813            reward_account in any_reward_account(),
814            amount in any::<Lovelace>(),
815        ) -> (RewardAccount, Lovelace) {
816            (reward_account, amount)
817        }
818    }
819
820    pub fn any_guardrails_script() -> impl Strategy<Value = Nullable<Hash<SCRIPT>>> {
821        any_nullable(any_hash28())
822    }
823
824    prop_compose! {
825        pub fn any_protocol_parameter()(
826            protocol_version in any_protocol_version(),
827            max_block_body_size in any::<u64>(),
828            max_transaction_size in any::<u64>(),
829            max_block_header_size in any::<u16>(),
830            max_tx_ex_units in any_ex_units(),
831            max_block_ex_units in any_ex_units(),
832            max_value_size in any::<u64>(),
833            max_collateral_inputs in any::<u16>(),
834            min_fee_a in any::<Lovelace>(),
835            min_fee_b in any::<Lovelace>(),
836            stake_credential_deposit in any::<Lovelace>(),
837            stake_pool_deposit in any::<Lovelace>(),
838            monetary_expansion_rate in any_rational_number(),
839            treasury_expansion_rate in any_rational_number(),
840            min_pool_cost in any::<Lovelace>(),
841            lovelace_per_utxo_byte in any::<Lovelace>(),
842            prices in any_ex_units_prices(),
843            min_fee_ref_script_lovelace_per_byte in any_rational_number(),
844            stake_pool_max_retirement_epoch in any::<u64>(),
845            optimal_stake_pools_count in any::<u16>(),
846            pledge_influence in any_rational_number(),
847            collateral_percentage in any::<u16>(),
848            cost_models in any_cost_models(),
849            pool_voting_thresholds in any_pool_voting_thresholds(),
850            drep_voting_thresholds in any_drep_voting_thresholds(),
851            min_committee_size in any::<u16>(),
852            max_committee_term_length in any::<u64>(),
853            gov_action_lifetime in any::<u64>(),
854            gov_action_deposit in any::<Lovelace>(),
855            drep_deposit in any::<Lovelace>(),
856            drep_expiry in any::<u64>(),
857        ) -> ProtocolParameters {
858        let default = &*PREPROD_INITIAL_PROTOCOL_PARAMETERS;
859        ProtocolParameters {
860            protocol_version,
861            max_block_body_size,
862            max_transaction_size,
863            max_block_header_size,
864            max_tx_ex_units,
865            max_block_ex_units,
866            max_value_size,
867            max_collateral_inputs,
868            min_fee_a,
869            min_fee_b,
870            stake_credential_deposit,
871            stake_pool_deposit,
872            monetary_expansion_rate,
873            treasury_expansion_rate,
874            min_pool_cost,
875            lovelace_per_utxo_byte,
876            prices,
877            min_fee_ref_script_lovelace_per_byte,
878            max_ref_script_size_per_tx: default.max_ref_script_size_per_tx,
879            max_ref_script_size_per_block: default.max_ref_script_size_per_block,
880            ref_script_cost_stride: default.ref_script_cost_stride,
881            ref_script_cost_multiplier: default.ref_script_cost_multiplier.clone(),
882            stake_pool_max_retirement_epoch,
883            optimal_stake_pools_count,
884            pledge_influence,
885            collateral_percentage,
886            cost_models,
887            pool_voting_thresholds,
888            drep_voting_thresholds,
889            min_committee_size,
890            max_committee_term_length,
891            gov_action_lifetime,
892            gov_action_deposit,
893            drep_deposit,
894            drep_expiry,
895            }
896        }
897    }
898}