plutus_ledger_api/generators/correct/
v1.rs

1//! Proptest strategies for Plutus V1 types
2//!
3//! These strategies always return valid values.
4use crate::feature_traits::FeatureTraits;
5use crate::generators::correct::primitive::{arb_bool, arb_bytes, arb_integer, arb_natural};
6use crate::plutus_data::PlutusData;
7use crate::v1::address::{
8    Address, CertificateIndex, ChainPointer, Credential, Slot, StakingCredential, TransactionIndex,
9};
10use crate::v1::assoc_map::AssocMap;
11use crate::v1::crypto::{Ed25519PubKeyHash, LedgerBytes, PaymentPubKeyHash, StakePubKeyHash};
12use crate::v1::datum::{Datum, DatumHash};
13use crate::v1::interval::{Extended, Interval, LowerBound, PlutusInterval, UpperBound};
14use crate::v1::redeemer::{Redeemer, RedeemerHash};
15use crate::v1::script::{MintingPolicyHash, ScriptHash, ValidatorHash};
16use crate::v1::transaction::{
17    DCert, POSIXTime, ScriptContext, ScriptPurpose, TransactionHash, TransactionInfo,
18    TransactionInput, TransactionOutput, TxInInfo,
19};
20use crate::v1::value::Lovelace;
21use crate::v2::value::{AssetClass, CurrencySymbol, TokenName, Value};
22use num_bigint::BigInt;
23use proptest::collection::btree_map;
24use proptest::collection::vec;
25use proptest::option;
26use proptest::prelude::{any, prop_oneof, Just};
27use proptest::strategy::Strategy;
28use std::collections::BTreeMap;
29
30/// Strategy to generate an arbitrary bytestring with a fixed length
31pub fn arb_ledger_bytes(length: usize) -> impl Strategy<Value = LedgerBytes> {
32    (vec(any::<u8>(), length)).prop_map(LedgerBytes)
33}
34
35/// Strategy to generate an asset class
36///
37/// This generator will only generate valid asset classes, the Ada token name will always be empty
38pub fn arb_asset_class() -> impl Strategy<Value = AssetClass> {
39    (arb_currency_symbol(), arb_token_name()).prop_map(|(currency_symbol, token_name)| {
40        let token_name = match currency_symbol {
41            CurrencySymbol::Ada => TokenName::ada(),
42            CurrencySymbol::NativeToken(_) => token_name,
43        };
44        AssetClass {
45            currency_symbol,
46            token_name,
47        }
48    })
49}
50
51/// Strategy to generate a currency symbol
52///
53/// In order to avoid generating too much Ada symbols, this generator is configured such that it
54/// only has 25% chance of getting Ada, and 75% of getting a native token
55pub fn arb_currency_symbol() -> impl Strategy<Value = CurrencySymbol> {
56    prop_oneof![
57        1 =>Just(CurrencySymbol::Ada),
58        3 =>arb_minting_policy_hash().prop_map(CurrencySymbol::NativeToken)
59    ]
60}
61
62/// Strategy to generate a token name
63pub fn arb_token_name() -> impl Strategy<Value = TokenName> {
64    arb_ledger_bytes(32).prop_map(TokenName)
65}
66
67/// Strategy to generate a minting policy hash
68pub fn arb_minting_policy_hash() -> impl Strategy<Value = MintingPolicyHash> {
69    arb_script_hash().prop_map(MintingPolicyHash)
70}
71
72/// Strategy to generate a validator hash
73pub fn arb_validator_hash() -> impl Strategy<Value = ValidatorHash> {
74    arb_script_hash().prop_map(ValidatorHash)
75}
76
77/// Strategy to generate a ScriptHash
78pub fn arb_script_hash() -> impl Strategy<Value = ScriptHash> {
79    arb_ledger_bytes(28).prop_map(ScriptHash)
80}
81
82/// Strategy to generate a Value
83///
84/// This generator will try to balance the result, such that there's a 50% chance that Ada is
85/// included in the Value
86pub fn arb_value() -> impl Strategy<Value = Value> {
87    prop_oneof![
88        arb_native_tokens(),
89        (arb_native_tokens(), arb_natural(2)).prop_map(|(Value(outer_dict), amount)| {
90            let mut outer_dict = outer_dict.clone();
91            let inner_dict = BTreeMap::from([(TokenName::ada(), amount)]);
92            outer_dict.insert(CurrencySymbol::Ada, inner_dict);
93            Value(outer_dict)
94        })
95    ]
96}
97
98/// Strategy to generate a Value
99pub fn arb_native_tokens() -> impl Strategy<Value = Value> {
100    btree_map(
101        arb_minting_policy_hash().prop_map(CurrencySymbol::NativeToken),
102        btree_map(arb_token_name(), arb_natural(1), 5),
103        5,
104    )
105    .prop_map(Value)
106}
107
108/// Strategy to generate an arbitrary PlutusData with a maximum depth of 5 recursions
109pub fn arb_plutus_data() -> impl Strategy<Value = PlutusData> {
110    arb_plutus_data_leaf().prop_recursive(5, 64, 16, |arb_data| {
111        prop_oneof![
112            arb_integer().prop_map(PlutusData::Integer),
113            arb_bytes().prop_map(PlutusData::Bytes),
114            vec(arb_data.clone(), 5).prop_map(PlutusData::List),
115            vec((arb_data.clone(), arb_data.clone()), 5).prop_map(PlutusData::Map),
116            (arb_natural(1), vec(arb_data.clone(), 5))
117                .prop_map(|(id, fields)| PlutusData::Constr(id, fields)),
118        ]
119    })
120}
121
122/// Leaf generator for PlutusData recursive generator
123fn arb_plutus_data_leaf() -> impl Strategy<Value = PlutusData> {
124    prop_oneof![
125        arb_integer().prop_map(PlutusData::Integer),
126        arb_bytes().prop_map(PlutusData::Bytes),
127    ]
128}
129
130/// Strategy to generate Ed25519 public key hash
131pub fn arb_ed25519_pub_key_hash() -> impl Strategy<Value = Ed25519PubKeyHash> {
132    arb_ledger_bytes(28).prop_map(Ed25519PubKeyHash)
133}
134
135/// Strategy to generate a Datum hash
136pub fn arb_datum_hash() -> impl Strategy<Value = DatumHash> {
137    arb_ledger_bytes(32).prop_map(DatumHash)
138}
139
140/// Strategy to generate a Datum
141pub fn arb_datum() -> impl Strategy<Value = Datum> {
142    arb_plutus_data().prop_map(Datum)
143}
144
145/// Strategy to generate a Redeemer
146pub fn arb_redeemer() -> impl Strategy<Value = Redeemer> {
147    arb_plutus_data().prop_map(Redeemer)
148}
149
150/// Strategy to generate a Datum hash
151pub fn arb_redeemer_hash() -> impl Strategy<Value = RedeemerHash> {
152    arb_ledger_bytes(32).prop_map(RedeemerHash)
153}
154
155/// Strategy to generate an Extended set
156pub fn arb_extended<T>(element: T) -> impl Strategy<Value = Extended<T::Value>>
157where
158    T: Strategy,
159    T::Value: FeatureTraits + Clone,
160{
161    prop_oneof![
162        Just(Extended::NegInf),
163        Just(Extended::PosInf),
164        element.prop_map(Extended::Finite)
165    ]
166}
167
168/// Strategy to generate an extended POSIX time
169pub fn arb_extended_posix_time() -> impl Strategy<Value = Extended<POSIXTime>> {
170    arb_extended(arb_posix_time())
171}
172
173/// Strategy to generate a POSIX Time
174pub fn arb_posix_time() -> impl Strategy<Value = POSIXTime> {
175    (0..2000000000).prop_map(|int| POSIXTime(BigInt::from(int)))
176}
177
178/// Strategy to generate an UpperBound
179pub fn arb_upper_bound<T>(element: T) -> impl Strategy<Value = UpperBound<T::Value>>
180where
181    T: Strategy,
182    T::Value: FeatureTraits + Clone,
183{
184    (arb_extended(element), arb_bool()).prop_map(|(bound, closed)| UpperBound { bound, closed })
185}
186
187/// Strategy to generate a LowerBound
188pub fn arb_lower_bound<T>(element: T) -> impl Strategy<Value = LowerBound<T::Value>>
189where
190    T: Strategy,
191    T::Value: FeatureTraits + Clone,
192{
193    (arb_extended(element), arb_bool()).prop_map(|(bound, closed)| LowerBound { bound, closed })
194}
195
196/// Strategy to generate a Interval
197pub fn arb_interval<T>(lower_bound: T, upper_bound: T) -> impl Strategy<Value = Interval<T::Value>>
198where
199    T: Strategy,
200    T::Value: FeatureTraits + Clone,
201{
202    (lower_bound, upper_bound).prop_flat_map(|(lb, ub)| {
203        prop_oneof![
204            4 => Just(Interval::Finite(lb.clone(), ub.clone())),
205            4 => Just(Interval::StartAt(lb.clone())),
206            4 => Just(Interval::StartAfter(lb)),
207            4 => Just(Interval::EndAt(ub.clone())),
208            4 => Just(Interval::EndBefore(ub)),
209            1 => Just(Interval::Always),
210            1 => Just(Interval::Never)
211        ]
212    })
213}
214
215/// Strategy to generate a PlutusInterval
216///
217/// This implementation is not normalized, so impossible values might be generated
218pub fn arb_plutus_interval<T>(
219    lower_bound: T,
220    upper_bound: T,
221) -> impl Strategy<Value = PlutusInterval<T::Value>>
222where
223    T: Strategy,
224    T::Value: FeatureTraits + Clone,
225{
226    (arb_lower_bound(lower_bound), arb_upper_bound(upper_bound))
227        .prop_map(|(from, to)| PlutusInterval { from, to })
228}
229
230/// Strategy to generate a PlutusInterval
231///
232/// This implementation is not normalized, so impossible values might be generated
233pub fn arb_plutus_interval_posix_time() -> impl Strategy<Value = PlutusInterval<POSIXTime>> {
234    arb_plutus_interval(arb_posix_time(), arb_posix_time())
235}
236
237/// Strategy to generate a Interval
238pub fn arb_interval_posix_time() -> impl Strategy<Value = Interval<POSIXTime>> {
239    (arb_posix_time(), arb_posix_time()).prop_flat_map(|(x, y)| {
240        if x > y {
241            arb_interval(Just(y), Just(x))
242        } else {
243            arb_interval(Just(x), Just(y))
244        }
245    })
246}
247
248/// Strategy to generate a Cardano address
249pub fn arb_address() -> impl Strategy<Value = Address> {
250    (arb_credential(), option::of(arb_staking_credential())).prop_map(
251        |(credential, staking_credential)| Address {
252            credential,
253            staking_credential,
254        },
255    )
256}
257
258/// Strategy to generate a chain pointer
259pub fn arb_chain_pointer() -> impl Strategy<Value = ChainPointer> {
260    (arb_slot(), arb_transaction_index(), arb_certificate_index()).prop_map(
261        |(slot_number, transaction_index, certificate_index)| ChainPointer {
262            slot_number,
263            transaction_index,
264            certificate_index,
265        },
266    )
267}
268
269/// Strategy to generate a slot number
270pub fn arb_slot() -> impl Strategy<Value = Slot> {
271    arb_natural(1).prop_map(Slot)
272}
273
274/// Strategy to generate a transaction index
275pub fn arb_transaction_index() -> impl Strategy<Value = TransactionIndex> {
276    arb_natural(1).prop_map(TransactionIndex)
277}
278
279/// Strategy to generate a certificate index.
280pub fn arb_certificate_index() -> impl Strategy<Value = CertificateIndex> {
281    arb_natural(1).prop_map(CertificateIndex)
282}
283
284/// Strategy to generate a staking credential
285pub fn arb_staking_credential() -> impl Strategy<Value = StakingCredential> {
286    prop_oneof![
287        arb_credential().prop_map(StakingCredential::Hash),
288        arb_chain_pointer().prop_map(StakingCredential::Pointer)
289    ]
290}
291
292/// Strategy to generate a credential
293pub fn arb_credential() -> impl Strategy<Value = Credential> {
294    prop_oneof![
295        arb_ed25519_pub_key_hash().prop_map(Credential::PubKey),
296        arb_validator_hash().prop_map(Credential::Script)
297    ]
298}
299
300/// Strategy to generate a transaction hash
301pub fn arb_transaction_hash() -> impl Strategy<Value = TransactionHash> {
302    arb_ledger_bytes(32).prop_map(TransactionHash)
303}
304
305/// Strategy to generate a transaction input
306pub fn arb_transaction_input() -> impl Strategy<Value = TransactionInput> {
307    (arb_transaction_hash(), arb_natural(1)).prop_map(|(transaction_id, index)| TransactionInput {
308        transaction_id,
309        index,
310    })
311}
312
313/// Strategy to generate transaction output
314pub fn arb_transaction_output() -> impl Strategy<Value = TransactionOutput> {
315    (arb_address(), arb_value(), option::of(arb_datum_hash())).prop_map(
316        |(address, value, datum_hash)| TransactionOutput {
317            address,
318            value,
319            datum_hash,
320        },
321    )
322}
323
324/// Strategy to generate a TxInInfo
325pub fn arb_tx_in_info() -> impl Strategy<Value = TxInInfo> {
326    (arb_transaction_input(), arb_transaction_output())
327        .prop_map(|(reference, output)| TxInInfo { reference, output })
328}
329
330/// Strategy to generate an AssocMap, given the strategies to generate keys and values
331pub fn arb_assoc_map<K: std::fmt::Debug, V: std::fmt::Debug>(
332    arb_k: impl Strategy<Value = K>,
333    arb_v: impl Strategy<Value = V>,
334) -> impl Strategy<Value = AssocMap<K, V>> {
335    vec((arb_k, arb_v), 10).prop_map(AssocMap)
336}
337
338/// Strategy to generate a PaymentPubKeyHash
339pub fn arb_payment_pub_key_hash() -> impl Strategy<Value = PaymentPubKeyHash> {
340    arb_ed25519_pub_key_hash().prop_map(PaymentPubKeyHash)
341}
342
343/// Strategy to generate a DCert
344pub fn arb_d_cert() -> impl Strategy<Value = DCert> {
345    prop_oneof![
346        arb_staking_credential().prop_map(DCert::DelegRegKey),
347        arb_staking_credential().prop_map(DCert::DelegDeRegKey),
348        (arb_staking_credential(), arb_payment_pub_key_hash())
349            .prop_map(|(sc, pkh)| DCert::DelegDelegate(sc, pkh)),
350        (arb_payment_pub_key_hash(), arb_payment_pub_key_hash())
351            .prop_map(|(p1, p2)| DCert::PoolRegister(p1, p2)),
352        (arb_payment_pub_key_hash(), arb_natural(1)).prop_map(|(pkh, i)| DCert::PoolRetire(pkh, i)),
353        Just(DCert::Genesis),
354        Just(DCert::Mir)
355    ]
356}
357
358/// Strategy to generate a ScriptPurpose
359pub fn arb_script_purpose() -> impl Strategy<Value = ScriptPurpose> {
360    prop_oneof![
361        arb_currency_symbol().prop_map(ScriptPurpose::Minting),
362        arb_transaction_input().prop_map(ScriptPurpose::Spending),
363        arb_staking_credential().prop_map(ScriptPurpose::Rewarding),
364        arb_d_cert().prop_map(ScriptPurpose::Certifying)
365    ]
366}
367
368/// Strategy to generate a TransactionInfo. Note that its inputs, outputs, d_cert,
369/// signatories and datums field will each have a length of 0 to 5
370pub fn arb_transaction_info() -> impl Strategy<Value = TransactionInfo> {
371    (
372        vec(arb_tx_in_info(), 5),
373        vec(arb_transaction_output(), 5),
374        arb_value(),
375        arb_value(),
376        vec(arb_d_cert(), 5),
377        vec((arb_staking_credential(), arb_natural(1)), 5),
378        arb_plutus_interval_posix_time(),
379        vec(arb_payment_pub_key_hash(), 5),
380        vec((arb_datum_hash(), arb_datum()), 5),
381        arb_transaction_hash(),
382    )
383        .prop_map(
384            |(inputs, outputs, fee, mint, d_cert, wdrl, valid_range, signatories, datums, id)| {
385                TransactionInfo {
386                    inputs,
387                    outputs,
388                    fee,
389                    mint,
390                    d_cert,
391                    wdrl,
392                    valid_range,
393                    signatories,
394                    datums,
395                    id,
396                }
397            },
398        )
399}
400
401/// Strategy to generate a ScriptContext
402pub fn arb_script_context() -> impl Strategy<Value = ScriptContext> {
403    (arb_script_purpose(), arb_transaction_info())
404        .prop_map(|(purpose, tx_info)| ScriptContext { purpose, tx_info })
405}
406
407pub fn arb_lovelace() -> impl Strategy<Value = Lovelace> {
408    arb_natural(1).prop_map(Lovelace)
409}
410
411pub fn arb_stake_pub_key_hash() -> impl Strategy<Value = StakePubKeyHash> {
412    arb_ed25519_pub_key_hash().prop_map(StakePubKeyHash)
413}