Skip to content

Commit b57424c

Browse files
authored
Merge pull request #11 from mlabs-haskell/connor/script-context
add v1/v2 `TransactionInfo` and related types
2 parents 7d976f0 + 24824a1 commit b57424c

File tree

14 files changed

+757
-18
lines changed

14 files changed

+757
-18
lines changed

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ serde = { version = "1.0.189", features = ["derive"], optional = true }
1616
true = { version = "0.1.0", optional = true }
1717
data-encoding = { version = "2.4.0", optional = true }
1818
thiserror = "1.0.50"
19+
linked-hash-map = "0.5.6"
1920

2021
[features]
2122
serde = ["dep:serde", "num-bigint/serde"]

src/generators/correct/v1.rs

Lines changed: 82 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@ use crate::plutus_data::PlutusData;
77
use crate::v1::address::{
88
Address, CertificateIndex, ChainPointer, Credential, Slot, StakingCredential, TransactionIndex,
99
};
10-
use crate::v1::crypto::{Ed25519PubKeyHash, LedgerBytes};
10+
use crate::v1::assoc_map::AssocMap;
11+
use crate::v1::crypto::{Ed25519PubKeyHash, LedgerBytes, PaymentPubKeyHash};
1112
use crate::v1::datum::{Datum, DatumHash};
1213
use crate::v1::interval::{Extended, LowerBound, PlutusInterval, UpperBound};
1314
use crate::v1::redeemer::{Redeemer, RedeemerHash};
1415
use crate::v1::script::{MintingPolicyHash, ScriptHash, ValidatorHash};
1516
use crate::v1::transaction::{
16-
POSIXTime, TransactionHash, TransactionInput, TransactionOutput, TxInInfo,
17+
DCert, POSIXTime, ScriptContext, ScriptPurpose, TransactionHash, TransactionInfo,
18+
TransactionInput, TransactionOutput, TxInInfo,
1719
};
1820
use crate::v2::value::{AssetClass, CurrencySymbol, TokenName, Value};
1921
use num_bigint::BigInt;
@@ -243,7 +245,7 @@ pub fn arb_transaction_index() -> impl Strategy<Value = TransactionIndex> {
243245
arb_integer().prop_map(TransactionIndex)
244246
}
245247

246-
/// Strategy to generate a certificate index
248+
/// Strategy to generate a certificate index.
247249
pub fn arb_certificate_index() -> impl Strategy<Value = CertificateIndex> {
248250
arb_integer().prop_map(CertificateIndex)
249251
}
@@ -293,3 +295,80 @@ pub fn arb_tx_in_info() -> impl Strategy<Value = TxInInfo> {
293295
(arb_transaction_input(), arb_transaction_output())
294296
.prop_map(|(reference, output)| TxInInfo { reference, output })
295297
}
298+
299+
/// Strategy to generate an AssocMap, given the strategies to generate keys and values
300+
pub fn arb_assoc_map<K: std::fmt::Debug, V: std::fmt::Debug>(
301+
arb_k: impl Strategy<Value = K>,
302+
arb_v: impl Strategy<Value = V>,
303+
) -> impl Strategy<Value = AssocMap<K, V>> {
304+
vec((arb_k, arb_v), 10).prop_map(AssocMap)
305+
}
306+
307+
/// Strategy to generate a PaymentPubKeyHash
308+
pub fn arb_payment_pub_key_hash() -> impl Strategy<Value = PaymentPubKeyHash> {
309+
arb_ed25519_pub_key_hash().prop_map(PaymentPubKeyHash)
310+
}
311+
312+
/// Strategy to generate a DCert
313+
pub fn arb_d_cert() -> impl Strategy<Value = DCert> {
314+
prop_oneof![
315+
arb_staking_credential().prop_map(DCert::DelegKey),
316+
arb_staking_credential().prop_map(DCert::DelegDeregKey),
317+
(arb_staking_credential(), arb_payment_pub_key_hash())
318+
.prop_map(|(sc, pkh)| DCert::DelegDelegate(sc, pkh)),
319+
(arb_payment_pub_key_hash(), arb_payment_pub_key_hash())
320+
.prop_map(|(p1, p2)| DCert::PoolRegister(p1, p2)),
321+
(arb_payment_pub_key_hash(), arb_integer()).prop_map(|(pkh, i)| DCert::PoolRetire(pkh, i)),
322+
Just(DCert::Genesis),
323+
Just(DCert::Mir)
324+
]
325+
}
326+
327+
/// Strategy to generate a ScriptPurpose
328+
pub fn arb_script_purpose() -> impl Strategy<Value = ScriptPurpose> {
329+
prop_oneof![
330+
arb_currency_symbol().prop_map(ScriptPurpose::Minting),
331+
arb_transaction_input().prop_map(ScriptPurpose::Spending),
332+
arb_staking_credential().prop_map(ScriptPurpose::Rewarding),
333+
arb_d_cert().prop_map(ScriptPurpose::Certifying)
334+
]
335+
}
336+
337+
/// Strategy to generate a TransactionInfo. Note that its inputs, outputs, d_cert,
338+
/// signatories and datums field will each have a length of 0 to 5
339+
pub fn arb_transaction_info() -> impl Strategy<Value = TransactionInfo> {
340+
(
341+
vec(arb_tx_in_info(), 5),
342+
vec(arb_transaction_output(), 5),
343+
arb_value(),
344+
arb_value(),
345+
vec(arb_d_cert(), 5),
346+
vec((arb_staking_credential(), arb_integer()), 5),
347+
arb_plutus_interval_posix_time(),
348+
vec(arb_payment_pub_key_hash(), 5),
349+
vec((arb_datum_hash(), arb_datum()), 5),
350+
arb_transaction_hash(),
351+
)
352+
.prop_map(
353+
|(inputs, outputs, fee, mint, d_cert, wdrl, valid_range, signatories, datums, id)| {
354+
TransactionInfo {
355+
inputs,
356+
outputs,
357+
fee,
358+
mint,
359+
d_cert,
360+
wdrl,
361+
valid_range,
362+
signatories,
363+
datums,
364+
id,
365+
}
366+
},
367+
)
368+
}
369+
370+
/// Strategy to generate a ScriptContext
371+
pub fn arb_script_context() -> impl Strategy<Value = ScriptContext> {
372+
(arb_script_purpose(), arb_transaction_info())
373+
.prop_map(|(purpose, tx_info)| ScriptContext { purpose, tx_info })
374+
}

src/generators/correct/v2.rs

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,18 @@ use crate::generators::correct::v1::{
55
arb_address, arb_datum, arb_datum_hash, arb_script_hash, arb_transaction_input, arb_value,
66
};
77
use crate::v2::datum::OutputDatum;
8-
use crate::v2::transaction::{TransactionOutput, TxInInfo};
8+
use crate::v2::transaction::{ScriptContext, TransactionInfo, TransactionOutput, TxInInfo};
9+
use proptest::collection::vec;
910
use proptest::option;
1011
use proptest::prelude::{prop_oneof, Just};
1112
use proptest::strategy::Strategy;
1213

14+
use super::primitive::arb_integer;
15+
use super::v1::{
16+
arb_assoc_map, arb_d_cert, arb_payment_pub_key_hash, arb_plutus_interval_posix_time,
17+
arb_redeemer, arb_script_purpose, arb_staking_credential, arb_transaction_hash,
18+
};
19+
1320
/// Strategy to generate transaction output
1421
pub fn arb_transaction_output() -> impl Strategy<Value = TransactionOutput> {
1522
(
@@ -42,3 +49,56 @@ pub fn arb_tx_in_info() -> impl Strategy<Value = TxInInfo> {
4249
(arb_transaction_input(), arb_transaction_output())
4350
.prop_map(|(reference, output)| TxInInfo { reference, output })
4451
}
52+
53+
pub fn arb_transaction_info() -> impl Strategy<Value = TransactionInfo> {
54+
(
55+
vec(arb_tx_in_info(), 5),
56+
vec(arb_tx_in_info(), 5),
57+
vec(arb_transaction_output(), 5),
58+
arb_value(),
59+
arb_value(),
60+
vec(arb_d_cert(), 5),
61+
arb_assoc_map(arb_staking_credential(), arb_integer()),
62+
arb_plutus_interval_posix_time(),
63+
vec(arb_payment_pub_key_hash(), 5),
64+
arb_assoc_map(arb_script_purpose(), arb_redeemer()),
65+
arb_assoc_map(arb_datum_hash(), arb_datum()),
66+
arb_transaction_hash(),
67+
)
68+
.prop_map(
69+
|(
70+
inputs,
71+
reference_inputs,
72+
outputs,
73+
fee,
74+
mint,
75+
d_cert,
76+
wdrl,
77+
valid_range,
78+
signatories,
79+
redeemers,
80+
datums,
81+
id,
82+
)| {
83+
TransactionInfo {
84+
inputs,
85+
reference_inputs,
86+
outputs,
87+
fee,
88+
mint,
89+
d_cert,
90+
wdrl,
91+
valid_range,
92+
signatories,
93+
redeemers,
94+
datums,
95+
id,
96+
}
97+
},
98+
)
99+
}
100+
101+
pub fn arb_script_context() -> impl Strategy<Value = ScriptContext> {
102+
(arb_script_purpose(), arb_transaction_info())
103+
.prop_map(|(purpose, tx_info)| ScriptContext { purpose, tx_info })
104+
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ pub mod v1;
77
pub mod v2;
88
#[cfg(feature = "lbf")]
99
pub use lbr_prelude::json;
10+
pub(crate) mod utils;

src/plutus_data.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,3 +568,51 @@ pub fn verify_constr_fields(
568568
Ok(())
569569
}
570570
}
571+
572+
/// Given a vector of PlutusData, parse it as an array whose length is known at
573+
/// compile time.
574+
pub fn parse_fixed_len_constr_fields<'a, const LEN: usize>(
575+
v: &'a [PlutusData],
576+
) -> Result<&'a [PlutusData; LEN], PlutusDataError> {
577+
v.try_into()
578+
.map_err(|_| PlutusDataError::UnexpectedListLength {
579+
got: v.len(),
580+
wanted: LEN,
581+
})
582+
}
583+
584+
/// Given a PlutusData, parse it as PlutusData::Constr and its tag as u32. Return
585+
/// the u32 tag and fields.
586+
pub fn parse_constr<'a>(
587+
data: &'a PlutusData,
588+
) -> Result<(u32, &'a Vec<PlutusData>), PlutusDataError> {
589+
match data {
590+
PlutusData::Constr(tag, fields) => u32::try_from(tag)
591+
.map_err(|err| PlutusDataError::UnexpectedPlutusInvariant {
592+
got: err.to_string(),
593+
wanted: "Constr bigint tag within u32 range".into(),
594+
})
595+
.map(|tag| (tag, fields)),
596+
_ => Err(PlutusDataError::UnexpectedPlutusType {
597+
wanted: PlutusType::Constr,
598+
got: PlutusType::from(data),
599+
}),
600+
}
601+
}
602+
603+
/// Given a PlutusData, parse it as PlutusData::Constr and verify its tag.
604+
pub fn parse_constr_with_tag<'a>(
605+
data: &'a PlutusData,
606+
expected_tag: u32,
607+
) -> Result<&'a Vec<PlutusData>, PlutusDataError> {
608+
let (tag, fields) = parse_constr(data)?;
609+
610+
if tag != expected_tag {
611+
Err(PlutusDataError::UnexpectedPlutusInvariant {
612+
got: tag.to_string(),
613+
wanted: format!("Constr tag to be: {}", expected_tag),
614+
})
615+
} else {
616+
Ok(fields)
617+
}
618+
}

src/utils.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
use std::iter::{empty, once};
2+
3+
/// Create a container C from one element.
4+
pub fn singleton<T, C>(value: T) -> C
5+
where
6+
C: FromIterator<T>,
7+
{
8+
once(value).collect()
9+
}
10+
11+
/// Create an empty container.
12+
pub fn none<T, C>() -> C
13+
where
14+
C: FromIterator<T>,
15+
{
16+
empty::<T>().collect()
17+
}

src/v1/assoc_map.rs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
use std::hash::Hash;
2+
3+
#[cfg(feature = "lbf")]
4+
use lbr_prelude::json::{json_array, Json};
5+
use linked_hash_map::LinkedHashMap;
6+
#[cfg(feature = "serde")]
7+
use serde::{Deserialize, Serialize};
8+
9+
use crate::plutus_data::{IsPlutusData, PlutusData, PlutusDataError, PlutusType};
10+
11+
#[derive(Debug, PartialEq, Eq, Clone)]
12+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
13+
pub struct AssocMap<K, V>(pub Vec<(K, V)>);
14+
15+
impl<K: IsPlutusData, V: IsPlutusData> IsPlutusData for AssocMap<K, V> {
16+
fn to_plutus_data(&self) -> PlutusData {
17+
PlutusData::Map(
18+
(&self.0)
19+
.into_iter()
20+
.map(|(k, v)| (k.to_plutus_data(), v.to_plutus_data()))
21+
.collect(),
22+
)
23+
}
24+
25+
fn from_plutus_data(plutus_data: &PlutusData) -> Result<Self, PlutusDataError> {
26+
match plutus_data {
27+
PlutusData::Map(pairs) => pairs
28+
.into_iter()
29+
.map(|(k, v)| Ok((K::from_plutus_data(k)?, V::from_plutus_data(v)?)))
30+
.collect::<Result<Vec<(K, V)>, PlutusDataError>>()
31+
.map(Self),
32+
_ => Err(PlutusDataError::UnexpectedPlutusType {
33+
got: From::from(plutus_data),
34+
wanted: PlutusType::Map,
35+
}),
36+
}
37+
}
38+
}
39+
40+
impl<K, V> From<Vec<(K, V)>> for AssocMap<K, V> {
41+
fn from(vec: Vec<(K, V)>) -> Self {
42+
AssocMap(vec)
43+
}
44+
}
45+
46+
impl<K, V> From<AssocMap<K, V>> for Vec<(K, V)> {
47+
fn from(m: AssocMap<K, V>) -> Self {
48+
m.0
49+
}
50+
}
51+
52+
impl<K: Hash + Eq, V> From<AssocMap<K, V>> for LinkedHashMap<K, V> {
53+
fn from(m: AssocMap<K, V>) -> Self {
54+
m.0.into_iter().collect()
55+
}
56+
}
57+
58+
impl<K: Hash + Eq, V> From<LinkedHashMap<K, V>> for AssocMap<K, V> {
59+
fn from(value: LinkedHashMap<K, V>) -> Self {
60+
AssocMap(value.into_iter().collect())
61+
}
62+
}
63+
64+
#[cfg(feature = "lbf")]
65+
impl<K: Json, V: Json> Json for AssocMap<K, V> {
66+
fn to_json(&self) -> serde_json::Value {
67+
json_array(
68+
(&self.0)
69+
.into_iter()
70+
.map(|(k, v)| json_array(vec![k.to_json(), v.to_json()]))
71+
.collect(),
72+
)
73+
}
74+
75+
fn from_json(value: &serde_json::Value) -> Result<Self, lbr_prelude::json::Error> {
76+
let vec_of_vectors: Vec<Vec<serde_json::Value>> = Json::from_json(value)?;
77+
let vec_of_pairs = vec_of_vectors
78+
.into_iter()
79+
.map(|vec| {
80+
let [k, v]: [serde_json::Value; 2] =
81+
TryFrom::try_from(vec).map_err(|vec: Vec<_>| {
82+
lbr_prelude::json::Error::UnexpectedArrayLength {
83+
got: vec.len(),
84+
wanted: 2,
85+
parser: "v1::assoc_map::AssocMap".into(),
86+
}
87+
})?;
88+
89+
let k = K::from_json(&k)?;
90+
let v = V::from_json(&v)?;
91+
92+
Ok((k, v))
93+
})
94+
.collect::<Result<Vec<(K, V)>, _>>()?;
95+
96+
Ok(Self(vec_of_pairs))
97+
}
98+
}

src/v1/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//! Plutus types and utilities for Plutus V1
22
pub mod address;
3+
pub mod assoc_map;
34
pub mod crypto;
45
pub mod datum;
56
pub mod interval;

0 commit comments

Comments
 (0)